For the server in network communication, it is obviously impossible to be one-to-one. What we hope is that the server can selectively communicate with a specific client when it is enabled, and when it does not need to communicate with the client , you only need to store the socket in the linked list and wait for subsequent operations. The socket server uses multi-threading to store sockets and select communications, which can improve the concurrency performance of the server and enable it to process multiple client request. In practical application scenarios, this technology is widely used in network programming, Internet applications and other fields.
The specific implementation idea of this function can be summarized as the following process;
When the server starts, a socket is created and bound, and then a thread (called the main thread) is started to listen for client connection requests. After receiving a new connection request, the main thread will add the corresponding socket to a data structure (such as a linked list, queue, hash table, etc.) for storage. At the same time, the main thread will pass the data structure of the storage socket to each sub-thread, and start multiple sub-threads for service. Each sub-thread takes out the socket from the data structure of the storage socket, and then communicates with it through the socket. client communicates.
When it comes to selecting communications, users can specify which client they want to communicate with. The server will search for a socket that meets the conditions in the data structure that stores the socket, and then send the communication data to the corresponding client.
First of all, in order to realize the storage function of the socket, here we need to define a ClientInfo
. This structure has only one defined function, which is to store the FD
handle of the socket. As well as the IP
address and port information of the socket, this structure should be defined as follows;
typedef struct {<!-- --> SOCKET client; sockaddr_in saddr; char address[128]; unsigned short port; }ClientInfo;
Next, let’s look at the implementation in the main function. First, listen
in the main function listens to the socket connection normally. When a new socket is connected, it directly passes CreateThread function opens a sub-thread. The sub-thread is hung in the background through the
EstablishConnect
function. Before hanging into the background, it is used by the std::vector
global variable. to save the socket.
When readers need to send data, they only need to call the SendMessageConnect
function. The function receives a socket list, the IP
address information that needs to be operated, and the data packet that needs to be sent. , when this information is available, the function will first determine whether it is the IP
we need to communicate based on the IP
address. If so, the socket will be taken out from the global linked list. words and send packets to specific clients.
Pop up a socket and call PopConnect
. This function receives a global linked list and a string IP
address. It internally searches for IP
by enumerating the linked list. code> address, if found, directly use the ptr.erase(it)
method to pop the found socket out of the linked list, and thereby achieve the purpose of closing communication.
When outputting socket elements, it is implemented by calling the ShowList
function. Internally, this function first enumerates all sockets through a loop and then Ping
tests them sequentially. If a disconnection is found, The socket is directly removed from the linked list. If there is no disconnection, the client will feedback a pong
to indicate that it is still there, and the socket information can be directly output at this time.
14.10.1 Server-side implementation
The server-side implementation has been briefly introduced in the above overview. The principle of server-side implementation can be summarized as follows: wait for the client to come online through multi-threading technology. When a client comes online, it is directly added to the global linked list to wait for operations. The main function executes an infinite loop, waiting for the user to input data, which is used to choose to communicate with a certain socket.
#include#include #include #include #include #pragma comment(lib,"ws2_32.lib") using namespace std; typedef struct {<!-- --> SOCKET client; sockaddr_in saddr; char address[128]; unsigned short port; }ClientInfo; std::vector info; // Global host list SOCKET server; // local socket sockaddr_in sai_server; //Storage server IP and port // Pop up the offline host void PopConnect(std::vector & amp;ptr, char *address) { // Loop iterator to find the elements that need to be popped out for (std::vector ::iterator it = ptr.begin(); it != ptr.end(); it + + ) { ClientInfo *client = *it; // If found, remove it from the linked list if (strcmp(client->address, address) == 0) { ptr.erase(it); // std::cout << "Address: " << client->address << "Offline" << std::endl; return; } } } // Output the current host list void ShowList(std::vector & amp;ptr) { for (int x = 0; x < ptr.size(); x + + ) { //Send Ping signal, detect bool ref = send(ptr[x]->client, "Ping", 4, 0); if (ref != true) { PopConnect(info, ptr[x]->address); continue; } // Receive detection signal to see if it is alive char ref_buf[32] = { 0 }; recv(ptr[x]->client, ref_buf, 32, 0); if (strcmp(ref_buf, "Pong") != 0) { PopConnect(info, ptr[x]->address); continue; } std::cout << "Host: " << ptr[x]->address << " Port: " << ptr[x]->port << std::endl; } } // Send a message void SendMessageConnect(std::vector &ptr, char *address, char *send_data) { for (int x = 0; x < ptr.size(); x + + ) { // std::cout << ptr[x]->address << std::endl; // Determine whether it is the IP that needs to be sent if (strcmp(ptr[x]->address, address) == 0) { //Send data to the selected host send(ptr[x]->client, send_data, strlen(send_data), 0); int error_send = GetLastError(); if (error_send != 0) { // std::cout << ptr[x]->address << "Offline" << endl; // pop up element PopConnect(info, address); return; } // Get execution results char recv_message[4096] = { 0 }; recv(ptr[x]->client, recv_message, 4096, 0); std::cout << recv_message << std::endl; } } } // Create socket voidEstablishConnect() { while (1) { ClientInfo* cInfo = new ClientInfo(); int len_client = sizeof(sockaddr); cInfo->client = accept(server, (sockaddr*) & amp;cInfo->saddr, & amp;len_client); // Fill in the host address and port char array_ip[20] = { 0 }; inet_ntop(AF_INET, & amp;cInfo->saddr.sin_addr, array_ip, 16); strcpy(cInfo->address, array_ip); cInfo->port = ntohs(cInfo->saddr.sin_port); info.push_back(cInfo); } } int main(int argc, char* argv[]) { //Initialize WSA and activate socket WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), & amp;wsaData); //Initialize socket and server information server = socket(AF_INET, SOCK_STREAM, 0); sai_server.sin_addr.S_un.S_addr = 0; // IP address sai_server.sin_family = AF_INET; // IPV4 sai_server.sin_port = htons(8090); //Transport protocol port //Local address associated socket if (bind(server, (sockaddr*) & amp;sai_server, sizeof(sai_server))) { WSACleanup(); } //The socket enters the listening state listen(server, SOMAXCONN); // Establish a sub-thread to implement listening connections CreateThread(0, 0, (LPTHREAD_START_ROUTINE)EstablishConnect, 0, 0, 0); while (1) { char command[4096] = { 0 }; input: memset(command, 0, 4096); std::cout << "[ LyShell ] # "; // send command int inputLine = 0; while ((command[inputLine + + ] = getchar()) != '\\ '); if (strlen(command) == 1) goto input; // Output host list if (strcmp(command, "list\\ ") == 0) { ShowList(info); } // Send a message else if (strcmp(command, "send\\ ") == 0) { SendMessageConnect(info, "127.0.0.1", "Send"); } //Send CPU data else if (strcmp(command, "GetCPU\\ ") == 0) { SendMessageConnect(info, "127.0.0.1", "GetCPU"); } //Send exit message else if (strcmp(command, "Exit\\ ") == 0) { SendMessageConnect(info, "127.0.0.1", "Exit"); } } return 0; }
14.10.2 Client implementation
The implementation of the client is the same as that in the previous article. Since the client does not need to use multi-threading technology, in the following code we only need to call connect every
Connect to the server. If the connection is not successful, continue to wait. If the connection is successful, it will directly enter the internal infinite loop. Different return information will be executed according to different commands in the loop body. The following is the complete code snippet for the client implementation. .5000
milliseconds through an infinite loop.
#include <iostream> #include <WinSock2.h> #include <WS2tcpip.h> #include <string> #pragma comment(lib,"ws2_32.lib") using namespace std; int main(int argc, char* argv[]) {<!-- --> while (1) {<!-- --> WSADATA WSAData; SOCKET sock; struct sockaddr_in ClientAddr; if (WSAStartup(MAKEWORD(2, 0), & amp;WSAData) != SOCKET_ERROR) {<!-- --> ClientAddr.sin_family = AF_INET; ClientAddr.sin_port = htons(8090); ClientAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); sock = socket(AF_INET, SOCK_STREAM, 0); int Ret = connect(sock, (LPSOCKADDR) & amp;ClientAddr, sizeof(ClientAddr)); if (Ret == 0) {<!-- --> while (1) {<!-- --> char buf[4096] = {<!-- --> 0 }; memset(buf, 0, sizeof(buf)); recv(sock, buf, 4096, 0); // Get CPU data if (strcmp(buf, "GetCPU") == 0) {<!-- --> char* cpu_idea = "10%"; int ServerRet = send(sock, cpu_idea, sizeof("10%"), 0); if (ServerRet != 0) {<!-- --> std::cout << "Send CPU packet" << std::endl; } } // Send a message else if (strcmp(buf, "Send") == 0) {<!-- --> char* message = "hello lyshark"; int ServerRet = send(sock, message, sizeof("hello lyshark"), 0); if (ServerRet != 0) {<!-- --> std::cout << "Send message packet" << std::endl; } } // Terminate the client else if (strcmp(buf, "Exit") == 0) {<!-- --> closesocket(sock); WSACleanup(); exit(0); } // Survival detection signal else if (strcmp(buf, "Ping") == 0) {<!-- --> int ServerRet = send(sock, "Pong", 4, 0); if (ServerRet != 0) {<!-- --> std::cout << "Ping survival detection..." << std::endl; } } } } } closesocket(sock); WSACleanup(); Sleep(5000); } return 0; }
Readers can compile and run the above code by themselves. When the server is started, the client comes online. At this time, readers can enter different commands to operate different sockets, as shown in the figure below;