473,883 Members | 1,665 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

detect bytes written on abort

Here is an advanced PHP question. Can anyone think of a way to detect
the number of bytes written to output when a script is aborted?

I am sending a large file to the client, and I want to record how many
bytes are actually sent. I can detect abort of the script using a
shutdown handler. In the shutdown handler, I tried ob_get_length, but
it returns false. I tried to read the server's log file, but it is does
not contain the information until the script fully quits. I tried to
use fwrite to php://output, and then get the bytes written return value.
However, if the script aborts in the middle of the write, then bytes
written is never returned.

The only thing that worked was writing one byte at a time using
fread/fwrite. But this also made the processor load 100% and download
speed very slow (700KB/sec versus 24MB/sec on localhost using wget).

I know I can poll the web server's log file using a background process.
But if I can do everything from within the script, the system becomes
simpler and more responsive.

Below is the PHP 5.1.1 test script. I suspect it can not be done, but
any advice would be appreciated!

sendfile.php

ignore_user_abo rt(false);
set_time_limit( 60);
register_shutdo wn_function("ha ndleShutdown");

$fp = false;
$fp = fopen("largefil e.html", "rb");
fpassthru($fp); // script is aborted in mid-execution

function handleShutdown( ) {
global $fp;
if($fp !== false) {
fclose($fp);
}
$byteswritten = 0; // how to detect here?
$shutdownmessag e =
"bytes: ".$byteswritten ."\n".
"status: ".connection_st atus()."\n".
"aborted: ".connection_ab orted()."\n";

file_put_conten ts("shutdownlog .txt", $shutdownmessag e);
}

Here is a windows batch file to run the request:

ECHO Hit Ctrl-C to simulate abort
IF EXIST sendfile.php.1 del sendfile.php.1
wget http://localhost/sendfile.php --tries=1
Jan 6 '06 #1
13 2440
>Here is an advanced PHP question. Can anyone think of a way to detect
the number of bytes written to output when a script is aborted?
What are you going to use that information for?
I am sending a large file to the client, and I want to record how many
bytes are actually sent.
Bytes sent != bytes received. Why is there a problem with the
script aborting? Modem drops carrier? Spontaneous Windows reboot?
Browser crash?
I can detect abort of the script using a
shutdown handler. In the shutdown handler, I tried ob_get_length, but
it returns false. I tried to read the server's log file, but it is does
not contain the information until the script fully quits. I tried to
use fwrite to php://output, and then get the bytes written return value.
However, if the script aborts in the middle of the write, then bytes
written is never returned.
If one end of a TCP connection quietly goes away, you might be
able to write 64k more bytes before you realize it has gone away.
The only thing that worked was writing one byte at a time using
fread/fwrite. But this also made the processor load 100% and download
speed very slow (700KB/sec versus 24MB/sec on localhost using wget).


What do you intend to use this for? If you're trying to figure out
where to restart sending the file, it won't be accurate.

Gordon L. Burditt
Jan 6 '06 #2
Gordon Burditt wrote:
Here is an advanced PHP question. Can anyone think of a way to detect
the number of bytes written to output when a script is aborted?


What are you going to use that information for?
I am sending a large file to the client, and I want to record how many
bytes are actually sent.


Bytes sent != bytes received. Why is there a problem with the
script aborting? Modem drops carrier? Spontaneous Windows reboot?
Browser crash?
I can detect abort of the script using a
shutdown handler. In the shutdown handler, I tried ob_get_length, but
it returns false. I tried to read the server's log file, but it is does
not contain the information until the script fully quits. I tried to
use fwrite to php://output, and then get the bytes written return value.
However, if the script aborts in the middle of the write, then bytes
written is never returned.


If one end of a TCP connection quietly goes away, you might be
able to write 64k more bytes before you realize it has gone away.
The only thing that worked was writing one byte at a time using
fread/fwrite. But this also made the processor load 100% and download
speed very slow (700KB/sec versus 24MB/sec on localhost using wget).


What do you intend to use this for? If you're trying to figure out
where to restart sending the file, it won't be accurate.

Gordon L. Burditt


TCP is a reliable transport, meaning that at the application layer, one
always know exactly how much data the client received, and this is
always equal to how much was successfully sent. I don't care how many
bytes were transferred by TCP in the data link layer.

I don't want to restart sending the file. I also do not care why the
script aborted. Of course, it must be from a network/client abort, not
a server reboot or such, because the script must finish executing. I
only want to be able to track how many bytes were sent to the client,
which equals the value that is eventually written to the server log file.

