TWSocket
Last Updated: 01/27/2007

Frequently Asked Questions for TWSocket:

     
  1. What is it? 
  2. Blocking operation 
  3. Broadcast vs. Multicast 
  4. Calling ws2_32.dll direcly 
  5. Instead of just getting a client to connect to a predetermined server IP, how would I go about clients broadcasting for a server IP? 
  6. Closing 
  7. COM object 
  8. Compile with BCB4 
  9. Connecting to a server without knowing his address 
  10. Coping with Client disconnect and TWSocketServer? 
  11. Datagram max or ideal size 
  12. Disabling nagle algoritm 
  13. DNS reverse lookup 
  14. Dual processor 
  15. Dup 
  16. Dynamic ip address assigned by ISP 
  17. How to use the GetRcvdCount properly? 
  18. Increasing packet size 
  19. IP v6 
  20. LineEdit and binary data 
  21. Listen on any port 
  22. LocalIPList 
  23. Multicast 
  24. Multiple clients on same port 
  25. Multithreaded 
  26. My server send two strings, I only receive one OnDataAvailable with both strings appended. 
  27. No forms 
  28. OnDataAvailable gets called multiple times 
  29. OnError 
  30. Why doesn't the OnDataAvailable event trigger in an ASP component (DLL) 
  31. RcvdCount and received bytes 
  32. Reading all data in OnDataAvailable 
  33. Receive a string 
  34. Receive count give very large numbers 
  35. Receiving high speed data 
  36. Re-establishing a connection 
  37. Reverse DNS's results vary 
  38. Server and client at the same time 
  39. SocketState 
  40. Stop receiving connections and start again later 
  41. Thread and ProcessMessage 
  42. Time_wait sockets 
  43. Udp broadcast problem 
  44. UDP Packet Fragmentation 
  45. How to avoid data loss with UDP packets larger than 1460 bytes 
  46. Why does DataAvaliable triggers only when socket is closed? 
  47. Winsock error codes 
  48. Winsock socket options 

[Back to Main]













  
What is it?
Francois Piette  francois.piette@overbyte.be    
20.09.1998

This class is designed to offer the base functionality of a socket.It has many features to move data back and forth using both binaryand ASCII data types. It provides excellent error checking, throwingexceptions for a variety of socket states. There are no specificprotocol definitions in this class and is intended for use as alow-level socket driver.

 
[Return to Top]

  
Blocking operation
Francois PIETTE francois.piette@overbyte.be    
04/06/2001

> I have a very lengthy operation to do when I receive data
> What is best


Use multi-threading to execute your blocking calls within a thread.

To use it, you have to created a thread to execute your blocking or lengthy operation. Many possibilities exists. The easiest is to created a thread when needed. The most powerfull is to use a pool of worker thread.

I talk a little bit about the easiest: From your client OnDataAvailable, create a new thread to execute the request and pass to the thread a reference to the client socket. Once the thread has finished his work, post a custom message to your main form passing the reference to the client socket as LParam and in WParam a reference to dynamically allocated data to be sent to remote client. Write a handler for your custom message. This handler will send the data to the clisent using the client socket reference and then free memory used for data.

 
[Return to Top]

  
Broadcast vs. Multicast
Michael thomasius.m@wige-mic.de    
21/07/2001

 Because I tried a long time to get Multicast/Broadcast to work, here some hints/experiences:

 At first: You will have the same problem with Multicast like with Broadcast: You an lose packets and they can be disordered, so you have no guarantee for receiving all packets the right way.

 BUT: In LANs the lost packets are going to zero and normally you don't have problems with disordering because the packets should use all the same route to a client. So the only difference between Microsoft is the traffic at the network.

 While broadcast sends all Packets to all Network-Participants, Multicast doesn't do that. Here you get only a connection between the server and the real listening clients.

 I don't want to go more to detail, only that:

 You send UDP to a Subnet. So every Client gets all packets and at first the network layer (that means your network adapter) will decide if he has to read the packet (because he knows his IP). If so he gives the packet to the higher layers and they have to decide if they are listening to the ports (thats a little bit to easy described, but just for the principals ok). 

 In Multicast you connect to a Multicast-Group. That means you connect to a special IP, that starts with  224.0.0.0 and 239.255.255.255 (hope I remembered the upper limit right). So the next switch/router in your network will connect to the next switch/router with the IP until someone find the group. So it should be guaranteed that the packets go only to the listening clients and they should use one of the shortest ways.

 But again: Problems are the same like broadcast, so Multicast is only in advantage if you provie your data over WAN or a LAN with other traffics. So a thankyou for the reading until this point a hint for which I was looking about 3 months. Francois Piette is right with his description about the UDP-Sending Problems. Like I know you wouldn't have that with Multicasts. But in UDP they are also solveable: The Winsock, which is the communication component for the TWSocket too, has a sending and receiving buffer of 8192 Bytes. So if you send faster than the Socket can push data on the network you lost packets (you can see that, if on every client same packets are missing), if a client is not able to receive as fast as data come in, so he will not receive all packages (that means also if you are not able to process data fast enough in TWSocket).

 So for me the key to the solution was the buffer size. After reading all the documentations of Winsock2 I found the necessary options and here they are like you set them for a TWSocket (tadaaa): 
 for ReceiveBuffer: 
  test := 1000000;
  Setsockopt(BroadCastReceive.HSocket, SOL_SOCKET , SO_RCVBUF, @test, SizeOf (test));
 and for SendBuffer:
  test := 1000000;
  Setsockopt(BroadSocket.HSocket, SOL_SOCKET , SO_SNDBUF, @test, SizeOf (test));

 With the function getsockopt you can read the buffer sizes. You must set them AFTER opening the socket, so at first call "Listen" or "Connect", than Set socket options. You have some parameters more like Send-TimeOut and Receive-Timeout, but they don't have influences in my environment. Since I use the big buffers of about 1 million bytes I never had lost a packet in my local network with 100 clients and 3 to 5 switches between them, so I decided thats the solution for me, maybe for you too ? At the end the big sorries: First for the very large mail, but maybe it was interesting. Second for maybe encoded Mail, I switched to Windows/Westeurope-Code like somebody said in history, but maybe thats not the solution...

 
