How to implement TCP / IP clients with Socket
- Example
1. Introduction, the Internet civilized, home appliance information, office automation, urban digitization. Communication has played a vital role in people's lives, work, entertainment. Today, most of the programs may involve communications, probably communicating with procedures developed by themselves, or may communicate with other programs on the network. TCP / IP is one of the most widely used protocols. Below I use Socket to implement TCP / IP client communication modules, share my personal practice with readers in this regard. 2. WINSOCKET introduction 2.1. From Socket 1.1 to Socket 2.0 initial WINDOW SOCKET1.1 applied only to popular TCP / IP at the time, other transport protocols are not supported. Programs implemented with Socket1.1 only support two types of sockets, namely connected SOCK_STREAM types, and SOCK_DGRAM-oriented SOCK_DGRAM. In view of the limitations of version 1.1, Microsoft has developed Socket 2.0. One of the main purposes is to provide an interface-independent interface that fully meets the needs of multimedia real-time communications. Notes here, Socket 2.0 is not an agreement, but an interface that can find a variety of available transport protocols. Therefore, some new functions have been added in Socket 2.0, which are headed by WSA. Socket 2.0 changes its architecture to make it easier to access multiple transport protocols. According to the principle of Windows Open System Architecture Model (WOSA), Socket 2.0 defines a standard service provider interface (Service Provider Interface, SPI). These interfaces are between the Application Programming Interface, API and Protocol Stacks, is commercially available to different services. The API is used in the program. As shown in Figure 2-1
Figure 2-1 Socket 2.0 Architecture 2.2. MFC's package in the Socket API in the MFC, there are two classes to encapsulate the Socket API, one is CasyncSocket, the other is CSocket, where the CSocket is inherited from CasyncSocket, Therefore, it is precisely that only CasyncSocket is really packaged in the Socket API. Socket abstraction after encapsulation makes programmers to fuse the Socket and MFC. If you want to use the CasyncSocket class separately, ask the programmer to have a deep understanding of Windows's socket, because it is only packaged for the Socket API, and the programmer also handles the problems that may encounter among the applications, such as blocking, byte order, Unicode To multiple byte character set (MBCS) conversion, etc. And these problems are not easy to handle. Fortunately, Csocket is more convenient to use. On the one hand, it is inherited from the CasyncSocket class, which is equivalent to encapsulating the Socket API; on the other hand, the class has handled the above problems, which greatly reduces the workload of the programmer. In addition, Csocket can also be used with CsocketFile and Carchive class, which is extremely convenient in some situations. 3. Examples of examples have a variety of methods for implementing TCP / IP communication with sockets. From substantially, it can be divided into two types of MFC and non-MFC. As the name suggests, the MFC mode is to implement two classes defined in the MFC, rather than MFC refers to not using the two classes. In the way, there is synchronous and asynchronous. Moreover, the implementation of the client and the server is also greatly different. Next, the example I want to talk about is a client communication module implemented by the author. The module is not much function, one is the reception and transmission of the data, the other is a reinforcement mechanism, because the network connection is not allowed to restart, and the program is required to be automatically reinable after the network connection is not allowed. In this example, there is a total of three classes (as shown in Figure 3-1), the name and function are as follows:
Figure 3-1 Module Class CsockClient, complete connection establishment, data reception, and sending function; ccommsvr, complete custom protocol function, data packaging, packet analysis, data distribution, etc .; CsyncData, processing thread data synchronization problem . Here, I want to combine a client instance (MFC mode) and some of the issues encountered during the implementation process. 3.1. When receiving Socket data by message-driven data, it is generally several ways. One is to call the Select function with a thread loop, and determine whether there is data arrival or a socket error by returning value. Another way is to implement the virtual function onRecEncEncSocket provided by CasyncSocket, which calls other functions to receive data, and the request Socket sends the message to the specified window by calling the AsyncSelect function, and then calls the message in the message processing function of the window. The corresponding function receives data. For the first method, most of them are used in non-MFC mode. In MFC mode, the latter two methods are mainly used. For the latter two methods, it is basically the same, but the window of the received message is not the same. Perhaps most readers have discovered that actually Socket itself has a Socket Notification window, which is invisible, but can receive and process messages. So how do you use the use of the two methods? The author believes that for a simple client, the possible data reception and transmission is done in a thread. In this case, consider the request Socket to send the message to the main window, then process these messages in the main window. Such as connections, receive data, etc. That is, the third approach is used for a miscellaneous client. Since the data is received, it is handled in a thread different from the main thread. At this time, considering the modularization of the program, it is not appropriate to send the Socket message. To the main window, the second method should be adopted, that is, the virtual functions provided by CasyncSocket are implemented. 3.2. Data synchronous thread synchronization between threads is a key and difficult point in multi-thread programming. There is a principle between data synchronization between threads, that is to concentrate on the data you want to synchronize and minimize data to be synchronized. The purpose of synchronization is to ensure the validity and integrity of the data, in an intuitive, that is, no two or two following threads accesses the same data block. In this example, I specifically used a class to achieve data synchronization, called CsyncData. The idea of achieving is like this. The communication module is only responsible for the analysis and distribution of data, and is not responsible for the processing of data. When the communication module receives data, after the communication module is parsed as a valid data, the communication module is distributed according to the data type, and the data is placed in the corresponding linked list in the CSyncData instance (for example, calling INSERTACK). The synchronization is to be synchronized because it is possible to access the list to be accessed by other threads. On the other hand, send messages to other threads, notifying new data to be processed. Other threads receive the message, the data is processed. At this time, you should first get the corresponding data (for example, getack) from the linked list, which is also synchronized because there may be other threads in the access list. This focuses on the synchronization of the data in CsyncData, which is handled by this class, clear structure, and it is not easy to make mistakes.
The specific synchronization process is simple, such as: // Add a list of recipromened information to the list void csyncdata :: Insertack (Sack * Pack) {CSILOCK LOCK (& M_CSACK); Lock.lock (); if (! Lock.islocked ()) Return; // Add data to list m_acklist.addtail (pack); if (m_acklist.getcount ()> max_list_count) delete m_acklist.removehead ();
LOCK.unlock ();
// Remove one to the list to send some information Sack * csyncData :: getack () {sack * pack = null;
CSILOCK LOCK; LOCK.LOCK (); if (! Lock.islocked ()) Return NULL;
// Remove the data from the list (m_acklist.getcount () package = m_acklist.removehead ();
Lock.unlock ();
Return Pack;} 3.3. Processing the Pending Message Perhaps the reader has encountered such a problem. When your client program tries to establish a connection with the remote program, the program host IP address does not exist, the program will temporarily block. Of course, if you use multiple threads, users may not feel. But if you want to exit the program at this time? There will be a phenomenon that the program cannot withstand immediately. Where is the reason? Is such that. When you call the Connect function attempt to connect with the remote host, if the host does not exist, the connection will return immediately, about 20 seconds. This will not occur if the host exists. So, if you want to quit the program quickly, what should I do? A virtual function is provided in the CSocket class. OnMessGepending. When the Socket is waiting for a message, the framework calls the function to processes other messages. This allows this function to quickly exit the program. Code is as follows: BOOL CSockClient :: OnMessagePending () {MSG msg; if (:: PeekMessage (& msg, NULL, WM_QUIT, WM_QUIT, PM_NOREMOVE)) {if (IsBlocking ()) CancelBlockingCall (); return TRUE;} return FALSE;} In this way, if other functions are called, it can be quickly exited quickly. 3.4. How to quickly position the frame header If the communication is only text information, there may be no such problem. But most of the cases, you may use your defined protocol, which will have a problem with the frame header. The usual practice is that when there is data, first find a frame head, one pick one, until the frame head is found. In normal case, this method is feasible, but it is possible that the data is wrong, the data is read, and the frame head is not found. This program has been waiting for data until the next frame data arrives. At this point, if the remote program is abnormal, the client will wait unlimited. Of course, to exit the program or a problem. When I encountered this problem: Whenever there is data arriving, I read a byte, and it is determined whether or not the definition of the frame is consistent, if yes, continue. Once you find that you are not pair, you immediately quit the receiving function, wait for the next message to be detected. The advantage of this is that it can basically guarantee that the remote data is not waiting, since after receiving the message, there is at least one byte of data. Moreover, in normal case, its effect is the same as the method mentioned above. Insufficient, if the data is wrong, there will be frequent call reception functions. But it is better than "death, etc.". 3.5. Running under the Release version If you use the CSocket in the thread other than the main thread, the Release version may not be able to run, but the debug version is no problem. This is Microsoft problem. The solution is simple, as long as the thread is initialized, add the following code to: // Initialize the Socket in the thread. #ifndef _afxdll #define _afx_sock_thread_state afx_module_thread_state #define _afxsockthreadState AFXGETMODULETHREADSTATE ()
_AFX_SOCK_THREAD_STATE * pState = _afxSockThreadState; if (pState-> m_pmapSocketHandle == NULL) pState-> m_pmapSocketHandle = new CMapPtrToPtr; if (pState-> m_pmapDeadSockets == NULL) pState-> m_pmapDeadSockets = new CMapPtrToPtr; if (pState-> m_plistSocketNotifications == NULL) PState-> m_plistsocketnotification = new cptrlist; #ENDIF 4. Summary The communication module is a very important module for many programs. A good communication module is not only efficient, but also strong. Communication is a dynamic process, the situation is more complicated, and debugging is also relatively laborious, so it is necessary to write a good communication module and must have a certain bitterness. The above talks about some of my individual's shallow understanding of Socket and a little experience in the practice process. Insufficient or wrong, please ask you to advice. Mutual encouragement from Beijing Software Technology Co., Ltd. of three trees:! HC.Kang 2002-07-02