CSocket Class
Derives from CAsyncSocket and inherits its encapsulation of the Represents a higher level of abstraction of the Windows Sockets API than that of a CAsyncSocket object.
class CSocket : public CAsyncSocket
Remarks
CSocket works with classes CSocketFile and CArchive to manage the sending and receiving of data.
A CSocket object also provides blocking, which is essential to the synchronous operation of CArchive. Blocking functions, such as Receive, Send, ReceiveFrom, SendTo, and Accept (all inherited from CAsyncSocket), do not return a WSAEWOULDBLOCK error in CSocket. Instead, these functions wait until the operation completes. Additionally, the original call will terminate with the error WSAEINTR if CancelBlockingCall is called while one of these functions is blocking.
To use a CSocket object, call the constructor, then call Create to create the underlying SOCKET handle (type SOCKET). The default parameters of Create create a stream socket, but if you are not using the socket with a CArchive object, you can specify a parameter to create a datagram socket instead, or bind to a specific port to create a server socket. Connect to a client socket using Connect on the client side and Accept on the server side. Then create a CSocketFile object and associate it to the CSocket object in the CSocketFile constructor. Next, create a CArchive object for sending and one for receiving data (as needed), then associate them with the CSocketFile object in the CArchive constructor. When communications are complete, destroy the CArchive, CSocketFile, and CSocket objects. The SOCKET data type is described in the article Windows Sockets: Background.
When you use CArchive with CSocketFile and CSocket, you might encounter a situation where CSocket::Receive enters a loop (by PumpMessages(FD_READ)) waiting for the requested amount of bytes. This is because Windows sockets allow only one recv call per FD_READ notification, but CSocketFile and CSocket allow multiple recv calls per FD_READ. If you get an FD_READ when there is no data to read, the application hangs. If you never get another FD_READ, the application stops communicating over the socket.
You can resolve this problem as follows. In the OnReceive method of your socket class, call CAsyncSocket::IOCtl(FIONREAD, ...) before you call the Serialize method of your message class when the expected data to be read from the socket exceeds the size of one TCP packet (maximum transmission unit of the network medium, usually at least 1096 bytes). If the size of the available data is less than needed, wait for all the data to be received and only then start the read operation.
In the following example, m_dwExpected is the approximate number of bytes that the user expects to receive. It is assumed that you declare it elsewhere in your code.
void CChatSocket::OnReceive(int nErrorCode)
{
CSocket::OnReceive(nErrorCode);
DWORD dwReceived;
if (IOCtl(FIONREAD, &dwReceived))
{
if (dwReceived >= m_dwExpected) // Process only if you have enough data
m_pDoc->ProcessPendingRead();
}
else
{
// Error handling here
}
}
Notes
When using MFC sockets in secondary threads in a statically linked MFC application, you must call AfxSocketInit in each thread that uses sockets to initialize the socket libraries. By default, AfxSocketInit is called only in the primary thread.
For more information, see Windows Sockets in MFC, Windows Sockets: Using Sockets with Archives, Windows Sockets: How Sockets with Archives Work, Windows Sockets: Sequence of Operations, Windows Sockets: Example of Sockets Using Archives.
Requirements
Header: afxsock.h