[Return to Top]

  
Calling ws2_32.dll direcly
Wilfried Mestdagh    
22/04/2001

> I have windows 95 with winsock 2 update. now i have a winsock32.dll and
> a ws2_32.dll. i tried if TWsocket can load ws2_32.dll directly and that
> worked. Doesn't the winsock32.dll just call functions in ws2_32.dll?
> that would be slower.

Yes winsock32.dll call functions in ws2_32.dll. You can load ws2_32 directly in WSocket but you will not be compatible with other versions of winsock, so it is descouraged. About the gain performance, theoreticly yes, but in practice it will be not measurable.

 
[Return to Top]

  
Instead of just getting a client to connect to a predetermined server IP, how would I go about clients broadcasting for a server IP?
Steve Williams stevewilliams@kromestudios.com    
19/12/2001

The server must be setup to listen on an agreed port for a specific UDP packet. This UDP packet is interpreted by the server as a request for information.  The server then responds with information such as IP address, port, and any other information that may be required by the client.

The client sets up a listening UDP socket on any port.  It then uses the SendTo method of the TWSocket to send a specific packet to
255.255.255.255
and the port that the server is known to be listening on.  The server receives the packet, sees that it is a request for server info and responds with a packet containing its address and other info to the address and port that the UDP packet originated from (available in the sin_addr when receiving the packet).  Then the client can use that information to make a connection to the server normally.  The client may also receive a number of responses and it can then choose the best server to connect to.  For example, the server's response may contain a value that represents how busy the server is.  The client might then choose the server that has the lowest load.

Note that you have to implement all of this.  There is no built-in or automatic way that this can be done.

 
[Return to Top]

  
Closing
Wilfried Mestdagh (revised by Steve Williams)    
07/05/2001

> Closing a socket, who, how, when

There are several ways to close (and eventually destroy) a TWSocket. Here is the list: CloseDelayed, Close, ShutDown, Abort, Release, Free, Destroy.  The last three are most useful for dynamically-created TWSOCKET objects.  Here is a brief explaination of them.


- CloseDelayed

Is in most cases the preferred way. It will post a message to itself to close the TWSocket. This means that the socket closure is done outside the code that calls the CloseDelayed, meaning a while later. The message handler will call Close.


- Close

Attempt to gracefully close the socket.  If there is still some data waiting in the buffers it will try to send it.  Do not use Close from within any TWSocket events, instead use CloseDelayed.


- ShutDown(1)

Preferred way of closing if you have just sent some data and want to close the socket.  It will try to send the data before closing.


- Abort

This is the quick and dirty way of closing.  If you have some data in any of the buffers it will probably be lost.


- Release

Similar to CloseDelayed, this method will allow you to destroy a dynamically-created TWSocket from within one of its own events. If the socket is still open it will be closed. The call to Destroy is done with a message handler, so it will be done outside of the event handler.


- Free

For dynamically-created TWSocket. Checks for nil pointer and if it's not it will call Destroy.


- Destroy

For dynamically-created TWSocket. If the socket is not closed it will call Close first.

As you can see by the way these methods work, you have to make a protocol that ensures sent data is received.  The simplest way is that the receiver closes the socket after it has received all of the data.  Of course the receiver must have some way of knowing when it has received all the data that it is expecting.  One approach is to use a simple character or command.  The sender can close the socket after the receiver has acknowledged that it has received all of the data.  This can be done quite easily if you write a command interpreter and use LineMode (with #13#10 as LineEnd because it makes testing easy with a telnet program). Through simple commands both ends can exchange information.  When someone decides to send binary data it tells the receiver.  First of all the receiver sets LineMode to False and then informs the sender that it is ok to start sending the data.  When the receiver has received all of the data, it can save and/or manipulate it, set LineMode back to True and then inform the sender it is ok to start exchanging commands again.

 
[Return to Top]

  
COM object
Francois PIETTE francois.piette@overbyte.be    
11/11/2004

  I've written a COM Object and used ICS(WSocket) to drive tcp communications. All it does is simply:

Connect
SendStr
Disconnect

However, if i use CreateOLEObject() in Delphi or CreateObject() in vb,then try to Connect(withouth sending anything or disconnecting), ICS immediately disconnects right after.


You just forgot that the component is working asynchronously without blocking. So "connect" take almost no time while connection take place in the background, generating OnSessionConnected event when the connection is established. Same behaviour for "Send". It doesn't take significant time. You get control back immediately while data is sent in the bacground for your. OnDataSent is triggered when data has been delivered to winsock (doesn't mean it has been delivered to the remote site). And finally Disconnect (which actually is "Close" or "Shutdown") act in the background, generating OnSessionClosed when done.

For all that to work properly, you need to have a message pump in your COM object.

 
[Return to Top]

  
Compile with BCB4
James Legg jns@saqnet.co.uk    
14.04.1999

To make ICS compile with BCB4, add the following changes:

snippet 1 from wsocket.pas:

{$ELSE}
{$IFDEF VER120}
    { Delphi 4 has changed var parameters to pointers }
    FASocket := WSocket_accept(FHSocket, @sin, @len);
{$ELSE}
{$IFDEF VER125}
    { C++Builder 4 has changed var parameters to }
    { pointers (Added by James Legg)             }
    FASocket := WSocket_accept(FHSocket, @sin, @len);
{$ELSE}
    FASocket := WSocket_accept(FHSocket, TSockAddr(sin), len);
{$ENDIF}
{$ENDIF}

snippet 2 from tnemulvt

procedure TTnEmulVT.Connect;
var
{$IFDEF VER100}   { Delphi 3 }
    Form : TCustomForm;
{$ELSE}
{$IFDEF VER120}   { Delphi 4 }
    Form : TCustomForm;
{$ELSE}
{$IFDEF VER125}   { BCB4 Added by James Legg}
    Form : TCustomForm;
{$ELSE}
{$IFDEF VER110}   { BCB3 }
    Form : TCustomForm;
{$ELSE}
    Form : TForm;


 
