Socket has two kinds of synchronous blocking methods and asynchronous non-blocking methods, in fact synchronous and asynchronous may have encountered a lot in our programming career, and Socket is nothing special. Although synchronous is easy to use, not hard, but can not meet some applications, its efficiency is very low. Perhaps people who have been programmed should not understand "synchronous (or blocked)" and "asynchronous (or non-blocking)", in fact, two sentences can be clear, synchronous and asynchronous are often for a function, "synchronization "It is the function that the function returns until the feature to be executed is completed, and" asynchronous "is, the function only does some simple work, then immediately returns, and the function it wants to achieve is left or the function is completed. For example, SendMessage is a "synchronization" function, which not only sends a message to the message queue, but also waiting for the message to return; the opposite PostMessage is an asynchronous function, which only sends a message, regardless of whether the message is processed, return.
First, the Socket API should first be known that there is the original API function provided by Socket1.1, and a set of extended functions provided by Socket 2.0, two sets of functions. These two sets of functions have repeat, but the function provided by 2.0 is more powerful, and the number of functions is more. These two sets of functions can be flexibly mixed, which are included in the header file Winsock.h, Winsock2.h, respectively, the library WSOCK32.LIB, WS2_32.LIB.
1. The default is used as synchronous blocking mode, that is, when you never call WSAIOCTL () and IOCTLSocket () to change the socket IO mode, WSAAsyncSelect () and WSaEventSelect () are not called to select the Socket event you want to process. It is because of functions that act (), wsaaccept (), connect (), wsaconnect (), send (), wsasend (), rec (), wsarecv (), etc., so you may need to be set In the thread, this does not affect the running of the main program and the refresh of the main window. 2. If as an asynchronous use, then the program is mainly to handle events. It has two ways to handle events: the first, it often associates a window, that is, asynchronous sockets will be sent to this window as a message, which is a function wsaasyncselect () in the Winsock extension specification to implement and windows Relationship. Eventually you only need to handle window messages to send and receive data. The second, uses another function of the extended specification for the event WSAEventSelect (), which uses the event object to handle the Socket event, that is, you must first use WSACReateEvent () to create an event object, then call WSAEventselectElectr () To enable Socket's events and this event object. Eventually you will have to use WSAwaitFormultiPleEvents () to be triggered in a thread. This process is also slightly complicated. Second, CasyncSocket reads the name of the class, it is a asynchronous non-blocking Socket package, CasyncSocket :: Create () has a parameter indicating which Socket events you want to process, after your concern is specified, this Socket default Used as an asynchronous manner. So how do CasyncSocket inside will give it to you? CasyncSocket's create () function, in addition to creating a socket, created a CSocketWnd window object, and using wsaasyncselect () associates this socket with the window object to make the window object to handle events from Socket (message), However, CSocketWnd received a Socket event, just simply calls CasyncSocket :: OnReckeet :: Onsend (), CasyncSocket :: onsend (), CasyncSocket :: onConnect (). So CasyncSocket derived class, only you need to add send and receive code in these virtual functions. After simplification, the rough code is: BOOL CASYNCSOCKET :: Create (long] file: // Parameter level is the Socket event specifying you care {m_hsocket = socket (pf_inet, sock_stream, 0); file: // Create a Socket itself CSocketWnd * psockWnd = new csocketWnd; File: // Create a window of the response event, the actual window is created when the AFXSockInit () is called. PSockWnd-> Create;
WSAAsyncSelect (m_hSocket, pSockWnd-> m_hWnd, WM_SOCKET_NOTIFY, lEvent); file: // Socket and window events associated} static void PASCAL CAsyncSocket :: DoCallBack (WPARAM wParam, LPARAM lParam) {CAsyncSocket Socket; Socket.Attach ((SOCKET) wParam ); file: // wparam is the handle of the socket of this event INT NERRORCODE = WSAGETSELECTERROR (LPARAM); file: // lParam is an error code and the synthesis of the event code Switch (WsagetSelectEvent (LPARAM)) {Case FD_Read: psocket-> OnReceive (nErrorCode); break; case FD_WRITE: pSocket-> OnSend (nErrorCode); break; case FD_OOB: pSocket-> OnOutOfBandData (nErrorCode); break; case FD_ACCEPT: pSocket-> OnAccept (nErrorCode); break; case FD_CONNECT: pSocket -> OnConnect (NerrorCode); Break; Case FD_Close: PSocket-> OnClose (NerrorCode); Break;}} CsocketWnd class is roughly:
Begin_MESSAGE_MAP (CSocketWnd, CWND) ON_MESSAGE (WM_SOCKET_NOTIFY, ONSOCKETNOTIFY) end_MESSAGE_MAP ()
Lresult CsocketWnd :: OnSocketNotify (WPARAM WPARAM, LPARAM LPARAM) {CasyncSocket :: Docallback (WPARAM, LPARAM); File: // Receive Socket Event Messages, callback CasyncSocket's Docallback () function return 0l;}
However, it is not easy to be approved by the first school, and it is the most reminded this article. The client is using CasyncSocket :: Connect (), often returns a WSAEWOULDBLOCK error (other function calls are also the same. ), In fact, this should not be counted as an error, it is Socket reminds us that because you use a non-blocking socket mode, the (connection) operation takes time, and cannot be established instantly. In this case, we can wait, wait for its connection to success, so many programmers are then returning after calling connect (), then use WsagetLastError () or CasyncSocket :: getLastError () to view Socket Returning Error until returns to success. This is a wrong approach, asserts, you can't achieve the expected purpose. In fact, we can wait for the CasyncSocket :: onConnect () event after Connect (), CasyncSocket :: onConnect () is to indicate that the socket is either successful, either connection completely failed. At this point, we know if the socket connection is successful after CasyncSocket :: onConnect (), or it has failed. Similar, send () If we return to WSAEWOULDBLOCK error, we wait at OnSend (), resceive () If you return to WSAEWOULDBLOCK errors, we are waiting in OnReceive () to push. Another point, maybe it's a difficult point, that is, in the customer-party Connect () connection server, then the service party accept () to establish a connection problem. Simple practice is to establish a connection with a new CasyncSocket object when the Socket is listened to the Socket, for example: void cmysocket :: onaccept (int errcode) {cmysocket * psocket = new cmysocket; accept (* psocket); } Then, the PSocket and the client have established a connection. The future communication is the PSocket object and the client is going, and the listener Socket still continues to listen, once there is another client to connect the server, then on the onaccept () Will be called once. Of course, PSocket is a serve party communication with the client party, it does not trigger onaccept () event because it is not listening to Socket.
Third, CSocket Csocket is a package class that MFC derived in the CasyncSocket basis. How does it turn CasyncSocket to synchronize, and can also respond to the same SOCKET event? In fact, it is very simple, and CSocket returns a WSAEWOULDBLOCK error in Connect (), not in onConnect (), OnRecEve () to wait. You must first understand how the Socket event reaches these event functions. These event handlers are called by the CSocketWnd window object, while window objects receive events from Socket, and the thread message queue is divided. In summary, the Socket event first is sent to the CSocketWnd window object as a message. This message is definitely the distribution of the thread message queue, and the final CSocketWnd window object will call the corresponding callback function (onConnect (), etc.). So, CSocket After calling connect (), if it returns a WSAEWOULDBLOCK error, it immediately enters a message loop, which is a message that cares from the current thread, if you get a WM_PAINT message, refresh the window, if you get it The message sent by the socket, then call the corresponding callback function according to whether there is an operation error code, calling the corresponding callback function (onConnect (), etc.). The rough simplified code is:
Bool CSocket :: Connect (...) {if (! CasyncSocket :: Connect (...) {if (wsagetlasterror () == wsaewouldblock) file: // Due to the time required for asynchronous operation, it cannot be completed immediately, so Socket Returns this error {file: // Enter the message loop to view the FD_CONNECT message from the thread message queue until the FD_CONNECT message is received, and it is considered successful. while (PumpMessages (FD_CONNECT));}}} BOOL CSocket :: PumpMessages (UINT uEvent) {CWinThread * pThread = AfxGetThread (); while (bBlocking) file: // bBlocking simply a flag to see whether the user to cancel Connect ( ) call {MSG msg; if (PeekMessage (& msg, WM_SOCKET_NOTIFY)) {if (msg.message == WM_SOCKET_NOTIFY && WSAGETSELECTEVENT (msg.lParam) == uStopFlag) {CAsyncSocket :: DoCallBack (msg.wParam, msg.lParam) Return true;}} else {onMessagepending (); file: // Handling other messages in the message queue pthread-> onidle (-1);}}} Bool CSocket :: OnMessagepending () {msg msg; if (PEEKMESSAGE & msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE)) {file: // Here only WM_PAINT messages to handle main windows during blocking :: DispatchMessage (& MSG); Return False;}} Other CSocket functions , Such as Send (), received (), accept () enters the pumpMessages () message loop when receiving a WSAEWOULDBLOCK error. Such an original asynchronous CasyncSocket, turned into synchronous CSOCKET. After understanding, we can apply it to CSocket. For example, some programmers put CSocket's operation in a thread to achieve multi-threaded asynchronous socket (usually, synchronous multi-threads is similar to asynchronous).
Fourth, CSocketFile Additionally, SOCKET programming can not be mentioned without mentioning the CSocketFile class, in fact it is not used to send files between the Socket, but will require sequential data, such as some structural data, pass it to the other party, this, The serialization function of the program's cdocument () can be fully linked to CSocketFile. For example, you have a CMYDocument to implement serialize (), you can pass your document data to the other party of Socket: CSocketfile File (PSocket); Carchive Ar (& File, Carchive :: Store); pdocument-> serialize (Arialize Ar.close ();
Similarly, the receiving one can only change the above code to CARCHIVE AR (& File, CARCHIVE :: LOAD); Note that Although the CSocketFile class is derived from the CFILE, it shields a function of cfile :: open (), and only one exception is thrown in the function. Then, you can't call the csocketfile Open function to open a real file, otherwise it will cause an exception, if you need to use CSocketFile to transfer files, you must provide these functions of the CSocketFile class. One point, Carchive does not support serialization data on the Socket connection of DataGram.