The reason I need it is because in this system, I want to be able to
show the user how many bytes the server sent them. This will tell them
how much data transfer they have used.

I need the status of bytes sent as soon as possible after the script
completes or aborts. Thanks.
Jan 6 '06 #3
Shailesh Humbad wrote:
Here is an advanced PHP question. Can anyone think of a way to detect
the number of bytes written to output when a script is aborted?


If you are using Apache and can recompile the SAPI module, you can add
the following function to php_functions.c :

/* {{{ proto integer apache_get_byte s_sent(void)
Get the number of bytes actually sent */
PHP_FUNCTION(ap ache_get_bytes_ sent)
{
php_struct *ctx;
ctx = SG(server_conte xt);
RETURN_LONG(ctx->r->bytes_sent);
}
/* }}} */
If TCP/IP guarenteed delivery is to be believed, then that should be
the exact number of bytes received (but not necessarily saved) by the
client.

Jan 6 '06 #4
>TCP is a reliable transport, meaning that at the application layer, one
always know exactly how much data the client received, and this is
always equal to how much was successfully sent.
The above is *NOT* a conventional definition of "reliable transport".
And it's not what TCP tries to implement.

Stdio buffering put on a "reliable transport" as you define it above
makes it unreliable, as a successful fwrite() on a socket may simply
mean that the data has been placed in a buffer on the sender, not
even passed to the OS yet. You also don't know how much data is
buffered by Apache or web proxies. You don't know that the other
end of the TCP connection is on the user's browser.

In a scenario where the communication channel is going to be cut
at some point in time (corresponding to, say, a modem dropping
carrier or network connectivity otherwise going down and staying
down), and no further message traffic is possible, it is impossible
to implement a protocol where the sender and receiver always agree
exactly on the number of bytes received. If you send a packet and
get no answer, you don't know whether the sent packet got lost or
the acknowledgement got lost. You can get the uncertainty down to
one byte by sending single-byte packets all the time. Slow. Wasteful
of bandwidth. Even the Theory of Relativity is relevant here. The
Speed of Data, as well as the Speed of Light, is finite and does
not permit instantaneous communication of information.
I don't care how many
bytes were transferred by TCP in the data link layer. I don't want to restart sending the file. I also do not care why the
script aborted.
You DO care if the *client* aborted. Just because the browser got the
data from TCP doesn't mean it was safely saved to disk before someone
tripped over the power cord.
Of course, it must be from a network/client abort, not
a server reboot or such, because the script must finish executing. I
only want to be able to track how many bytes were sent to the client,
which equals the value that is eventually written to the server log file.

The reason I need it is because in this system, I want to be able to
show the user how many bytes the server sent them. This will tell them
how much data transfer they have used.
Why would the user care? Unless you're billing them against a quota
or something, which is quite a different problem from being able
to restart a file transfer.
I need the status of bytes sent as soon as possible after the script
completes or aborts. Thanks.


It won't happen reliably. You might get something accurate enough
for *quotas*, but not for restarting file transfers. The way things
like FTP do this is get the size of the partially-transferred file
on the client side and start from there.

Gordon L. Burditt
Jan 6 '06 #5
Chung Leong wrote:
Shailesh Humbad wrote:
Here is an advanced PHP question. Can anyone think of a way to detect
the number of bytes written to output when a script is aborted?


If you are using Apache and can recompile the SAPI module, you can add
the following function to php_functions.c :

/* {{{ proto integer apache_get_byte s_sent(void)
Get the number of bytes actually sent */
PHP_FUNCTION(ap ache_get_bytes_ sent)
{
php_struct *ctx;
ctx = SG(server_conte xt);
RETURN_LONG(ctx->r->bytes_sent);
}
/* }}} */
If TCP/IP guarenteed delivery is to be believed, then that should be
the exact number of bytes received (but not necessarily saved) by the
client.


Here is my hosting provider's configuration:
apache_get_vers ion: Apache/1.3.33 (Debian GNU/Linux)
mod_throttle/3.1.2 mod_ssl/2.8.22 OpenSSL/0.9.7d
php_sapi_name: apache

I am unfamiliar with PHP internals and how they interact with the web
server. Will that only work on Apache 2 and with sapi as
"apache2fil ter" or "apache2handler "? I don't think I can recompile the
SAPI module, but I could build my own module and have my hosting
provider install it. Is it possible to add such a function in a custom
module? If it is too hard, then I will just go the low-tech route
(polling the log file).

Thanks.
Jan 6 '06 #6
Gordon Burditt wrote:
TCP is a reliable transport, meaning that at the application layer, one
always know exactly how much data the client received, and this is
always equal to how much was successfully sent.