[Return to Top]

  
Connecting to a server without knowing his address
Steve Williams stevewilliams@kromestudios.com    
26/10/2001

> I would like to connect to a server in a local network, but don't know the
> ip or hostname, but I have a Server running on  port 9000 for example. Is
> it possible to connect to the server? If possible how?


 You need three things: address, port number and protocol.  You have port number and protocol, but you do not know the address.  The solution to this is for the server to advertise itself.  Your client sends out a broadcast UDP packet to a known port.  The server listens for these broadcast packets and then sends a response UDP packet to the address and port the broadcast packet came from.  This response packet contains the server's IP address and port that the server has listening for client connections.  Now that your client has the address, port and protocol, you can make a connection to the server.

 
[Return to Top]

  
Coping with Client disconnect and TWSocketServer?
Francois PIETTE francois.piette@overbyte.be    
10/04/2002

> Here is a general problem that arises when using TWSocketServer.  It has a
> collection of client sockets (TWSocketClient
).  Very handy.  But any time
> you access one of the clients, thereby receiving a reference to the client,
> you enter a situation where if the client disconnects and is subsequently
> destroyed, any other part of the program holding a reference to the client
> is going crash.
>
> How are those of you out there dealing with this?
>
> If the collection of clients were ref-counted interfaces this wouldn't be a
> problem, but since they are class objects its a real issue.  The other idea
> I've thought of is putting a destruction notification in the my
> TWSocketClient
descendant class in conjunction with the use of a proxy
> object to the client socket.
>
> What are you all doing?


You can use TWSocketServer.IsClient to check if you still have a reference to a valid client. And you can use OnClientDisconnect to kill the reference you have and which will be soon no longer valid.

> The other idea I've thought of is putting a destruction notification

This is also probably a good idea if the above two methods doesn't work for you.

 
[Return to Top]

  
Datagram max or ideal size
Francois Piette francois.piette@overbyte.be    
22/12/2002

> Does anybody know the maximum size for a UDP packet? Currently I send 1024-byte packets over our network (containing an 12-byte header and the rest is video frame data), but I'm wondering if this is a normal size. Is it more normal/efficient to send larger sized packets?

What I also want to know is how the data to be sent is divided into packets. Does each send()-call generate a new UDP packet or is the data put into a buffer and separated into packets by the underlying network protocol? Has this got something to do with the BufLen-property of TWSocket?


It is implementation dependant. Use WinsockInfo function (located in wsocket.pas) to retrieve WSADATA record. Member iMaxUdpDg gives the max size of a datagram.

 
[Return to Top]

  
Disabling nagle algoritm
J. Merrill jvm_cop+ics@spamcop.net    
21/04/2003

Actually it turns out that I didn't describe what causes the delay correctly.  The magic number is 200 mSec, but the delay is caused because the sender won't send a second packet until the first one is acknowledged, and the server doesn't acknowledge quickly (hoping to send acknowledgement of multiple packets in one call).  Setting the wsoTCPNoDelay option prevents the delays, but from the text below, that needs to be done by both the client and the server to make things work optimally.


 
[Return to Top]

  
DNS reverse lookup
Francois PIETTE francois.piette@overbyte.be    
04/06/2001

> The reverse lookup mode does not work with Windows 2000, 
> it appears that if you do a reverse lookup of the local IP address 
> Windows 2000 responds with the current machine name rather than
> querying the DNS server.


If you want to query the DNS, use TDnsQuery component instead of TWSocket. TDnsQuery send a query to the DNS server while TWSocket send the request to winsock which "optimize" the query if it is the local address.

 
[Return to Top]

  
Dual processor
Primoz Gabrijelcic   fab@siol.net    
25/02/2001

> dual processors gives problems


> Try to boot your machine in single processor mode, then you know if it is
> the reason. You have to specify in the 'boot.ini' (root directory, hidden)
> /NUMPROC=1

Although this might work, documented switch is /ONECPU.

 
[Return to Top]

  
Dup
Francois PIETTE francois.piette@overbyte.be    
22/09/2001

> Does Dup duplicate a socket

Dup method simply takes a socket handle (originating from winsock, most of the time obtained with Accept method) and associate it with the TWSocket object instance calling Dup method.

 TWSOcket is an object which handle winsock communication. It is a "winsock wrapper", putting an object interface around winsock. Actually, all communication stuff is done by winsock. TWSocket just say to winsock "hey do this and to that !". Winsock only knows socket handles and windows handles. Dup associates a socket handle to a given TWSocket object instance which in turn associate a windows handle with the socket handle.

 
[Return to Top]

  
Dynamic ip address assigned by ISP
Jan Tomasek    
26.09.1998

This function will return IP address assigned to your modem by ISP. If you have more than modem you must adapt it. Francois uses this function in his ras.pas unit which is available from his web site in file rasdial.zip.

function GetRASIPAddress: string;
var
  RASConns   : TRasConn;
  dwSize     : dword;
  dwCount    : dword;
  RASpppIP   : TRASpppIP;

Begin
  result          := '';
  RASConns.dwSize := SizeOf(TRASConn);
  RASpppIP.dwSize := SizeOf(RASpppIP);
  dwSize          := SizeOf(RASConns);

  If RASEnumConnections(@RASConns, @dwSize, @dwCount)=0 Then
      If dwCount > 0 Then begin
        dwSize := SizeOf(RASpppIP);
        If RASGetProjectionInfo(
            RASConns.hRasConn, RASP_PPPIP,
            @RasPPPIP, @dwSize) = 0 Then 
          result := strPas(RASpppIP.szIPAddress);
    End;
End;


 
[Return to Top]

  
How to use the GetRcvdCount properly?
Wilfried Mestdagh wilfried@mestdagh.biz    
18/04/2002

Do not rely on RcvdCount, this is not a reliable property. It is not TWSocket's fault, but Microsoft tell's just the same...

