[High concurrency network communication architecture] 2. Introduce multi-threading to realize the tcp server of multi-client connection

Table of contents

1. Previous articles

Second, code implementation

key code

full code

running result


1. Previous articles

[High concurrent network communication architecture] 1. The tcp server for single client connection under Linux

Second, code implementation

key code

  • Because accept is blocking and waiting for the client to connect, the code behind accept will be executed only after the client connects successfully, so in order to realize multiple client connections, the first step is to put accept in the master loop.
  • recv is a blocking function, if the code related to recv is not put into the thread, the client will not be able to communicate with the server. ​​​​

full code

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <pthread.h>

#define BUFFER_LENGTH   1024

void* handleClient(void *arg)
{
    int cfd = *((int*)arg);
    while(1)    //一直循环是确保一直和服务端通信,并且recv是阻塞函数
    {
        char buffer[BUFFER_LENGTH] = {0};
        int recvLen = recv(cfd,buffer,BUFFER_LENGTH,0);
        if(recvLen < 0){
            printf("recv error code: %d codeInfo: %s\n",errno,strerror(errno));
            break;
        }else if(recvLen == 0){
            //客户端断开连接
            printf("client fd %d disconnect\n",cfd);
            close(cfd);     //关闭客户端文件描述符,释放资源
            break;
        }else{
            printf("recv fd %d data: %s\n",cfd,buffer);
            send(cfd,buffer,recvLen,0);    //将接收到的数据重新发给客户端
        }
    }

    //退出线程
    pthread_exit(NULL);
    return NULL;
}

int init_server(int port){
    //获取服务端fd,通常为3,前面0,1,2用于指定输入,输出,错误值
    int sfd = socket(AF_INET,SOCK_STREAM,0);

    if(-1 == sfd){
        printf("socket error code: %d codeInfo: %s\n",errno,strerror(errno));
        return -1;
    }

    struct sockaddr_in serverAddr;
    memset(&serverAddr,0,sizeof(struct sockaddr_in));
    serverAddr.sin_family = AF_INET;  //ipv4
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);   //0.0.0.0
    serverAddr.sin_port = htons(port);
    
    //绑定IP和端口号
    if(-1 == bind(sfd,(struct sockaddr*)&serverAddr,sizeof(struct sockaddr_in)))
    {
        printf("bind error code: %d codeInfo: %s\n",errno,strerror(errno));
        return -1;
    }

    //监听该套接字上的连接
    if(-1 == listen(sfd,SOMAXCONN))
    {
        printf("listen error code: %d codeInfo: %s\n",errno,strerror(errno));
        return -1;
    }

    return sfd;
}

int main(int argc,char *argv[]){

    if(argc < 2)return -1;

    int port = atoi(argv[1]);
    int sfd = init_server(port);
    printf("server fd: %d\n",sfd);
    if(sfd == -1)exit(0);

    pthread_t thread_id;
    struct sockaddr_in clientAddr;
    socklen_t clientAddrLen = sizeof(struct sockaddr_in);

    while (1)//每个服务器至少都有一个主循环
    {
        //接受新的连接请求
        int cfd = accept(sfd,(struct sockaddr*)&clientAddr,&clientAddrLen);

        //创建一个新的线程来处理客户端连接
        int flag = pthread_create(&thread_id,NULL,handleClient,(void*)&cfd);
        if(0 == flag){
            printf("client fd: %d ,thread ID: %ld start\n",cfd,thread_id);
        }else{
            perror("pthread_create");
            break;
        }

        //分离线程,让线程自行处理结束
        pthread_detach(thread_id);
    }

    close(sfd);     //关闭服务端文件描述符,释放资源
    printf("server fd %d close\n",sfd);
    
    return 0;
}

running result

  • Remember to add -lpthread to connect to the library when compiling, and run as follows.

There is a problem

  1. When the program is running, if you press it directly without performing the client connection operation,  Ctrl+Z it will send a signal (SIGTSTP) to the process running in the foreground, which will cause the server program to be suspended (Suspended), which will cause the following to be printed when I run it next time Error, indicating that the server socket was not released by the previous run. ( without client connection )
  2. Because the resources are not released, it is very troublesome to manually kill the suspended process every time, as follows.
  3. Finally execute kill -9 394407 to kill the hung process.

Solution

  • The final reason is how the program exits. To make the program exit directly, you can register a signal handler function to catch the  SIGTSTP signal, and execute the exit logic in this function.

problem thinking

  • This implementation method may be fine for tens or hundreds of concurrent connections of clients, but it is obviously unrealistic to use this method to deal with high concurrent connections of thousands of clients? Introduced in the next chapter. . .

Guess you like

Origin blog.csdn.net/weixin_43729127/article/details/131591649