The above is *NOT* a conventional definition of "reliable transport".
And it's not what TCP tries to implement.

Stdio buffering put on a "reliable transport" as you define it above
makes it unreliable, as a successful fwrite() on a socket may simply
mean that the data has been placed in a buffer on the sender, not
even passed to the OS yet. You also don't know how much data is
buffered by Apache or web proxies. You don't know that the other
end of the TCP connection is on the user's browser.

In a scenario where the communication channel is going to be cut
at some point in time (corresponding to, say, a modem dropping
carrier or network connectivity otherwise going down and staying
down), and no further message traffic is possible, it is impossible
to implement a protocol where the sender and receiver always agree
exactly on the number of bytes received. If you send a packet and
get no answer, you don't know whether the sent packet got lost or
the acknowledgement got lost. You can get the uncertainty down to
one byte by sending single-byte packets all the time. Slow. Wasteful
of bandwidth. Even the Theory of Relativity is relevant here. The
Speed of Data, as well as the Speed of Light, is finite and does
not permit instantaneous communication of information.
I don't care how many
bytes were transferred by TCP in the data link layer.

I don't want to restart sending the file. I also do not care why the
script aborted.


You DO care if the *client* aborted. Just because the browser got the
data from TCP doesn't mean it was safely saved to disk before someone
tripped over the power cord.
Of course, it must be from a network/client abort, not
a server reboot or such, because the script must finish executing. I
only want to be able to track how many bytes were sent to the client,
which equals the value that is eventually written to the server log file.

The reason I need it is because in this system, I want to be able to
show the user how many bytes the server sent them. This will tell them
how much data transfer they have used.


Why would the user care? Unless you're billing them against a quota
or something, which is quite a different problem from being able
to restart a file transfer.
I need the status of bytes sent as soon as possible after the script
completes or aborts. Thanks.


It won't happen reliably. You might get something accurate enough
for *quotas*, but not for restarting file transfers. The way things
like FTP do this is get the size of the partially-transferred file
on the client side and start from there.

Gordon L. Burditt


I don't care how much data the client actually saved, only how much was
transferred. Yes, my eventual aim is to bill against a quota. To solve
the file restart problem, I can implement HTTP range handling later.

"Reliable Delivery - Once a connection has been established, TCP
guarantees that data is delivered in exactly the same order it was sent,
with no loss, and no duplication. If a failure prevents reliable
delivery, the sender is informed.", Internetworking with TCP/IP Vol.
III, p. 103
Jan 6 '06 #7
>>> TCP is a reliable transport, meaning that at the application layer, one
always know exactly how much data the client received, and this is
always equal to how much was successfully sent.
The above is *NOT* a conventional definition of "reliable transport".
And it's not what TCP tries to implement.

Stdio buffering put on a "reliable transport" as you define it above
makes it unreliable, as a successful fwrite() on a socket may simply
mean that the data has been placed in a buffer on the sender, not
even passed to the OS yet. You also don't know how much data is
buffered by Apache or web proxies. You don't know that the other
end of the TCP connection is on the user's browser.

In a scenario where the communication channel is going to be cut
at some point in time (corresponding to, say, a modem dropping
carrier or network connectivity otherwise going down and staying
down), and no further message traffic is possible, it is impossible
to implement a protocol where the sender and receiver always agree
exactly on the number of bytes received. If you send a packet and
get no answer, you don't know whether the sent packet got lost or
the acknowledgement got lost. You can get the uncertainty down to
one byte by sending single-byte packets all the time. Slow. Wasteful
of bandwidth. Even the Theory of Relativity is relevant here. The
Speed of Data, as well as the Speed of Light, is finite and does
not permit instantaneous communication of information.
I don't care how many
bytes were transferred by TCP in the data link layer.

I don't want to restart sending the file. I also do not care why the
script aborted.


You DO care if the *client* aborted. Just because the browser got the
data from TCP doesn't mean it was safely saved to disk before someone
tripped over the power cord.
Of course, it must be from a network/client abort, not
a server reboot or such, because the script must finish executing. I
only want to be able to track how many bytes were sent to the client,
which equals the value that is eventually written to the server log file.

The reason I need it is because in this system, I want to be able to
show the user how many bytes the server sent them. This will tell them
how much data transfer they have used.


Why would the user care? Unless you're billing them against a quota
or something, which is quite a different problem from being able
to restart a file transfer.
I need the status of bytes sent as soon as possible after the script
completes or aborts. Thanks.


