To facilitate network programming, Microsoft and several other companies jointly developed a set of network programming interfaces for windows in Early 1990s, namely, the Windows Sockets specification, which is not a network protocol, it is an open network programming interface in Windows that supports multiple protocols. Currently, Winsock has basically implemented protocol-independent functions. You can use Winsock to call the functions of multiple protocols, but the TCP/IP protocol is usually used. The socket actually provides a communication port in the computer, which can be used to communicate with any computer with a socket interface. Application Program The received information is transmitted over the network through this socket interface.
Microsoft defines Winsock classes for VC, such as casyncsocket and csocket classes born in casyncsocket. They are easy to use. Readers can certainly use these classes to implement their own network programs, however, in order to better understand Winsock API programming technology, we will discuss how to use underlying API functions to implement simple Winsock network application programming, and explain how to operate sockets on the server and client respectively, implement Data Transmission Based on TCP/IP, and finally provide the relevantSource code.
When developing Winsock APIs in VC, you must use the following three files in the project. Otherwise, a compilation error occurs.
1. Winsock. H: This is the header file of Winsock API and must be included in the project.
2. wsock32.lib: Winsock API Connection Library file. In use, it must be included in the project file as a non-default Connection Library of the project.
3. Winsock. dll: the dynamic Connection Library of WinSock, which is located in the installation directory of windows.
I. socket operations on the server)
1) Call wsastartup () during initialization ()
This function initializes Windows Sockets DLL in the application. Only after this function is successfully called can the application call other APIs in Windows Sockets DLL. The following code calls a function in a program: wsastartup (Word) (1 <8 | 1), (lpwsadata) & wsadata ), (1 <8 | 1) indicates that we use winsocket1.1, and wsaata is used to store winsocket information returned by the system.
2) Establish a socket
After initializing the Winsock dynamic connection library, you need to establish a listening socket on the server side. Therefore, you can call the socket () function to establish the socket for this listener, define the communication protocol used by the socket. If this function is called successfully, a socket object is returned. If the function fails, invalid_socket (wsagetlasterror () is called to find the cause. All winsocket functions can use this function to obtain the cause of the failure ).
Socket Pascal far socket (int af, int type, int Protocol)
Parameter: AF: currently, only pf_inet (af_inet) is provided );
Type: socket type (sock_stream, sock_dgram );
Protocol: Communication Protocol (set to 0 if not specified by the user );
If you want to establish a socket that complies with the TCP/IP protocol, the second parameter type should be sock_stream. For example, the socket that is UDP (datagram) should be sock_dgram.
3) bind the port
Next, you need to specify an address and port for the socket of the listener defined by the server so that the client can know which port to connect, therefore, we need to call the BIND () function. If this function is successfully called, 0 is returned; otherwise, socket_error is returned.
Int Pascal far BIND (socket S, const struct sockaddr far * Name, int namelen );
Parameter: S: Socket Object Name;
Name: the socket address value, which must be the IP address of the machine on which the program is executed;
Namelen: Name Length;
If you do not care about the address or port value, you can set the address to inaddr_any and port to 0, windows Sockets automatically sets the appropriate address and port (value between 1024 and 5000 ). Then you can call the getsockname () function to obtain the set value.
4) Listen
After the socket object on the server is bound, the server must establish a listener queue to receive client connection requests. The listen () function enables the socket on the server to enter the listening status, and sets the maximum number of connections that can be established (currently the maximum value is 5 and the minimum value is 1 ). If this function is successfully called, 0 is returned; otherwise, socket_error is returned.
Int Pascal far listen (socket S, int backlog ); Parameter: s: the socket to be monitored; Backlog: Maximum number of connections; |
After the server socket calls listen (), if the client calls the connect () function to apply for a connection, the server must call the accept () function again, in this way, the connection between the server and the client is deemed to have officially completed the communication program. In order to know when the client puts forward connection requirements, so that the socket on the server can call the accept () function to establish the connection at the right time, we need to use the wsaasyncselect () function, let the system send a connection request to a client. If this function is successfully called, 0 is returned; otherwise, socket_error is returned.
Int Pascal far wsaasyncselect (socket S, hwnd, unsigned int wmsg, long Levent ); Parameter: S: Socket object; Hwnd: the handle to receive messages; Wmsg: the message sent to the window; Levent: A registered network event, that is, a network event in which an application sends messages to a window. The value is a combination of the following values: fd_read, fd_write, fd_oob, fd_accept, fd_connect, and fd_close, the specific meaning of each value is fd_read: You want to receive a message when receiving data from socket s; fd_write: You want to receive a message when sending data on socket s; fd_accept: to receive a message when receiving a connection request on socket s; fd_connect: to receive a message when the connection on socket S is successful; fd_close: to receive a message when the connection on socket S is closed; fd_oob: if you want to receive out-of-band data on socket S, you will receive the message. |
For specific applications, wmsg should be the message name defined in the application, while lparam in the message structure is the name of the above network events. Therefore, you can use the following structure in the window processing custom message functions to respond to different Socket events:
Switch (lparam) {Case fd_read: ... Break; Case fd_write, ... Break; ... } |
5) The Server accepts client connection requests
When the client initiates a connection request, the hwnd window on the server receives a custom message from Winsock stack. In this case, we can analyze lparam and call related functions to handle this event. To enable the server to accept client connection requests, you must use the accept () function. This function creates a new socket that communicates with the client's socket. The socket originally listened to continues to enter the listening status, wait for others' connection requirements. If this function is successfully called, a new socket object is returned; otherwise, invalid_socket is returned.
Socket Pascal far accept (scoket S, struct sockaddr far * ADDR, int far * addrlen ); Parameter: S: Socket identifier; ADDR: the address of the client to be connected; Addrlen: length of ADDR |
6) End the socket connection
It is very easy to end the communication connection between the server and the client. This process can be started at either end of the server or client. You only need to call closesocket, this function is also used to disable the socket in the server listening status. In addition, it corresponds to the number of wsastartup () threads called during program startup. Before the program ends, you need to call wsacleanup () to notify Winsock stack to release the resources occupied by the socket. If both functions are called successfully, 0 is returned; otherwise, socket_error is returned.
Int Pascal far closesocket (socket S ); Parameter: S: Socket identifier; Int Pascal far wsacleanup (void ); Parameter count: None |
Ii. client socket operations
1) create a socket for the client
The client application first calls the wsastartup () function to establish a relationship with Winsock's dynamic connection library, and then calls socket () to establish a TCP or UDP socket (the same protocol can communicate with each other, TCP to TCP, UDP to UDP ). Different from the socket on the server, the client socket can call the BIND () function to specify the IP address and port number, but it can also not call BIND (), winsock automatically sets the IP address and port number.
2) Submit a connection Application
The client socket uses the connect () function to apply for a connection with the server socket. If the function call succeeds, 0 is returned; otherwise, socket_error is returned.
Int Pascal far connect (socket S, const struct sockaddr far * Name, int namelen ); Parameter: S: Socket identifier; Name: the address of the other party that the socket wants to connect; Namelen: Name Length |
Iii. Data Transmission
Although the TCP/IP-based connection protocol (stream socket) service is the mainstream standard for designing client/server applications, some services can also use the connectionless protocol (datagram socket) provided. First, we will introduce the features of TCP socket and UDP socket when transmitting data: stream (TCP) socket provides bidirectional, reliable, ordered, and non-repeated data transmission. Although datax (UDP) socket provides two-way communication, it is not reliable, ordered, and non-repetitive. Therefore, UDP data transmission may receive unordered and duplicated data, even data is missing during transmission. Because UDP socket does not guarantee that the data can be completely delivered to the other party during data transmission, most applications use TCP to process the socket to ensure the correctness of the data. Generally, the send and receive functions of the TCP socket call the send () and Recv () functions, while the UDP socket uses sendto () and recvfrom () functions () these two functions call the length of the data sent or received. Otherwise, socket_error is returned.
Int Pascal far send (socket S, const char far * Buf, int Len, int flags ); Parameter: S: Socket identifier Buf: the temporary storage area for storing the data to be transferred Len Buf: Length Flags: How this function is called |
For datax socket, if the size of datax exceeds the limit, no data is sent and an error value is returned. For stream socket, in blocking mode, if the storage space in the transmission system is insufficient to store the data to be transmitted, send () will be blocked until the data is delivered; if the socket is set to the non-blocking mode, the data will be sent based on the current output buffer space and will not be blocked. The flags value can be set to a combination of 0, msg_dontroute, and MSG_OOB.
Int Pascal far Recv (socket S, char far * Buf, int Len, int flags ); Parameter: S: Socket identifier Buf: the temporary storage area for storing received data Len Buf: Length Flags: How this function is called |
For stream socket, we can receive valid data in the current input buffer, but its quantity cannot exceed the Len size.
IV. Implementation of custom cmysocket classCode:
Based on the above knowledge, I have customized a simple cmysocket class. Below is some implementation code of the class I have defined:
////////////////////////////////////// Cmysocket: cmysocket (): file: // constructor of the class { Wsadata wsad; Memset (m_lasterror, 0, err_maxlength ); // M_lasterror is a string variable in the class. It is initialized to store the string with the last error description; // Initialize the addr_in structure variable in the class. The former stores the client address, and the latter corresponds to the server address; Memset (& m_sockaddr, 0, sizeof (m_sockaddr )); Memset (& m_rsockaddr, 0, sizeof (m_rsockaddr )); Int result = wsastartup (Word) (1 <8 | 1), & wsad); // initialize the winsocket dynamic Connection Library; If (result! = 0) // initialization failed; {Set_lasterror ("wsastartup failed! ", Wsagetlasterror ()); Return; } } ////////////////////////////// Cmysocket ::~ Cmysocket () {wsacleanup () ;}// class destructor; //////////////////////////////////////// //////////// Int cmysocket: Create (void) {// M_hsocket is the socket object in the class. Create a socket variable based on TCP/IP and assign the value to the variable; If (m_hsocket = socket (af_inet, sock_stream, ipproto_tcp) = invalid_socket) { Set_lasterror ("socket () failed", wsagetlasterror ()); Return err_wsaerror; } Return err_success; } //////////////////////////////////////// /////// Int cmysocket: Close (void) // close the socket object; { If (closesocket (m_hsocket) = socket_error) { Set_lasterror ("closesocket () failed", wsagetlasterror ()); Return err_wsaerror; } File: // reset the sockaddr_in structure variable; Memset (& m_sockaddr, 0, sizeof (sockaddr_in )); Memset (& m_rsockaddr, 0, sizeof (sockaddr_in )); Return err_success; } ///////////////////////////////////////// Int cmysocket: connect (char * strremote, unsigned int iport) // defines the connection function; { If (strlen (strremote) = 0 | iport = 0) Return err_badparam; Hostent * hostent = NULL; Long lipaddress = 0; Hostent = gethostbyname (strremote); // obtain the related content of the Computer Based on the computer name; If (hostent! = NULL) { Lipaddress = (in_addr *) hostent-> h_addr)-> s_addr; M_sockaddr.sin_addr.s_addr = lipaddress; } Else { M_sockaddr.sin_addr.s_addr = inet_addr (strremote ); } M_sockaddr.sin_family = af_inet; M_sockaddr.sin_port = htons (iport ); If (connect (m_hsocket, (sockaddr *) & m_sockaddr, sizeof (m_sockaddr) = socket_error) { Set_lasterror ("Connect () failed", wsagetlasterror ()); Return err_wsaerror; } Return err_success; } //////////////////////////////////////// /////////////// Int cmysocket: BIND (char * strip, unsigned int iport) // bind the function; { If (strlen (strip) = 0 | iport = 0) Return err_badparam; Memset (& m_sockaddr, 0, sizeof (m_sockaddr )); M_sockaddr.sin_family = af_inet; M_sockaddr.sin_addr.s_addr = inet_addr (strip ); M_sockaddr.sin_port = htons (iport ); If (BIND (m_hsocket, (sockaddr *) & m_sockaddr, sizeof (m_sockaddr) = socket_error) { Set_lasterror ("BIND () failed", wsagetlasterror ()); Return err_wsaerror; } Return err_success; } //////////////////////////////////////// // Int cmysocket: accept (socket s) // establishes a connection function. s indicates the name of the listener socket object; { Int Len = sizeof (m_rsockaddr ); Memset (& m_rsockaddr, 0, sizeof (m_rsockaddr )); If (m_hsocket = accept (S, (sockaddr *) & m_rsockaddr, & Len) = invalid_socket) { Set_lasterror ("accept () failed", wsagetlasterror ()); Return err_wsaerror; } Return err_success; } //////////////////////////////////////// ///////////// Int cmysocket: asyncselect (hwnd, unsigned int wmsg, long Levent) File: // event selection function; { If (! Iswindow (hwnd) | wmsg = 0 | Levent = 0) Return err_badparam; If (wsaasyncselect (m_hsocket, hwnd, wmsg, Levent) = socket_error) { Set_lasterror ("wsaasyncselect () failed", wsagetlasterror ()); Return err_wsaerror; } Return err_success; } //////////////////////////////////////// //////////// Int cmysocket: Listen (INT iqueuedconnections) // listener function; { If (iqueuedconnections = 0) Return err_badparam; If (Listen (m_hsocket, iqueuedconnections) = socket_error) { Set_lasterror ("Listen () failed", wsagetlasterror ()); Return err_wsaerror; } Return err_success; } //////////////////////////////////////// //////////// Int cmysocket: Send (char * strdata, int ilen) // data sending function; { If (strdata = NULL | ilen = 0) Return err_badparam; If (send (m_hsocket, strdata, ilen, 0) = socket_error) { Set_lasterror ("Send () failed", wsagetlasterror ()); Return err_wsaerror; } Return err_success; } //////////////////////////////////////// ///////////// Int cmysocket: receive (char * strdata, int ilen) // data receiving function; { If (strdata = NULL) Return err_badparam; Int Len = 0; Int ret = 0; Ret = Recv (m_hsocket, strdata, ilen, 0 ); If (ret = socket_error) { Set_lasterror ("Recv () failed", wsagetlasterror ()); Return err_wsaerror; } Return ret; } Void cmysocket: set_lasterror (char * newerror, int errnum) File: // The string setting function of the Winsock API operation error; { Memset (m_lasterror, 0, err_maxlength ); Memcpy (m_lasterror, newerror, strlen (newerror )); M_lasterror [strlen (newerror) + 1] = '\ 0 '; } |
With the definition of the above class, you can define the cmysocket object on the server and client of the Network Program, establish a connection, and transmit data. For example, in order to send data on the server and client, two cmysocket objects, serversocket1 and serversocket2, must be defined on the server side for listening and connection. The client defines a cmysocket object, clientsocket, it is used to send or receive data. If the number of established connections is greater than one, you can define a cmysocket object on the server. Note that the number of connections must not exceed five.
As there are many Socket API functions, such as obtaining the IP address and Host Name of the remote server and local client, the reader can further improve the cmysocket to implement more functions.