1.) GetRcvdCount return values:
 > 0 -> there is data, and most of the time the value is right
 < 0 -> error, you can safely exit the OnDataAvaliable
 = 0 -> probably no data, but you HAVE try to receive, if you dont you get wierd errors of even on some NT SP winsock does not wants to receive anymore.

2.) GetRcvCount versus the return value of the Receive method Both values can be different. The only value to taken into account is the return value of Receive, even if it is different than GetRcvdCount. This value can also be:
 < 0 -> error, just exit OnDataAvailable
 = 0 -> winsock does not wants to give data, so we exit also
 > 0 -> we have exacly received that amount of data

 
[Return to Top]

  
Increasing packet size
Francois Piette francois.piette@overbyte.be    
08/07/2003



Max packet size is determined by the underlaying physical media. Using Ethernet to transport TCP/IP on a LAN result is maximum packet size of 1514 bytes. There is no way to change that as it is by Ethernet design.

Note that increasing packet size is not a very good idea because - as you know - transmission is not 100% reliable. To the protocol layer use an error detection mechanism and retransmission mechanism to give you 100% reliability of TCP transport. Each time there is an error, a packet need to be retransmitted (you don't see it, but it is retransmitted). Having very large packet would decreasy overall thruput.

Note that packet are also dropped because of network congestion or because of speed mismatch (a 10 Mbps segment connected to a 100 Mbps for example). Each dropped packet need to be retransmitted later. Again having too large packet would cause big performance problem.

Also, increasing packet size would require much larger buffer in each intelligent device (router and the like) and in your computer. This would be a major limitation on the number of concurrent socket you can have and your computer would be unable to handle let's say 1000 or 10000 of simultaneous connections.

 
[Return to Top]

  
IP v6
Francois PIETTE francois.piette@overbyte.be    
21/04/2003

Have you ever thought about IPv6, and implementing it in TWsocket, or a special test version

The question has already been asked. As far as TWSocket is concerned, there should be not much changes as everything is handled at winsock level. The bigger change would be the size of an IP address and very few variables in TWSocket.

 
[Return to Top]

  
LineEdit and binary data
Wilfried Mestdagh www.mestdagh.biz    
14/01/2006

If LineEdit is on and receiving binary data I receive not enough bytes

If you have turned on LineEdit, and your binary data contains backspace characters then previous characters are deleted in input queue.

 
[Return to Top]

  
Listen on any port
Wilfried Mestdagh wilfried@mestdagh.biz    
2/12/2001

> I want let the OS choose a port for my server, how ?
> Also how do a know wich port is choosed ?


var
   saddr       : TSockAddrIn;
   saddrlen    : Integer;
   AssignedPort: Word;
begin
   Sock.Addr := '0,0,0,0';
   Sock.Port   := '0'; // Listen on any port
   Sock.Listen;

   // Get the port assigned by winsock
   saddrlen := SizeOf(saddr);
   Sock.GetSockName(saddr, saddrlen);
   AssignedPort := WSocket_ntohs(saddr.sin_port);
end;



 
[Return to Top]

  
LocalIPList
Jan Tomasek     
20.09.1998

> How to get the IP address for the computer running the program

In unit WSocket is declared function LocalIPList: TStrings.Remember that it is list of strings, it should contain more than one string. Typicaly if you are connected to local network and to internet trought modem.

 
[Return to Top]

  
Multicast
Mark G. Lewis Lewis@erg.sri.com    
26/10/2001

For Multicasting there are added 4 properties to TWSocket:

ReuseAddr
 permit multiple windows on the same PC to listen to the multicast socket

MultiCast
 allow socket to listen on a multicast port

MultiCastAddrStr
 specify a multicast IP address to listen on, like "225.1.2.3" note, this must be within the defined multicast address range 

MultiCastIpTTL
 change the Time-To-Live from the default of 1, care should be taken because nearby IP routers (gateways) may likely need to be   configured to allow

 Multicast sends UDP packets to a group address, and is therefore no more reliable (actually less) than other UPD usage.  Typically,   you build the reliability as needed in your own application for UDP or else use TCP.

 Multicast usage should be similar to using ICS with UDP without multicast, but make the multicast property 'true' and set the   address and port appropriately. You cast test by running the resulting program twice (start
2 windows) on you development PC if you set socket->ReuseAddr = true on the recieve sockets.

 I typically put 2 Wsocket components on my form, naming them XmtSocket and RcvSocket. To send, I set
        XmtSocket->Multicast = true;
        XmtSocket->Proto = "udp";
        XmtSocket->Addr  = "225.1.2.3";  // or whatever you decide
        XmtSocket->Port  = 12345; // or whatever you decide
        XmtSocket->Connect();
        XmtSocket->Send(buf, size);
        XmtSocket->Close();


 To recieve, I set
        RcvSocket->Proto             = "udp";
        RcvSocket->Addr              = "0.0.0.0";
        RcvSocket->MultiCast         = true;
        RcvSocket->ReuseAddr    = true;
        RcvSocket->Port              = 12345; // or whatever
        RcvSocket->MultiCastAddrStr  =225.1.2.3; // or whatever
        RcvSocket->Listen();


 On the event: OnDataAvailable, call a handler that calls:
 Len = RcvSocket->ReceiveFrom(data, sizeof(data), Src, SrcLen);


 
[Return to Top]

  
Multiple clients on same port
Wilfried Mestdagh wilfried@mestdagh.biz    
28/04/2001

> Can I connect more that one client to the same port

Yes you can. A TWSocket (or TWSocketServer) is listening on a port. When a client connect the connection is transferred by the OS to another free local port. The server socket stay listening on the same port letting other clients connect to it.

Note you can only 1 TWSocket(Server) listening on 1 particulary port.

When you have to listen on several ports you have to have many TWSocket(Server) listening on different ports. The only reason you wants to do this if you have different servers with different protocols (like POP3, SMTP, FTP, HTTP,...).

 
[Return to Top]

  
Multithreaded
Francois Piette  francois.piette@overbyte.be    
20.09.1998

> Do I need a multithreaded server

When you think about making a server which has to service many clients simultaneously, you immediately think you have to use multi-threading.

This is really unnecessary in most cases. Multi-threading is mandatory if you have some lengthy process to execute. Using TWSocket, telecommunication is definitively not a lengthy process. All transmissions take place in the background, even if you send a large data volume. TWSocket is fullybuffered. It means that it automatically buffers data (incoming or outgoing) without ever blocking your application.

TWSocket is fully event driven. This means that an event is generated each time processing is required. Every invocation of a TWSocket method is non-blocking. For example, when you connect to a remote computer, you call TWSocket.Connect which setup all things to start a connection and thenreturns almost immediately. Eventually when the connection is established, you get the OnSessionConnected event. During the interval, your application will continue to run.

If you send data, for example 10KB of data, much more than the receive is able to receive at once your application will not block. TWSocket.Send method is just like Connect: the data is put in a dynamically allocate buffer, everything is setup to send your data in the background and thenSend return almost immediately. Eventually your data gets transmitted (may after a long time). Your application gets the OnDataSent event.

In the client/server dialog, you are never blocked: you receive an OnDataAvailable event when some data is available (so you never wait for the client commands), the server responses are sent in the background.

So if you build a multi-user server - which is generally the case - you really don't have to do anything special. Ok, you must have a TWSocket for each client (create it dynamically or drop it onto a form and create the form dynamically like TnSrv does). But once each client has his ownTWSocket, everything goes through event handlers and never blocks you application. A client will never block another one.

Giving all that consideration, you have to use multi-thread only to process the actual request sent from the client, only if those requests take time (such a SQL request), not counting the transmission time.

Most of the time, if you have lengthy requests, you should use threads to execute each request in parallel. Do not use a threads for each client (MtSrv uses a thread for each client), but create a pool of worker threads to execute the requests. If your computer is able to process 20simultaneous requests, then create 20 threads. As clients send requests, find a free worker thread and let it execute the request, then returns the thread to the free thread pool. If more than 20 clients are connected and more than 20 are requesting at the same time, just queue their request, orupgrade your computer to be able to simultaneously execute more than 
20 requests at the same time within a reasonable time.

Don't forget that if you have a single processor computer, only one thread really executes at a given time. You get more performance using threads only if the processing for one thread is waiting for I/O. During that wait time, another thread can use the processor. Also note that your hardware has to be able to make several I/O requests at the same time to gain advantage of the threads. If you have a single disk or even multiple disks with IDE, you cannot perform several disk I/O's at the same time. If your process is CPU bound, you have no advantage using threads!

If you have a multi-processor computer, you have to use threads to benefit from the added power. And you will only see benefit if you are CPU bound and not I/O bound.

All in all, begin your server on the event driven model, and then later add the worker thread mechanism.

 
[Return to Top]

  
My server send two strings, I only receive one OnDataAvailable with both strings appended.
Francois Piette  francois.piette@overbyte.be    
04/03/2001

See "TCP/UDP primer" document.

If the sending application appends a line end marker (eg: #13#10) to each string, then you can set (in the receiving applicaiton):

     LineMode := True;
     LineEnd  := #13#10;

Then TWSocket will fire OnDataAvailable only when the LineEnd is received. The LineEnd character(s) are included in the received data.

 
[Return to Top]

  
No forms
Steve Williams   stevewilliams@kromestudios.com    
25/02/2001

> Using WSocket without forms

To handle the events, you only need an object, not necessarily a form.  If you define NOFORMS and recompile WSocket.pas, then it does not use the Forms unit and you must call the ProcessMessages method of the socket to allow it to process winsock messages.

 
[Return to Top]

  
OnDataAvailable gets called multiple times
Steve Williams   stevewilliams@kromestudios.com    
25/02/2001

Dont use Application->ProcessMessages() inside OnDataAvailable.  This is not required at all and will cause re-entrancy problems because the OnDataAvailable event is triggered by messages from Winsock.

 
[Return to Top]

  
OnError
Wilfried Mestdagh  wilfried@mestdagh.biz    
10/03/2001

Dont use the OnError property. It is there for people who does not wants to listen. If you assign code to it your own try / except blocks will never execute the except part of it. If you still wants to use it do can do it as follows:

procedure TForm1.WSocket1Error(Sender: TObject);
begin
    with Sender as TWSocket do
         Log(WsocketErrorDesc(LastError));
end;

but remember it is better to handle all exceptions yourself.

 
[Return to Top]

  
Why doesn't the OnDataAvailable event trigger in an ASP component (DLL)
Francois PIETTE francois.piette@overbyte.be    
28/02/2002


> I am making an ASP-component with ICS.
> For some reason - the OnSessionConnected
event triggers, but the
> OnDataAvailable
event doesn't seem to trigger, although it does when doing
> the same
> with a static socket. Have I forgot something?


There is no message pump in an ASP component. Use a thread with his own message pump and your events will start to work.

 
[Return to Top]

  
RcvdCount and received bytes
Steve Williams   stevewilliams@kromestudios.com    
25/02/2001

> Why it's happens, that function RcvdCount value not equal quantity of
> bytes, received by function Receive(...,RcvdCount)?

RcvdCount is not a reliable indicator.  Trust the value returned by Receive().

 
[Return to Top]

  
Reading all data in OnDataAvailable
Francois Piette francois.piette@overbyte.be    
18/08/2003

How can I know if I have read all data in OnDataAvailable, because maybe my buffer is smaller than the amount of data to be read. I solve it to read in a loop until there is no more to receive, is this correct?

If you don't read everything, then the component enter a loop calling OnDataAvailable event until all data has been read. Same happens if you read only a part (because if your buffer is to small).

I never tough about reading data by calling several times Receive from the OnDataAvailable. As Wilfried said, it is unsupported but may be perfectly correct even if I never intended this use. One should carefully study the code in the component to see if there are no cases where it would give bad results. I think that when LineMode is FALSE there should be no problem and I think there could be problems when LineMode is TRUE. To be verifyed !

 
[Return to Top]

  
Receive a string
Francois Piette  francois.piette@overbyte.be    
04/03/2001

> How do I receive a string

You can't receive a string. You can receive data into a string, but you receive only what is available at the time you call ReceiveStr. And you can call ReceiveStr only from the OnDataAvailable event. See discussion in my "TCP/UDP primer" document.

If the sending application appends a line end marker (eg: #13#10) to each string, then you can set (in the receiving applicaiton):

     LineMode := True;
     LineEnd  := #13#10;

Then TWSocket will fire OnDataAvailable only when the LineEnd is received. The LineEnd character(s) are included in the received data.

 
[Return to Top]

  
Receive count give very large numbers
Wilfried Mestdagh wilfried@mestdagh.biz    
06/03/2001

The return value of TWSocket.Receive can give the value -1. You have to assign it to a signed variable (eg: integer).

 
[Return to Top]

  
Receiving high speed data
Wilfried Mestdagh wilfried@mestdagh.biz    
2/05/2002

> How to receive continuesly high speed data stream.

- Receive always all data in OnDataAvailable
- Receive in a buffer that is always large enough
- Do not move data over and over again
- Process the data as quick as possible

Ok, let's discuss it from the beginning with some example code. In this example I will receive complete records. Of course when receiving binary data it is not possible to use LineMode (unless the data is escaped), so data has to be concatenated until there is a complete record. But also it is possible that more records at once are received.

Let's start with the record. Note that I use here a 'packed' record. In this case it will be the same code, but it is mostly a good idea, specially if ther possibility exists that the other end is written in a different language / OS / CPU. By using a packed record you are sure that all members are byte aligned:
type
   PTestRecord = ^TTestRecord;
   TTestRecord = packed record
     Ints : array[1..50] of integer;
   end;



Then we need a dynamicley allocated buffer with read / write pointers, because we dont want to move data whole the time. You can do this in a separate object, or in a derived TWSocket component or in a TForm. But this is purely depending on your application.
  private
    WrPtr: integer;
    RdPtr: integer;
    Buffer: PChar;
    BufferSize: integer;



Of course you have to take care that pointers are reset at the start of a connection. We alse start with some initional buffersize. Note that this initial size is purely depending on the speed that you receive data, the time to process the data and the speed of the machine. You have to experiment a little with it and do some logging to know the minimum needed. Do the test on the slowest machine you want to support with the fastest network you want to support. Specially if you plan to contiously sending data. Note that it can be better to throttle the sender or make a protocol where the receiver ackowledge every chunck of data. Note that we dont touch the buffer if it is already allocated, because we assume that is has growed to a good value in the previous session and we dont want to loose time again. In OnSessionConnected is probably a good place to use this code:
      RdPtr := 0;
      WrPtr := 0;
      if BufferSize = 0 then begin
         BufferSize := 16384;
         ReAllocMem(Buffer, BufferSize);
      end;



Now we descuss the OnDataAvailable in detail. As you see we use TR as PTestRecord, because we only are going to use pointers without moving data. First you have to check the Error argument. If it is not zero you can exit the procedure because you have nothing more to do there. Of course you will do something with the error (at least log it).
procedure TForm1.WSocket1DataAvailable(Sender: TObject; Error: Word);
var
   TR: PTestRecord;
   Count: integer;
begin
   if Error <> 0 then
      Exit;



Then we check the size of our receiving buffer. If it is smaller than some predefined value we are going to increase it. Again this value is depending on the same reasons as the setting of the initial value. During testfase you should log every incremening, just to see if your initial settings are fine.
   try
      if BufferSize - WrPtr <= 8192 then begin
         BufferSize := BufferSize * 2;
         ReAllocMem(Buffer, BufferSize);
      end;



Now we are ready to receive. Note that using Sender is a good idea here as you can have many TWSocket wich all using the same code. Note that Count can be 0 or less. Again in that case we have nothing to do anymore here and we exit the procedure. During testfaze you should log if Count is getting the same value as the empty room in the buffer. If so you have to change intitial values, the amount of bytes where you decide to increment the buffer and the incrental value itself.
      with Sender as TWSocket do
         Count := Receive(@Buffer[WrPtr], BufferSize - WrPtr);
      if Count <= 0 then
         Exit;



We received data, so we must increment the write pointer to get ready for next receive. Next we just check if we still have at least a complete record (or more), and just set TR to the right address in the receiving buffer. TR can be accessed in the proccesing handler as: TR^.Ints[n];. After processing the data we just increment our read pointer.
      Inc(WrPtr, Count);
      while WrPtr - RdPtr >= Sizeof(TTestRecord) do begin
         TR := @Buffer[RdPtr];
         ProcessData(TR);
         Inc(RdPtr, SizeOf(TTestRecord));
      end;



After processing we check if our buffer is empty. If so the both pointers have the same value. We then set it of course to 0 to start again from the begin of the buffer.
      if RdPtr = WrPtr then begin
         RdPtr := 0;
         WrPtr := 0;
      end;



And finally you do something with the exeption error if there was some. At least log it.
   except
   end;
end;



Here is the complete code:
procedure TForm1.WSocket1DataAvailable(Sender: TObject; Error: Word);
var
   TR: PTestRecord;
   Count: integer;
begin
   if Error <> 0 then begin
      Log('Winsock error: ' + IntToStr(Error));
      Exit;
   end;

   try
      if BufferSize - WrPtr <= 8192 then begin
         BufferSize := BufferSize * 2;
         ReAllocMem(Buffer, BufferSize);
      end;

      with Sender as TWSocket do
         Count := Receive(@Buffer[WrPtr], BufferSize - WrPtr);
      if Count <= 0 then
         Exit;

      Inc(WrPtr, Count);
      while WrPtr - RdPtr >= Sizeof(TTestRecord) do begin
         TR := @Buffer[RdPtr];
         ProcessData(TR);
         Inc(RdPtr, SizeOf(TTestRecord));
      end;

      if RdPtr = WrPtr then begin
         RdPtr := 0;
         WrPtr := 0;
      end;

   except
      on E: Exception do
         Log(E.Message);
   end;
end;



 
[Return to Top]

  
Re-establishing a connection
Wilfried Mestdagh  wilfried@mestdagh.biz    
10/03/2001

> my program is automated, and when ant any reason the session
> is closed I want to reetablish the connection

In OnSessionClosed you post a message. The message handler does the connection again. Youcan use the same message handler for auto-startup or reconfiguration (change port or addr).

 
[Return to Top]

  
Reverse DNS's results vary
bas steendijk@xs4all.nl    
27/06/2003


I am using TWsocket's reversedns function to find host name by ip address. I am mostly working with ftp servers and I would like to get nice hostname of the ftp server, like ftp.somesite.com, but results vary, ie:

213.*.*.12 returns www.somesite.com
213.*.*.13 returns ip-234-*-*-12.inet.com
213.*.*.14 returns ftp.somesite.com

Is there a way to specify reverse dns ?port? to get intended results?


- a reverse DNS on an IP may give an "official name" and a list of "aliases" - thus, more than one name.

- usually, the "good looking name" you are looking for, is one of the aliases. thus dont expect a reverse dns to give you a "fancy" name. thats normal DNS behavior.

- if you do a reverse DNS on an IP, you *should* also do a forward dns on the result of the reverse DNS; it must resolve and give the same IP you started with. otherwise the reverse DNS result is invalid/forged and you should discard it and show the IP instead.

- IP's can reverse and forward to a good looking hostname if it is intentionally set up that way; this is usually done for shells/bouncers, to look good on IRC.

 
[Return to Top]

  
Server and client at the same time
Steve Williams  stevewilliams@kromestudios.com    
28/04/2001

> Is it possible to have a application functioning as server and client at the same time?

Of course it is possible.  Since TWSocket is asynchronous, multiple socketscan be active at any time.  One or more could be listening for connections while others are making connections.


> But with a single TWSocket object ?

Technically, yes it can.  If it is a UDP listening socket, then it can send data using the SendTo method while listening.  This is how applications suchas ICQ send and receive UDP data from the same socket.

 
[Return to Top]

  
SocketState
Wilfried Mestdagh  wilfried@mestdagh.biz    
04.05.2001

> For what detailed are all these SocketStates?
> And when do they appear (or are the states of the Socket)?
>
>  TSocketState       = (wsInvalidState,
>                        wsOpened,     wsBound,
>                        wsConnecting, wsConnected,
>                        wsAccepting,  wsListening,
>                        wsClosed);

In general the socketstates are interesting for debugging or display purposes. However you have to take care fo some things if you display socketstate in the OnChangeState. For example if you close an application with an active socket. Some of the Delphi / Builder versions will try to do the display after the application has terminated so generating an error.

About the detail / meaning:
wsInvalidState: We have to begin somewhere dont we
wsOpened:       you try to connect, so the socket is opened first
wsConnecting:   trying to etablish the ocnnection with the other one
wsConnected:    as it says: we are connected
wsBound:        After the connection is etablished the sockets have to talk a little with each other. You will never see this state however.
wsListening:    As it says this is a listening socket (a server)
wsAccepting:    A client is connected, it has to be accepted
wsClosed:       As it says, (server or client) socket is closed.



> Is that right: If a client wants to connect to a server, and that server
> isnt listening, the client first has wsOpened, then wsClosed again.
> Whats the diffrence between wsOpened and wsConnecting? - Has the server
> already accepted a client, if the client gets wsConnecting? And a litte
> bit later, wsConnected?

When a client tries to connect and there's no server listening you get following sequence:
     - Opening
     - Connecting
     - Closed

The difference to Open and Connecting is when the socket is created, then it is opened. Connecting is the next step. When there's a server listening then 'Connected' follows the 'Connecting'. You can easy see it with a small test project where you do in OnChangeState:

procedure TForm1.SocketChangeState(
 Sender: Tobject; OldState, NewState: TSocketState);
const
    aWsState: array[TSocketState] of string = (
        'invalid',   'opened',    'bound',     'connecting',
        'connected', 'accepting', 'listening', 'closed'
  );
begin
    if not Application.Terminated then  // Dont forget this !!!
        Memo1.Lines.Add(aWsState[NewState]);
end;


 
[Return to Top]

  
Stop receiving connections and start again later
Francois Piette  francois.piette@overbyte.be    
20.09.1998

Just close the listening socket to block other connection attempts. Then when you want to accept connections again, reassign the properties (Addr, Proto, Port) for the listening socket and call listen again. You have to reassign properties because when a socket is closed, his properties are reset to the default values.

 
[Return to Top]

  
Thread and ProcessMessage
Francois PIETTE   francois.piette@overbyte.be    
25/02/2001

> Within a single Thread of creating many TWSocket, I do understand that
> with Threading we have to call TWsocket->ProcessMessage, I question will
> be, do we have to call TWsocket->ProcessMessage for all the created
> TWsockets?

You need to have ONE and excatly ONE message pump per thread, no matter how many TWSocket or other ICS components you create. To implement the message pump, you MAY choose to call ProcessMessage on only ONE  WSocket created in the thread's context (in the Execute method). Other message pump will do the same work.

 
[Return to Top]

  
Time_wait sockets
Graham Wright gswright@nc.rr.com    
26/10/2001


> Is there a way to force release TIME_WAIT?

 There's no hidden way to force sockets in TIME_WAIT to be closed - we have faced this in numerout servers we have written and the only answer is to allow the remote client to close the connection first. On the remote client *they* will have a socket in TIME_WAIT  but your server will not. Using this technique and the IO_Completion port model, we have been able to regularly handled 8,000 to 10,000 active sockets on an NT4 or W2K system.

 
[Return to Top]

  
Udp broadcast problem
Wilfried Mestdagh wilfried@mestdagh.biz    
14/01/2006

I hava a really stupid problem. I would like to send a udp broadcast to my network and receive the response form many devices. If i do this with the demo UdpSend ( Example from ICS ) it is working for devices with an Ip in my subnet and out of my subnet ( e.g. PC = 192.168.120.44 and device 192.168.120.70 is responding and device 10.20.121.22 also ), the Wsocket always trigges the event  SocketDataAvailable ( I included this event in the demo ). So the demo is workin correctly.

The crazy thing is that when I open a new project and copy the code, it is only working for devices with an IP within my subnet. I can see that I receive data from devices out of my subnet but the event WSocketDataAvailable is not triggerd.


I found the problem. In the registry the key
  HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\SharedAccess\Parameters\Fir
ewallPolicy\StandardProfile\AuthorizedApplications\List is an entry for the application. I donīt know why but only the exact path of this entry is allowed to receive the response from  Ip Adresses out of the subnet.

 
[Return to Top]

  
UDP Packet Fragmentation
Francois Piette francois.piette@overbyte.be    
14/01/2006

I'm getting this problem with UDP. The sender sends a 1625 byte packet per second, the recipient gets two packets instead: 1460 bytes and 165 bytes. I understand that UDP packets can be fragmented but that should be transparent to the application. If I'm right, I should receive one 1625 byte packet, instead of two smaller ones. Am I right? The buffer I used is 2048 bytes, which is big enough. My understanding is that UDP packets can be as big as 64K bytes.

You have to ask TWSocket to use a larger buffer (BufSize property). By default it is 1460 bytes
which is the largest TCP packet on Ethernet. Change BufSize according to the largest UDP datagram
you plan to receive.

 
[Return to Top]

  
How to avoid data loss with UDP packets larger than 1460 bytes
Francois PIETTE francois.piette@overbyte.be    
21/04/2002

With UDP, you must set TWSocket.BufSize equal to the largest datagram you want to send. The default value is 1460 bytes.

 
[Return to Top]

  
Why does DataAvaliable triggers only when socket is closed?
Wilfried Mestdag wilfried@mestdagh.biz    
18/02/2002


> When I set my client app to use LineMode, it receives information only
> on when socket is disconnected.
>
>     Any idea why DataAvaliable event is launched only when i make
> a clientsocket.disconnect.


You forgot to send the LineEnd characters. That's why you receive only when you close the socket.

 Sock.SendStr('Hello'#13#10);

 
[Return to Top]

  
Winsock error codes
Jan Tomasek    
24.09.1998

This article describes the Windows Sockets error codes. 

The following table is a list of possible error codes returned by the WSAGetLastError() call, along with their explanations.

Windows Sockets code Error Description
WSAEINTR            10004  Interrupted system call.
WSAEBADF            10009  Bad file number.
WSEACCES            10013  Permission denied.
WSAEFAULT           10014  Bad address.
WSAEINVAL           10022  Invalid argument.
WSAEMFILE           10024  Too many open files.
WSAEWOULDBLOCK      10035  Operation would block.
WSAEINPROGRESS      10036  Operation now in progress. This error is returned if any Windows Sockets API function is called while a blocking function is in progress.
WSAENOTSOCK         10038  Socket operation on nonsocket.
WSAEDESTADDRREQ     10039  Destination address required.
WSAEMSGSIZE         10040  Message too long.
WSAEPROTOTYPE       10041  Protocol wrong type for socket.
WSAENOPROTOOPT      10042  Protocol not available.
WSAEPROTONOSUPPORT  10043  Protocol not supported.
WSAESOCKTNOSUPPORT  10044  Socket type not supported.
WSAEOPNOTSUPP       10045  Operation not supported on socket.
WSAEPFNOSUPPORT     10046  Protocol family not supported.
WSAEAFNOSUPPORT     10047  Address family not supported by protocol family.
WSAEADDRINUSE       10048  Address already in use.
WSAEADDRNOTAVAIL    10049  Cannot assign requested address.
WSAENETDOWN         10050  Network is down. This error may be reported at any time if the Windows Sockets implementation detects an underlying failure.
WSAENETUNREACH      10051  Network is unreachable.
WSAENETRESET        10052  Network dropped connection on reset.
WSAECONNABORTED     10053  Software caused connection abort.
WSAECONNRESET       10054  Connection reset by peer.
WSAENOBUFS          10055  No buffer space available.
WSAEISCONN          10056  Socket is already connected.
WSAENOTCONN         10057  Socket is not connected.
WSAESHUTDOWN        10058  Cannot send after socket shutdown.
WSAETOOMANYREFS     10059  Too many references: cannot splice.
WSAETIMEDOUT        10060  Connection timed out.
WSAECONNREFUSED     10061  Connection refused.
WSAELOOP            10062  Too many levels of symbolic links.
WSAENAMETOOLONG     10063  File name too long.
WSAEHOSTDOWN        10064  Host is down.
WSAEHOSTUNREACH     10065  No route to host.
WSASYSNOTREADY      10091  Returned by WSAStartup(), indicating that the network subsystem is unusable.
WSAVERNOTSUPPORTED  10092  Returned by WSAStartup(), indicating that the Windows Sockets DLL cannot support this application.
WSANOTINITIALISED   10093  Winsock not initialized. This message is returned by any function except WSAStartup(), indicating that a successful WSAStartup() has not yet been performed.
WSAEDISCON          10101  Disconnect.
WSAHOST_NOT_FOUND   11001  Host not found. This message indicates that the key (name, address, and so on) was not found.
WSATRY_AGAIN        11002  Nonauthoritative host not found. This error may suggest that the name service itself is not functioning.
WSANO_RECOVERY      11003  Nonrecoverable error. This error may suggest that the name service itself is not functioning.
WSANO_DATA          11004  Valid name, no data record of requested type. This error indicates that the key (name, address, and so on) was not found.


You can find error code in winsock.hlp file. This file is available at ftp://ftp.stardust.com/sockets/wsa_dev.zip


 
[Return to Top]

  
Winsock socket options
Francois PIETTE francois.piette@overbyte.be    
21/07/2001

> Is that possible to access the socket Options through TWSocket
> to be able to join a Multicast membership like that ?


 Yes you can. You have acces to the handle in TWSocket.Handle property. Then you can call whatever winsock function you like provided you know what you are doing in order to keep the component working.

 
[Return to Top]

   

The ICS FAQ is created and maintained by the ICS VIP Documentation Effort Group.
For more information on how to join our efforts, send email to:
[ICS FAQ]