It won't happen reliably. You might get something accurate enough
for *quotas*, but not for restarting file transfers. The way things
like FTP do this is get the size of the partially-transferred file
on the client side and start from there.

Gordon L. Burditt


I don't care how much data the client actually saved, only how much was
transferred. Yes, my eventual aim is to bill against a quota.


Why do you care about getting these numbers exact? You don't
seem to care about what is transmitted at the data link layer,
which is probably how your provider will bill YOU if your
agreement with them involves traffic-sensitive costs.
"Reliable Delivery - Once a connection has been established, TCP
guarantees that data is delivered in exactly the same order it was sent,
with no loss, and no duplication. If a failure prevents reliable
delivery, the sender is informed.", Internetworking with TCP/IP Vol.
III, p. 103


This says nothing about knowing HOW MUCH was delivered in the case
of a failure. If the session fails, you know not all of it got
delivered. You also know that they didn't get any more than you
sent. When a write() on a socket returns, you don't know that ANY
of it got delivered (yet). A failure may be reported later. Much
later. The above quote does not say "If a failure prevents reliable
delivery, the sender is informed instantaneously with an itemized
report of how much was delivered".

Gordon L. Burditt
Jan 6 '06 #8
Following on from Shailesh Humbad's message. . .

8><

I don't care how much data the client actually saved, only how much was
transferred. Yes, my eventual aim is to bill against a quota. To solve
the file restart problem, I can implement HTTP range handling later.

"Reliable Delivery - Once a connection has been established, TCP
guarantees that data is delivered in exactly the same order it was sent,
with no loss, and no duplication. If a failure prevents reliable
delivery, the sender is informed.", Internetworking with TCP/IP Vol.
III, p. 103


Gordon, being the sort of person who spots the cracks down which bad
things happen that most people miss, is suggesting that what you sent
will not be the same as what was received unless completed and there
will be buffers of various sorts between your code and your cable to the
Internet and then some. In this he is trying to be helpful because the
effort required to engineer a byte-accurate solution is going to be a
great deal more than to obtain an estimate.

Here is a 'free' solution: If a file transfer fails then charge for
half the full size. Either there are very few failures in practice in
which case the issue is not very important in the scheme of things; or
it happens all the time, in which case this will work out fine
statistically. [But you will lose all your customers for reasons of crap
service.]
--
PETER FOX Not the same since the bolt company screwed up
pe******@eminen t.demon.co.uk.n ot.this.bit.no. html
2 Tees Close, Witham, Essex.
Gravity beer in Essex <http://www.eminent.dem on.co.uk>
Jan 6 '06 #9
Gordon Burditt wrote:
TCP is a reliable transport, meaning that at the application layer, one
always know exactly how much data the client received, and this is
always equal to how much was successfully sent.
The above is *NOT* a conventional definition of "reliable transport".
And it's not what TCP tries to implement.

Stdio buffering put on a "reliable transport" as you define it above
makes it unreliable, as a successful fwrite() on a socket may simply
mean that the data has been placed in a buffer on the sender, not
even passed to the OS yet. You also don't know how much data is
buffered by Apache or web proxies. You don't know that the other
end of the TCP connection is on the user's browser.

In a scenario where the communication channel is going to be cut
at some point in time (corresponding to, say, a modem dropping
carrier or network connectivity otherwise going down and staying
down), and no further message traffic is possible, it is impossible
to implement a protocol where the sender and receiver always agree
exactly on the number of bytes received. If you send a packet and
get no answer, you don't know whether the sent packet got lost or
the acknowledgement got lost. You can get the uncertainty down to
one byte by sending single-byte packets all the time. Slow. Wasteful
of bandwidth. Even the Theory of Relativity is relevant here. The
Speed of Data, as well as the Speed of Light, is finite and does
not permit instantaneous communication of information.

I don't care how many
bytes were transferred by TCP in the data link layer.
I don't want to restart sending the file. I also do not care why the
script aborted.
You DO care if the *client* aborted. Just because the browser got the
data from TCP doesn't mean it was safely saved to disk before someone
tripped over the power cord.

Of course, it must be from a network/client abort, not
a server reboot or such, because the script must finish executing. I
only want to be able to track how many bytes were sent to the client,
which equals the value that is eventually written to the server log file.

The reason I need it is because in this system, I want to be able to
show the user how many bytes the server sent them. This will tell them
how much data transfer they have used.
Why would the user care? Unless you're billing them against a quota
or something, which is quite a different problem from being able
to restart a file transfer.

