TWSocket Last Updated: 01/27/2007 |
Frequently Asked Questions for TWSocket:
- What is it?
- Blocking operation
- Broadcast vs. Multicast
- Calling ws2_32.dll direcly
- Instead of just getting a client to connect to a predetermined server IP, how would I go about clients broadcasting for a server IP?
- Closing
- COM object
- Compile with BCB4
- Connecting to a server without knowing his address
- Coping with Client disconnect and TWSocketServer?
- Datagram max or ideal size
- Disabling nagle algoritm
- DNS reverse lookup
- Dual processor
- Dup
- Dynamic ip address assigned by ISP
- How to use the GetRcvdCount properly?
- Increasing packet size
- IP v6
- LineEdit and binary data
- Listen on any port
- LocalIPList
- Multicast
- Multiple clients on same port
- Multithreaded
- My server send two strings, I only receive one OnDataAvailable with both strings appended.
- No forms
- OnDataAvailable gets called multiple times
- OnError
- Why doesn't the OnDataAvailable event trigger in an ASP component (DLL)
- RcvdCount and received bytes
- Reading all data in OnDataAvailable
- Receive a string
- Receive count give very large numbers
- Receiving high speed data
- Re-establishing a connection
- Reverse DNS's results vary
- Server and client at the same time
- SocketState
- Stop receiving connections and start again later
- Thread and ProcessMessage
- Time_wait sockets
- Udp broadcast problem
- UDP Packet Fragmentation
- How to avoid data loss with UDP packets larger than 1460 bytes
- Why does DataAvaliable triggers only when socket is closed?
- Winsock error codes
- Winsock socket options
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.
|
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. |
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... |
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. |
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.255and 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. |
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. |
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. |
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; |
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. |
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. |
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. |
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.
|
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. |
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. |
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. |
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; |
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 |
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. |
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. |
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. |
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; |
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. |
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); |
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,...). |
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. |
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. |
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. |
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.
|
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. |
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. |
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(). |
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 ! |
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. |
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).
|
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; |
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). |
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. |
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. |
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:
> 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; |
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.
|
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. |
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. |
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. |
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. |
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.
|
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); |
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.
You can find error code in winsock.hlp file. This file is available at ftp://ftp.stardust.com/sockets/wsa_dev.zip |
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. |