14.10 Socket socket selection communication

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 info 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 5000 milliseconds through an infinite loop. 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. .

#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;

syntaxbug.com © 2021 All Rights Reserved.