I need the status of bytes sent as soon as possible after the script
completes or aborts. Thanks.
It won't happen reliably. You might get something accurate enough
for *quotas*, but not for restarting file transfers. The way things
like FTP do this is get the size of the partially-transferred file
on the client side and start from there.

Gordon L. Burditt

I don't care how much data the client actually saved, only how much was
transferred. Yes, my eventual aim is to bill against a quota.


Why do you care about getting these numbers exact? You don't
seem to care about what is transmitted at the data link layer,
which is probably how your provider will bill YOU if your
agreement with them involves traffic-sensitive costs.
"Reliable Delivery - Once a connection has been established, TCP
guarantees that data is delivered in exactly the same order it was sent,
with no loss, and no duplication. If a failure prevents reliable
delivery, the sender is informed.", Internetworking with TCP/IP Vol.
III, p. 103


This says nothing about knowing HOW MUCH was delivered in the case
of a failure. If the session fails, you know not all of it got
delivered. You also know that they didn't get any more than you
sent. When a write() on a socket returns, you don't know that ANY
of it got delivered (yet). A failure may be reported later. Much
later. The above quote does not say "If a failure prevents reliable
delivery, the sender is informed instantaneously with an itemized
report of how much was delivered".

Gordon L. Burditt


You have good points, but I just don't need that much resolution or
accuracy. The socket will time out in 30 seconds if there is a problem
sending data. The bytes returned by the write call, even if known 30
seconds later, is all I need, and I know somewhere internally in PHP it
is being recorded.

When socket write returns (if being called in blocking-mode), it returns
the number of bytes written successfully to the socket. This is the
number of bytes guaranteed to be delivered to the client's receiving
socket (though the client may not have written it all to disk or other
issues may have occurred). The reason why TCP can know the number of
bytes sent with certainty is because every sent packet is replied to
with an acknowledgment (ACK) packet.

For my purposes, this number is going to be a decent approximation of
actual bandwidth used, and I realize it's not going to be exact. Thanks.

Jan 6 '06 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

15
3604
by: Manish Jethani | last post by:
Hi all, Is there a way to detect typos in a Python program, before actually having to run it. Let's say I have a function like this: def server_closed_connection(): session.abost() Here, abort() is actually misspelt. The only time my program follows this path is when the server disconnects from its
1
2395
by: Daniel | last post by:
when writing out a file from .net, when is the file created? after the bytes are all written to the hard drive or before the bytes are written to the hard drive?
4
2314
by: THY | last post by:
Hi, After we let the user upload file to the server, how do we actually detect if that is a valid jpeg file ? thanks, Tee
1
5426
by: Roy | last post by:
Hi, I have a problem that I have been working with for a while. I need to be able from server side (asp.net) to detect that the file i'm streaming down to the client is saved completely/succsessfully on the client's computer before updating some metadata on the server (file downloaded date for instance) However, All examples i have tried, and all examples I have found that other people says works - doesn't work for me :-(
4
2348
by: Stefan Gentzmer | last post by:
Hello, how can i detect, if the text for a XmlElement has to be written as CDATA (i am using the XmlWriter) instead of a normal string. For example <start> <node1>HelloWorld</node1> <node2>a>b</node2>
5
2185
by: TulasiKumar | last post by:
Hi all, I am new in Netwrok porgraming in cSharp.how can i detect Tcp/Ip packet in c#.Net.Any body knows please tell me, what are the interfaces or classes are supported regrading on TCP/IP packets.Please tell me any website refrences or any Source code examples.This is very urgent requirement. Thanks in advance, Regards, Tulasi Kumar.
5
3668
by: philip | last post by:
Here is some lines of code than I wrote. You can copy/paste theis code as code of form1 in a new project. My problem is this one : I try to write in a file a serie of bytes. BUT some bytes written in file are not the sent bytes. Copy and paste the following lines to observe my problem. What can I do to resolve problem ? Only System.Text.Encoding.ASCII write the same number of bytes, but not the good bytes. Someone can help me. Thanks by...
2
2511
by: macracan | last post by:
It has been discussed before, but I still can't find a solution and I have a need. So here goes: The problem: I'm writing a something to handle messages from XWindows. The idea is to have virtual handlers for different kind of messages. But XWindows has a feature thereby you have to subscribe to messages you're interested in. I was thinking of providing empty handlers by default, and overwrite them in derived classes. And I would need
1
1321
by: viza | last post by:
Hi all Suppose I have: static sig_atomic_t timed_out= 0; void time_out( int unrequired ){ timed_out= 1; }
0
9932
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
11112
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10730
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
10405
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9559
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7959
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5980
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
4205
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
3228
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.