Here, the thread model refers to the thread model design related to Winsock.
Some problems in the design process of this software involve Winsock. In order to design the thread model well, we must understand the internal working mechanism of socket. Therefore, we need to analyze the data from the outside.
I. Why multithreading?
1. multithreading is used to prevent the main interface of the application from responding in I/O operations and false crashes.
Socket is a special type of I/O, so this phenomenon may occur. For example, when sending data or connecting to the server.
2. To improve the CPU usage (in multi-CPU environments) and improve the concurrent performance of applications.
In a multi-CPU environment, several threads can be executed on different CPUs at the same time, thus improving the application efficiency. In addition, applications sometimes need concurrency (including execution in turn in a single CPU environment) to make the application behavior smoother and more coherent. For example, if three tasks are submitted to one thread for receiving, sending, and processing packets, messages may be received or cannot be sent when the packets are processed.
Ii. Problems Caused by multithreading
Because the socket is I/O, multiple threads operate on the same I/O will cause complicated synchronization and mutex problems. If not properly handled, unpredictable results may occur.
Thread switching and management can reduce computer efficiency. The data structure required by the thread is also the memory overhead.
Iii. Key points in socket programming
1. Basic Socket Structure
Winsock is a network communication API programming interface on Windows. The TCP/IP protocol stack is only a subset of WinSock communication. Winsock can also support protocol stacks other than TCP/IP. BSD socket is a programming interface of the TCP/IP protocol stack on UNIX, so WinSock and BSD socket contain different protocol stacks. In WinSock programming, you must specify the protocol family and address type for the addresses to be listed. Because it supports many communication protocols.
Winsock description
The purple rectangle in the figure indicates the data buffer zone, and the nic and protocol stack both have the buffer zone. After the data arrives, it is first in the NIC buffer. At this time, the data driven by the network card is copied to the protocol stack buffer to which the data belongs. Finally, the application can remove data from the protocol stack buffer. When an application sends data, the data is first cached in the protocol stack buffer. When appropriate, the protocol stack copies the data to the NIC buffer through the NIC Driver, finally, the data is sent to the physical network by the NIC Driver. However, it should be clear that the data buffer of the NIC is much smaller than that of the protocol stack. Therefore, the buffer content of the protocol stack is the result of accumulating the content of the NIC buffer.
2. Large buffer zone
The Winsock API allows programmers to set the buffer size of the entire protocol stack. Setting the buffer to a greater value allows more customers to send data at the same time, and supports temporarily caching data sent by applications.
That is, when a large buffer zone is used, the Remote Sending program will not fail to send because the protocol stack cache is full; the local application will not fail to send because the cache is full. Or the sending is blocked when the stream socket is used.
3. Overlapping I/O
Overlapping I/O can improve the efficiency of applications in sending and receiving data.
After overlapping I/O is adopted, data will be directly copied from the NIC's data buffer to the application's data buffer, thus reducing a data buffer link of the protocol stack, eliminates many memory copy operations. This improves the efficiency of applications.
Overlapped Io
4. connectionless Winsock
1. Overview
In the network, you can use a triple to globally uniquely identify a process. The structure of the triple is: (protocol, local address, local port number ). This triple is called a semi-correlation.
A complete inter-network process communication must be composed of two processes and can only use the same high-level protocol (such as TCP and UDP ). That is to say, it is impossible to use TCP at one end and UDP at the other end. Therefore, a complete inter-network communication requires a quintuple to identify: (protocol, local address, local port, remote address, remote port number ). Such a quintuple is called full correlation. That is, two semi-correlations of the same Protocol can form a fully correlated, that is, a connection.
Connectionless socket
BIND () is related to whether it is connection-oriented. After a socket is generated, BIND () associates the socket with a local port. That is, the process registers an address for its own communication in the system. This is similar to specifying a telephone number for a service, such as 114 to query the service or a customer service hotline. Creating a socket is similar to creating a service, but the customer cannot communicate with the service before a telephone number is specified (before bind. After bind, the service provider must let the customer know the service number, that is, a semi-correlation (protocol, local address, local port number ).
BIND () is an explicit binding. The client generally does not have to be explicitly bound. For example, you can bind a bind through Connect (), sendto (), or other methods to generate semi-correlation between the client. If the client is explicitly bound, the client is actually no different from a server. This is important in the Multi-Point peering communication model.
First, the sender knows the address of the other party and sends data through sendto (). The receiver receives data through recvfrom () and obtains the address of the sender through the exit parameter of this function. Then, the sender can send data back. Therefore, the client does not need to explicitly BIND () an address. The reason why the server needs BIND () is that it first calls recvfrom (). The parameter of this interface requires a socket with a local address. If the client also explicitly binds an address, it can achieve end-to-end network communication.
Connectionless socket 2
The client does not use bind () to explicitly provide semi-correlation. It is bundled with sendto and recvfrom. How does the server send data back? Recvfrom () provides an exit parameter to return the source address, through which the server can send data back to the client.
2. Socket details
Socket is a special type of I/O, so socket is similar to file pointer and file handle. Data can be written and read through socket.
Socket schematic
The special feature of socket I/O is that data reading or writing cannot be performed immediately after a socket is created. It can be operated only when it is associated with a certain address. See this process from the connectionless protocol.
Sendto (): first, the party that executes sendto () must know the address of the other party before sendto () can write data into a socket.
Connectionless socket
First, the data receiver creates a socket, and then binds an address sockaddr1 to this special I/O. Then you can read the data from this socket: recvfrom (). This is generally the server side.
Then, on the client side, it must know the sockaddr1 address on the server side before writing data. Therefore, it creates a special I/O --- socket and then writes data to sendto (). In this case, sendto not only writes data to the specified address, but also implicitly binds the socket to a local address: sockaddr2.
In addition to reading the data provided by the customer through recvfrom (), the service provider can also obtain the sender's address through exit parameters. It uses sendto () and writes some data back to the client through any socket (using socket2 or creating a new one is a waste of resources, and then using socket2.
In the above case, the client implicitly binds the address to the new socket through sendto (), which is randomly generated by the system. The client can also explicitly bind an address before sendto (), and then sendto () will use this explicitly bound address as the source address, but this is not encouraged.
Summary: socket is a special type of I/O, through which data can be read and written. When using the socket, You need to bind an address. The bound address is the address used by the application to read data, that is, the address for receiving data. The address of the data written by the application, that is, the data sending Address, is not bound to the socket, and can be changed at will. Therefore, in the absence of a connection, data can be written to multiple addresses through the same socket, but there is only one data read address, that is, the address bound to this socket.
Connectionless multi-point communication
We can consider the preceding example as a server. The server creates soctet1 and then explicitly binds it with the address sockaddr2. Therefore, sockaddr2 is the server's address for reading data from socket1, with only one read address.
Then, the three clients know the server address sockaddr2, which writes data to the server, sendto (). Then, the server obtains data through recvfrom () and obtains the binding addresses of the three clients (both implicitly and explicitly ). The server writes data to the three clients through the same socket --- socket1 through the addresses of the three clients. The client obtains server data through recvfrom.
For one socket, only one address can be bound, that is, one BIND (). If a client writes data to three servers first, and the client does not explicitly bind the sockaddr2 address, the client calls sendto () for the first time () will be implicitly bound, and will not be later. The sockaddr2 allocated for the first binding will not change. The source address obtained by the three servers through recvfrom () is sockaddr2.
Broadcast: From the above principle, we can know that the broadcast without connection protocol is a special case of the socket Sending address, but it has nothing to do with the binding address and function. You only need to set the target address sockaddr of sendto () to the broadcast type.
3. connectionless socket and Multithreading
The connectionless socket is flexible. You can use the same socket to write data to multiple addresses and read data from the same address. Therefore, the organization of such servers is flexible. For example, you can use multiple threads to share the socket on the same server to read and write data.
However, it should be noted that socket is a special I/O. Since it belongs to I/O, thread synchronization and mutex are very important. Because the order in which they read and write the socket will not be guaranteed, or unexpected. Theoretically, a port number corresponds to a different buffer, that is, the port number is the handle of the data buffer on the TCP/IP protocol stack.
5. Connected socket
1. Overview
The Programming Method of a connected socket is very different from that of a Non-connected client and server.
Connection-oriented socket
It should be noted that the connection-oriented socket is in the model, the server creates a socket, and explicitly binds an address with this socket.
Connection-oriented socket 2
To learn more about the connection-oriented socket, we start with accept.
Accept () retrieves the first connection request from the specified socket connection request wait queue, and then it returns a new socket handle. The new socket can complete the connection request and start to serve it. The newly created socket has the same attributes as the socket used to listen for connection requests, including the same asynchronous selection event (events selected using the wsaasyncselect or wsaeventselect function ). Then, the connection request service is retrieved from the new socket as accept (), and the socket of the original listening request can return to the listening status.
So what is the difference between the communication details of the connection-oriented socket and that of the connectionless socket? This requires studying the Data Reading and Writing interfaces and Other interfaces for connected sockets.
Accept (), accepts client connection requests, and generates a socket to serve this customer. The exit parameter of accept () can provide the sockaddr of the customer, that is, the address. However, the service provider does not need to use this address to return some data. In the connection-oriented data writing method, only one socket is required. Send (). The connection-oriented data reading method, Recv (), can be completed through the same socket. The details of the communication process are as follows:
The server creates a special I/O --- socket, which is used to listen to client connection requests. Therefore, the socket needs to be associated with the local address of the server. As we know before, the BIND () address is the address for reading data from this socket, whether explicit or implicit.
Then the server calls listen (socket, num). Num is an integer that represents the maximum value of the Connection Request queue. For concurrent connection requests on this socket (request connection binding address), if the server cannot respond immediately, the message queue will be cached for processing by the server. However, when the queue is full, incoming requests cannot be responded. Listen () is a critical step. The server can listen to client requests only when this step is called.
After listen (), the server calls accept () and provides an exit parameter to obtain the requester's address. When the connection request queue on the specified accept socket is empty, accept () is blocked. However, before accept, the server has been requesting listen.
If the connection request cache queue on this socket has connection requests, the accept () will be executed out of the blocking status. Accept () creates a new socket for the current request service that is retrieved from the queue, and the socket () that is, the socket () of listen () continues to return to listen () status.
Connection-oriented communication process
For example, if the server creates a socket1 socket, it must be explicitly bound with a local address sockaddr1 to read data from socket1, or someone else can send data to this address.
Then, the server calls listen () on socket socket1 to listen for requests. Listen () specifies the size of the Request Buffer Queue. The Client Connection Request connect () before Listen fails.
After listen () is called, the server calls accept () to retrieve a connection request from the connection request queue for service. If the queue is empty, accept is blocked. Accept () extracts a request from the queue, creates a socket newsocket for the retrieved request, and returns the client address clientaddr1 of the request from the exit parameter. Newsocket has two features. The first one is that it has the same attributes as socket1. That is to say, newsocket is also bound to the address sockaddr1, which is the address for reading data from it. The other one is that newsocket and clientaddr1 are also connected. This address is the place where data is written into newsocket. Therefore, you do not need to retrieve the clientaddr1 client address of the exit parameter. You can read or write data by setting a full correlation with a specific client through the new socket newsocket server.
Check the client again. The client must first obtain the server's binding address sockaddr1. After the client creates a socket socket2, it calls the connect function on the socket. Connet () implicitly binds a local address clientaddr1 to socket socket2 as the Data Receiving address. Connect connects the server address sockaddr1 with socket2. Therefore, with socket2, the client can read and write data.
Connection-oriented
As shown in, the creation process is fully relevant. Now we have a unified idea: BIND () a local address on a socket (only a local address can be bound ), it is the data read address of the local program on this socket, but the other side of the communication is the data write address. The server generates a newsocket for each client. The difference is that the newsocket has different data write addresses. But it has the same binding address. Even so, the data sent by different clients will not be confused. It seems that the read address is related to the socket handle, so although different newsocket has the same read address, however, data is read.
The client knows the server address in advance, which is the write address of the client. In a connect () request, connect () implicitly binds a local address to the client as the read address, and displays the bound server address as the sending Address. The server accepts the request and obtains the client address as the write address. In this way, both sockets have read and write capabilities, so a data connection channel is established.
2. socket address
According to the previous analysis, we can think that the soket handle and the local binding address jointly determine the data receiving buffer or read buffer on the protocol stack. The write buffer or sending on the protocol stack is public (but different protocols cannot be public, such as TCP and UDP ). Therefore, different sockets can receive different contents for the same address. But the binding of the address on a socket can only be performed once, whether explicit or implicit.
3. concurrent connections
If the client fails to use connect for connection requests, multiple clients may have concurrent requests. The Server caches unresponsive requests in a request queue with the specified size in listen. At this time, the client connect () method cached for the request will return the correct result and continue the execution. But it will be blocked in the send and Recv methods.
If the number of concurrent connections exceeds the buffer size of the Connection Request, the connection connect () method that cannot be cached will return (it is not clear whether an error is returned ). However, if you call the send or Recv Method on this socket, an error is returned.
Therefore, if the service process of the connection request is time-consuming, in order not to wait for a long time for the client to be cached to fail to connect to other clients, multithreading is generally required. Because after the service is handed over to the sub-thread, the main thread will always have the opportunity to access more requests. Therefore, in addition to setting a larger request queue, multithreading is also a way to improve services.
4. Connect ()
Connect () has a function. In the connection-oriented example, a data sending or writing address is pushed to the socket, so that the socket is bound to two addresses. Explicit binding of the sending Address and implicit binding of the Receiving address.
A connectionless protocol can also call connect (), but in this case, connect () does not send a connection request to the server. In this case, an IP address is bound to a socket so that it has an associated data sending Address. In this way, you can use the send () and Recv () Protocols to write and read data on such a socket. Although a default Sending address has been bound, data can be sent to a non-default address through sendto.
BIND () is used to explicitly bind a data read address to a socket, which is a local address. The client does not encourage this method, but uses implicit binding. But if it is explicitly bound, it will not be wrong.