WxSocket

From WxWiki
Jump to navigation Jump to search

wxSocket

blocking versus non-blocking read

To get a 'non-polling' wait, you do a regular Read() with the wxSOCKET_BLOCK flag set. This would mean, though, that for example when this socket lives in a seperate thread, the thread will freeze until there actually is data that can be read (not processing shutdown messages).

If you use WaitForRead, wxSocket uses an active (polling) wait loop. This has the downside of pulling 100% CPU while in the busy loop.

To circumvent that, your best bet is to do a blocking Read() with a timeout so that Read() returns after a short while, thus giving the thread a chance to process the shutdown message. If the Read times out and there's no message to process, you can just Read() again.

Another workaround would be to create joinable threads with blocking sockets.

Minor gotcha: non-blocking Read() (and Connect() too) will not work if called from wxApplication::OnInit(). Something about needing the message loop active I presume. Hope this saves someone else some debugging time...

using sockets in a secondary thread (with wxMSW)

To be able to use wxSocket in a secondary thread with wxMSW, you must call wxSocketBase::Initialize() (undocumented function) from the main thread before creating any sockets. A good place to do that is in wxApp::OnInit(). For more information, please look at http://www.litwindow.com/Knowhow/wxSocket/wxsocket.html.

Random characters with wxSocketClient

If you're getting random characters in wxSocketClient, keep in mind that the wxChar array returned by the Read call isn't necessarily null-terminated, and might contain null characters at other places than the end.

Buffered Line Input

On http://www.neume.org/ is a bit of code under the name wxSocketLineReader. This demonstrates buffering incoming data until a complete line is available and calling and handler function with the data.

TODO items to add to this page

  • Describe where and why you should use ReadMsg() and WriteMsg() instead of Read() and Write()
  • Mention WaitForRead()
  • Why does sometimes reading the socket gets old data
  • I have started looking at IPv6 (Lathiat at irc-desk dot net), I know how to do it in unix, not so sure in Win32, but looking into it (12/2/2003)

UDP sockets

UDP sockets are currently not supported, but work is underway by Lenny Maiorani to implement this and get it into 2.5.1. However, some apps are using UDP sockets with 2.4.2 implementation of wxDatagramSocket and it's working on them (at least on unix)

Here is a function to play out a wav file to a remote UDP port using RTP and wxDatagramSocket (). If you want the functions for reading and writing the wavefiles then they are available from : http://www.numerix-dsp.com/files. This function has been tested under 2.4.2 on a Windows XP platform.

Many thanks must go to Janos Vegh, on whose code this is based.

void PlayOutWavToIP (char *inFile, char * DestnIPAddressString, unsigned short DestnPortNumber)
{
	FILE				*inFp = NULL;
	unsigned int		i = 0;
	struct sockaddr_in	DestnSockAddr;
	unsigned short		SequenceNumber = RTP_INITIAL_SEQUENCE_NUMBER;
	unsigned long		Timestamp = RTP_INITIAL_TIMESTAMP;
	unsigned int		bytes_read = 0;
	char				packet[RTP_HDR_SIZE + PAYLOAD_SIZE];
	short				WavDataArray [PAYLOAD_SIZE];
	struct timeb		CurrentTime, PreviousTime;
	short				ErrorCode;

	inFp = fopen(inFile,"rb");
//	DebugPrintf ("File opened : %s\n", inFile);

	// Create the destination address
	wxIPV4address m_localaddr, m_remoteaddr;
	wxDatagramSocket *m_socket;

	m_remoteaddr.Hostname (DestnIPAddressString);
	m_remoteaddr.Service (DestnPortNumber);


	m_localaddr.AnyAddress();
	m_localaddr.Service(0x8000);
										// Create the socket
	m_socket = new wxDatagramSocket (m_localaddr, wxSOCKET_NOWAIT);
	if (m_socket->Error ())
	{
        	DebugPrintf(_("Error %d at opening datagram socket"),m_socket->LastError());
	}

	WavInfo = wav_read_header (inFp);						// Read .wav header

	ftime (&PreviousTime);

	while ((bytes_read = fread ((void*)(WavDataArray), 2, PAYLOAD_SIZE, inFp)) == PAYLOAD_SIZE )
	{

		for (i = 0; i < PAYLOAD_SIZE; i++)
		{
			*(packet+RTP_HDR_SIZE+i) = linear2ulaw (WavDataArray[i]);
		}

		ftime (&CurrentTime);
								/* Wait for XX ms before sending next packet
									where XX depends on the size of the packet */
		while (((CurrentTime.time*1000)+CurrentTime.millitm) < (((PreviousTime.time*1000)+PreviousTime.millitm) + VIF_SIZE))
		{
			ftime(&CurrentTime);
		}
		PreviousTime = CurrentTime;

//		DebugPrintf ("SequenceNumber %d\n", SequenceNumber);

								/* Build up the RTP header */
		SequenceNumber++;
		Timestamp += PAYLOAD_SIZE;					/* PAYLOAD_SIZE for G.711 packets */
		*((short *)(packet+0)) = htons (0x08000);
		*((short *)(packet+RTP_SEQNUM_OFFSET)) = htons (SequenceNumber);
		*((long *)(packet+RTP_TS_OFFSET)) = htonl (Timestamp);
		*((long *)(packet+RTP_SSRC_OFFSET)) = htonl (RTP_SYNCHRONIZATION_SOURCE);

		m_socket->SendTo (m_remoteaddr, packet, RTP_HDR_SIZE + PAYLOAD_SIZE); 

		if (m_socket->Error ())
		{
			DebugPrintf(_("Error %d at sending packet"),m_socket->LastError());
		}

	} /* while */


	delete (m_socket);
	fclose (inFp);
}

Getting notified

Most people do their socket handling in another thread and send messages to the GUI threads as needed when the state of the app changes. Some other ideas can be found here:

http://wiki.wxpython.org/index.cgi/LongRunningTasks

wxSOCKET_INPUT_FLAG or wxSOCKET_INPUT

I found this quite confusing, that there are two different sets of values used for socket events: In the SetNotify() function, you need to use wxSOCKET_INPUT_FLAG, wxSOCKET_OUTPUT_FLAG, wxSOCKET_CONNECTION_FLAG and wxSOCKET_LOST_FLAG. However, in the actual events that are produced, the GetSocketEvent() function returns one of wxSOCKET_INPUT, wxSOCKET_OUTPUT, wxSOCKET_CONNECTION and wxSOCKET_LOST.

Behaviour of IsData() and WaitForRead(0, 0)

(wxWidgets 2.6.3 MSW)

First of all, I found that IsData() just calls WaitForRead(0, 0), which means that it doesn't block as stated in the manual. Then I noticed that WaitForRead(0, 0) sometimes returns false, although I believe there is input data available. This happened sometimes after moving the window around (?). The solution I found was to always call wxThread::Yield() prior to calling WaitForRead(0, 0). As far as I can tell, I don't have to do this for calls to WaitForRead() with a timeout other than 0.

I still sometimes don't get notified if there is new data on MSW (3.1.3) if the gui receives events, but the data is there when the next idle event is sent => might it be that the event loop needs to do something before the data can be processed in that case?

A way to get predictive results from IsData() without calling wxYield() is:

  • Don't process the data not from the event telling that there is data available,
  • But process the data from the next idle event instead. This event will be issued as soon as after processing the event that announces that there is new data the event queue gets empty again.