In recent years, conventional services such as WWW browsing, FTP, and Gopher have been used for intercommunication over the Internet, it has become a research hotspot in applications that require strict real-time performance, such as network telephones and Multimedia conferences, and is already necessary. In Windows, the most basic method for communication program design is to use Windows Sockets to implement inter-process communication. Therefore, Microsoft provides a large number of communication APIs Based on Windows Sockets, such as WinSockAPI, WinInetAPI, and ISAPI, it has been committed to developing faster and easier communication APIs and integrating them with MFC to make communication programming easier and easier. This example focuses on how to use the CSocket class of MFC to write network communication programs, and uses the CSocket class to implement network chat programs. After the program is compiled and run, the interface effect 1 is shown as follows:
Figure 1. Web chat program interface |
I. Implementation Method
Microsoft's MFC encapsulates complex WinSock API functions into classes, making it easier to write network applications. CAsyncSocket classes encapsulate WinSock APIs one by one, providing more powerful and flexible methods for Senior Network programmers. This class is based on the assumption that programmers understand network communication and aims to use WinSock in MFC. programmers have the responsibility to handle tasks such as blocking, byte sequence, and character conversion between Unicode and MBCS. To provide more convenient interfaces for programmers to automatically process these tasks, MFC provides the CSocket class, which is inherited by the CAsyncSocket class, it provides a higher-level WinSock API interface than CAsyncSocket. The Csocket class and CsocketFile class can work with the Carchive class to manage the sent and received data, which makes the management data receiving and receiving more convenient. The CSocket object provides the blocking mode, which is crucial for Carchive synchronization operations. Blocking functions (such as Receive (), Send (), ReceiveFrom (), SendTo (), and Accept () do not return control until the operation is complete. Therefore, if you need lower-level control and high efficiency, use the CasyncSock class. You can use the Csocket class for convenience.
Some network applications (such as network phones and Multimedia Conferencing tools) have high real-time requirements and can directly use WinSock to send and receive data. To make full use of the advantages of MFC, the preferred solution should be the CAsyncSocket class or CSocket class in MFC. These two classes completely encapsulate WinSock APIs and provide more convenience. This example introduces the application programming models of these two classes, and introduces the relevant member functions and some concepts.
The CSocket class is inherited by CAsyncSocket. In fact, in MFC, CAsyncSocket encapsulates WinSock APIs one by one. Each CAsyncSocket object represents a Windows Socket object, the use of the CAsyncSocket class requires programmers to be familiar with network programming. In comparison, the CSocket class is a derived class of CAsyncSocket and inherits its encapsulated WinSock API.
A CSocket object represents an abstraction of a Windows Socket that is higher than the CAsyncSocket object. The CSocket class works with the CSocketFile class and the CArchive class to send and receive data, so it is easier to use. The CSocket object provides the blocking mode, Because blocking is crucial for CArchive synchronization operations. It is necessary to explain the concept of blocking: A socket can be in blocking mode or non-blocking mode. When a socket is in blocking mode (synchronous operation, its blocking function will return control until the operation is completed. Blocking is called blocking because the blocking function of this socket cannot do anything before the return of the operation is completed. If a socket is in non-blocking mode (that is, asynchronous operation), The called function will return immediately. In the CAsyncSocket class, you can use the GetLastError member function to query the final error. if the error is WSAEWOULDBLOCK, it indicates blocking, and CSocket will never return WSAEWOULDBLOCK because it manages blocking on its own. Microsoft recommends that you use the non-blocking mode whenever possible to notify the application to handle network events. However, in the CSocket class, to use CArchive to handle many problems in communication and simplify programming, some of its member functions are always congested because the CArchive class requires synchronous operations.
In the Win32 environment, if you want to use a socket with blocking nature, it should be processed in an independent working thread, and the multi-thread method should be used to prevent blocking from interfering with other threads, it will not waste CPU time on blocking. The multi-threaded method allows programmers to enjoy the convenience of simplified programming brought by CSocket, and does not affect the user interface's response to users.
CAsyncSocket Programming Model
In an MFC application, to easily process multiple network protocols without sacrificing flexibility, you can consider using the CAsyncSocket class, which is more efficient than the CSocket class. The Programming Model of CAsyncSocket class for byte flow socket is described as follows:
1. Construct a CAsyncSocket object and use the Create member function of this object to generate a Socket handle. It can be constructed in the following two methods:
CAsyncSocket sock; // use the default parameter to generate a byte stream socket Sock. Create (); |
Or generate a datagram socket on the specified port number.
CAsyncSocket * pSocket = newCAsyncSocket; IntnPort = 27; PSocket-> Create (nPort, SOCK-DGRAM ); |
The first method generates a CAsyncSocket object on the stack, and the second method generates a CAsyncSocket object on the stack. In the first method, the Create () member function generates a byte stream socket with the default parameter, the second method uses the Create () member function to generate a digital socket on the specified port. The prototype of the Create () function is:
BOOL Create (UINT nSocketPort = 0, int nSocketType = SOCK_STREAM, LPCTSTR lpszSocketAddress = NULL ); |
The function parameters include:
1) Port, UINT type. NOTE: If it is a service provider, a well-known port is used for the service provider to connect. If it is a customer, a typical practice is to accept the default parameter so that the socket can select an available port;
2) socket type, can be SOCK-STREAM (default value, byte STREAM) or SOCK-DGRAM (datagram );
3) socket address, such as "ftp.gliet.edu.cn" or "202.193.64.33 ".
2. If the client program is used, use the casyncsocket consumer connect () member function to connect to the service provider. If the provider program is used, use the casyncsocket consumer listen () member function to start listening. Once a connection request is received, then, call the casyncsocket ACCEPT () member function to start receiving. Note: The casyncsocket ACCEPT () member function uses a new and empty casyncsocket object as its parameter, the "null" here refers to the fact that this new object has not yet called the CREATE () member function.
3. Call other member functions of the casyncsocket class, such as receive (), receivefrom (), send (), and sendto () for data communication.
4. After the communication ends, destroy the casyncsocket object. If the casyncsocket object is generated on the stack, the object is automatically destructed when it exceeds the defined range. If it is generated on the stack, the new operator is used, the delete operator must be used to destroy the casyncsocket object.
CSocket Programming Model
The use of csocket objects involves carchive and csocketfile class objects. In the following steps, only step 1 is different for the customer and the service provider, and the other steps are the same.
1. Construct a csocket object.
2. Use the CREATE () member function of this object to generate a socket object. In the client program, unless the datagram socket is required, the CREATE () function should generally use the default parameter. The Service Provider Program must specify a port when calling CREATE. It should be noted that the carchive Class Object cannot work with the datagram (UDP) socket, so the use of the datagram socket, casyncsocket and csocket is the same.
3. If it is a client socket, call the CAsyncSocket consumer Connect () function to Connect to the service provider socket. If it is a service provider socket, call CAsyncSocket consumer Listen () start listening for connection requests from the client. After receiving the connection request, call the CAsyncSocket Accept () function to Accept the request and establish a connection. Note that the Accept () member function requires a new and empty CSocket object as its parameter, which is interpreted as the same as above.
4. Generate a CSocketFile object and associate it with the CSocket object.
5. Generate a CArchive object for receiving and sending data, and associate them with the CSocketFile object. Remember that CArchive cannot work with the datagram socket.
6. Use the Read () and Write () Functions of the CArchive object to transmit data between the customer and the service provider.
7. After the communication is completed, the CArchive, CSocketFile, and CSocket objects are destroyed.
Ii. programming steps
1. Start Visual C ++ 6.0, generate an application based on the dialog framework, and name the application "Test ";
2. Follow the Settings dialog box shown in Figure 1;
3. Use Class Wizard to add and click the message response function for the Application button;
4. Use Class Wizard to define the new Class CNewSocket in the application. The base Class is CSocket;
5. Add code and compile and run the program.
3. program code
//////////////////////////////////////// //// // NewSocket. h: header file # If! Defined (afx_newsocket_h1_8ce2ed73_1d56_11d3_9928_00a0c98f3e851_ded _) # Define afx_newsocket_h1_8ce2ed73_1d56_11d3_9928_00a0c98f3e851_encoded _ # If _ MSC_VER> = 1000 # Pragma once # Endif // _ MSC_VER >=1000 Class CTestDlg; # Include <afxsock. h>Class CNewSocket: public CSocket { // Attributes Public: // Operations Public: CNewSocket (); Virtual ~ CNewSocket (); // Overrides Public: Int m_Status; Void GetDlg (CTestDlg * dlg ); CTestDlg * m_dlg; // ClassWizard generated virtual function overrides // {AFX_VIRTUAL (CNewSocket) Public: Virtual void OnAccept (int nErrorCode ); Virtual void OnReceive (int nErrorCode ); Virtual void OnClose (int nErrorCode ); //} AFX_VIRTUAL // Generated message map functions // {AFX_MSG (CNewSocket) // NOTE-the ClassWizard will add and remove member functions here. //} AFX_MSG // Implementation Protected: }; # Endif //////////////////////////////////////// /// // NewSocket. cpp: implementation file # Include "stdafx. h" # Include "Test. h" # Include "NewSocket. h" # Include "TestDlg. h" # Ifdef _ DEBUG # Define new DEBUG_NEW # Undef THIS_FILE Static char THIS_FILE [] = _ FILE __; # Endif CNewSocket: CNewSocket () {} CNewSocket ::~ CNewSocket () {} # If 0 BEGIN_MESSAGE_MAP (CNewSocket, CSocket) // {AFX_MSG_MAP (CNewSocket) //} AFX_MSG_MAP END_MESSAGE_MAP () # Endif // 0 Void CNewSocket: OnAccept (int nErrorCode) { If (m_dlg-> m_ClientSocket = NULL) m_dlg-> OnAccept (); Csocket: onaccept (nerrorcode ); } Void cnewsocket: onreceive (INT nerrorcode) { M_dlg-> onreceive (); Csocket: onreceive (nerrorcode ); } Void cnewsocket: getdlg (ctestdlg * DLG) { M_dlg = DLG; } Void cnewsocket: onclose (INT nerrorcode) { M_dlg-> onclose (); Csocket: onclose (nerrorcode ); } //////////////////////////////////////// /// // Testdlg. h: header file # If! Defined (afx_testdlg_h1_eddde196_1bf1_11d3_be77_rjb454aee41_included _) # Define afx_testdlg_h1_eddde196_1bf1_11d3_be77_rjb454aee41_encoded _ # If _ msc_ver> = 1000 # Pragma once # Endif // _ msc_ver >=1000 # Include "newsocket. H" Class ctestdlg: Public cdialog { // Construction Public: Void socketreset (); Void onclose (); Void onreceive (); Void onaccept (); Csocketfile * m_file; Carchive * m_arout; Carchive * m_arin; Cnewsocket * m_serversocket; Cnewsocket * m_clientsocket; Ctestdlg (cwnd * pparent = NULL); // standard Constructor // Dialog data // {Afx_data (ctestdlg) Enum {IDD = idd_test_dialog }; Cstring m_info; Cstring m_output; Cstring m_input; Cstring m_connect; Cstring m_ipaddress; Uint m_port; Int m_status; //} Afx_data // Classwizard generated virtual function overrides // {Afx_virtual (ctestdlg) Protected: Virtual void dodataexchange (cdataexchange * PDX); // DDX/DDV support //} Afx_virtual // Implementation Protected: Hicon m_hicon; // Generated message map Functions // {Afx_msg (ctestdlg) Virtual bool oninitdialog (); Afx_msg void onsyscommand (uint NID, lparam ); Afx_msg void onpaint (); Afx_msg hcursor onquerydragicon (); Afx_msg void onconnect (); Afx_msg void ondisconnect (); Afx_msg void onsend (); Afx_msg void onserverradio (); Afx_msg void onclientradio (); Afx_msg void onsendclear (); Afx_msg void onreceiveclear (); //} Afx_msg Declare_message_map () }; # Endif //////////////////////////////////////// /// // Testdlg. CPP: implementation file # Include "stdafx. H" # Include "test. H" # Include "testdlg. H" # Include <afxsock. h> # Ifdef _ debug # Define new debug_new # UNDEF this_file Static char this_file [] = _ file __; # Endif Class caboutdlg: Public cdialog { Public: Caboutdlg (); // Dialog data // {Afx_data (caboutdlg) Enum {IDD = idd_aboutbox }; //} Afx_data // Classwizard generated virtual function overrides // {AFX_VIRTUAL (CAboutDlg) Protected: Virtual void DoDataExchange (CDataExchange * pDX); // DDX/DDV support //} AFX_VIRTUAL // Implementation Protected: // {AFX_MSG (CAboutDlg) //} AFX_MSG DECLARE_MESSAGE_MAP () }; CAboutDlg: CAboutDlg (): CDialog (CAboutDlg: IDD) { // {AFX_DATA_INIT (CAboutDlg) //} AFX_DATA_INIT } Void CAboutDlg: DoDataExchange (CDataExchange * pDX) { CDialog: DoDataExchange (pDX ); // {AFX_DATA_MAP (CAboutDlg) //} AFX_DATA_MAP } BEGIN_MESSAGE_MAP (CAboutDlg, CDialog) // {AFX_MSG_MAP (CAboutDlg) // No message handlers //} AFX_MSG_MAP END_MESSAGE_MAP () CTestDlg: CTestDlg (CWnd * pParent/* = NULL */) : CDialog (CTestDlg: IDD, pParent) { // {AFX_DATA_INIT (CTestDlg) M_Info = _ T (""); M_Output = _ T (""); M_Input = _ T (""); M_Connect = _ T (""); M_IPAddress = _ T (""); M_Port = 0; M_Status =-1; //} AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 M_hIcon = AfxGetApp ()-> LoadIcon (IDR_MAINFRAME ); } Void CTestDlg: DoDataExchange (CDataExchange * pDX) { CDialog: DoDataExchange (pDX ); // {AFX_DATA_MAP (CTestDlg) DDX_Text (pDX, IDC_OUTPUTEDIT, m_Output ); DDX_Text (pDX, IDC_INPUTEDIT, m_Input ); DDX_Text (pDX, IDC_CONNECTEDIT, m_Connect ); DDX_Text (pDX, IDC_IPADDRESS, m_IPAddress ); DDV_MaxChars (pDX, m_IPAddress, 15 ); DDX_Text (pDX, IDC_PORT, m_Port ); DDX_Radio (pDX, IDC_SERVERRADIO, m_Status ); //} AFX_DATA_MAP } BEGIN_MESSAGE_MAP (CTestDlg, CDialog) // {AFX_MSG_MAP (CTestDlg) ON_WM_SYSCOMMAND () ON_WM_PAINT () ON_WM_QUERYDRAGICON () ON_BN_CLICKED (IDC_CONNECTBUTTON, OnConnect) ON_BN_CLICKED (IDC_DISCONNECTBUTTON, OnDisconnect) ON_BN_CLICKED (IDC_SENDBUTTON, OnSend) ON_BN_CLICKED (IDC_SERVERRADIO, OnServerradio) ON_BN_CLICKED (IDC_CLIENTRADIO, OnClientradio) ON_BN_CLICKED (IDC_SENDCLEARBUTTON, OnSendclear) ON_BN_CLICKED (idc_javaseclearbutton, on‑eclear) //} AFX_MSG_MAP END_MESSAGE_MAP () BOOL CTestDlg: OnInitDialog () { CDialog: OnInitDialog (); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT (IDM_ABOUTBOX & 0xFFF0) = IDM_ABOUTBOX ); ASSERT (IDM_ABOUTBOX <0xF000 ); CMenu * pSysMenu = GetSystemMenu (FALSE ); If (pSysMenu! = NULL) { CString strAboutMenu; StrAboutMenu. LoadString (IDS_ABOUTBOX ); If (! StrAboutMenu. IsEmpty ()) { PSysMenu-> AppendMenu (MF_SEPARATOR ); PSysMenu-> AppendMenu (MF_STRING, IDM_ABOUTBOX, strAboutMenu ); } } // Set the icon for this dialog. The framework does this automatically // When the application's main window is not a dialog Seticon (m_hicon, true); // set big icon Seticon (m_hicon, false); // set small icon M_status =-1; M_serversocket = NULL; M_clientsocket = NULL; M_arin = NULL; M_arout = NULL; M_file = NULL; M_connect = ""; M_ipaddress = "202.207.243.29 "; M_port = 5000; Getdlgitem (idc_ipaddress)-> enablewindow (false ); Getdlgitem (idc_port)-> enablewindow (false ); Updatedata (false ); Return true; // return true unless you set the focus to a control } Void ctestdlg: onsyscommand (uint NID, lparam) { If (nID & 0xFFF0) = IDM_ABOUTBOX) { CAboutDlg dlgAbout; DlgAbout. DoModal (); } Else { CDialog: OnSysCommand (nID, lParam ); } } // If you add a minimize button to your dialog, you will need the code below // To draw the icon. For MFC applications using the document/view model, // This is automatically done for you by the framework. Void CTestDlg: OnPaint () { If (IsIconic ()) { CPaintDC dc (this); // device context for painting SendMessage (WM_ICONERASEBKGND, (WPARAM) dc. GetSafeHdc (), 0 ); // Center icon in client rectangle Int cxIcon = GetSystemMetrics (SM_CXICON ); Int cyIcon = GetSystemMetrics (SM_CYICON ); CRect rect; GetClientRect (& rect ); Int x = (rect. Width ()-cxIcon + 1)/2; Int y = (rect. Height ()-cyIcon + 1)/2; // Draw the icon Dc. DrawIcon (x, y, m_hIcon ); } Else { CDialog: OnPaint (); } } // The system callthis to obtain the cursor to display while the user drags // The minimized window. HCURSOR CTestDlg: OnQueryDragIcon () { Return (HCURSOR) m_hIcon; } Void CTestDlg: OnConnect () { CString msg; UpdateData (TRUE ); If (m_Status = 0) // server { If (m_ServerSocket! = NULL) { M_Connect = "Please disconnect! "; UpdateData (FALSE ); } Else { M_Connect = "Waiting for Client ..."; UpdateData (FALSE ); If (! AfxSocketInit ()) { MessageBox ("WindowsSocket initial failed! "," Send ", MB_ICONSTOP ); Return; } M_ServerSocket = new CNewSocket; M_ServerSocket-> m_Status = m_Status; M_ServerSocket-> GetDlg (this ); If (! M_ServerSocket-> Create (m_Port )) MessageBox ("SendSocket create failed! "," Send ", MB_ICONSTOP ); Else { M_ServerSocket-> Listen (); } } } Else { If (m_Status = 1) { If (m_ClientSocket! = NULL) { M_Connect = "Please disconnect! "; UpdateData (FALSE ); } Else { M_Connect = "Connect to the Server ..."; UpdateData (FALSE ); If (! AfxSocketInit ()) { MessageBox ("WindowsSocket initial failed! "," Receive ", MB_ICONSTOP ); Return; } M_ClientSocket = new CNewSocket; M_ClientSocket-> GetDlg (this ); M_ClientSocket-> m_Status = m_Status; If (! M_ClientSocket-> Create ()) { MessageBox ("ReceiveSocket create failed! "," Receive ", MB_ICONSTOP ); Return; } Else { If (! M_ClientSocket-> Connect (m_IPAddress, m_Port )) { CString str = m_Connect; SocketReset (); M_Connect = str; M_Connect + = "Error! "; UpdateData (FALSE ); } Else { M_Connect + = "OK! "; M_file = new CSocketFile (m_ClientSocket ); M_arIn = new CArchive (m_file, CArchive: load ); M_arOut = new CArchive (m_file, CArchive: store ); } UpdateData (FALSE ); } } } } If (m_Status =-1) { Msg = "Please choose the status! "; AfxMessageBox (msg ); } } Void CTestDlg: OnSend () { If (m_arOut) { If (m_Status = 0) { UpdateData (TRUE ); * M_arOut <m_Output; M_arOut-> Flush (); } Else { UpdateData (TRUE ); * M_arOut <m_Output; M_arOut-> Flush (); } } Else AfxMessageBox ("Not connected! "); } Void CTestDlg: OnAccept () { M_Connect + = "OK! "; UpdateData (FALSE ); M_ClientSocket = new CNewSocket; M_ClientSocket-> GetDlg (this ); M_serversocket-> Accept (* m_clientsocket ); M_clientsocket-> m_status = m_serversocket-> m_status; M_file = new csocketfile (m_clientsocket ); M_arin = new carchive (m_file, carchive: load ); M_arout = new carchive (m_file, carchive: Store ); } Void ctestdlg: onreceive () { * M_arin> m_input; Updatedata (false ); } Void ctestdlg: ondisconnect () { If (m_arout! = NULL) { Socketreset (); M_connect = "disconnected! "; Updatedata (false ); } } Void ctestdlg: onclose () { If (m_clientsocket-> m_status = 0) m_connect = "client "; Else m_connect = "server "; M_Connect + = "has disconnected! "; UpdateData (FALSE ); } Void CTestDlg: SocketReset () { If (m_arIn! = NULL) { Delete m_arIn; M_arIn = NULL; } If (m_arOut! = NULL) { Delete m_arOut; M_arOut = NULL; } If (m_file! = NULL) { Delete m_file; M_file = NULL; } If (m_ClientSocket! = NULL) { Delete m_ClientSocket; M_ClientSocket = NULL; } If (m_ServerSocket! = NULL) { Delete m_ServerSocket; M_ServerSocket = NULL; } M_Connect = ""; UpdateData (FALSE ); } Void CTestDlg: OnServerradio () { UpdateData (TRUE ); GetDlgItem (IDC_IPADDRESS)-> EnableWindow (FALSE ); GetDlgItem (IDC_PORT)-> EnableWindow (TRUE ); UpdateData (FALSE ); } Void CTestDlg: OnClientradio () { UpdateData (TRUE ); GetDlgItem (IDC_IPADDRESS)-> EnableWindow (TRUE ); GetDlgItem (IDC_PORT)-> EnableWindow (TRUE ); UpdateData (FALSE ); } Void CTestDlg: OnSendclear () { M_Output = ""; UpdateData (FALSE ); } Void CTestDlg: OnReceiveclear () { M_Input = ""; UpdateData (FALSE ); } |
Iv. Summary
This example introduces the CAsyncSocket and CSocket classes, and uses the CSocket class to implement network chat programs. Readers can also use the MFC CArchive object to send and receive information, making network transmission simple and easy to use, just like using the MFC file Serialization protocol.