yaohacker0225 2022-04-24 10:13 采纳率: 100%
浏览 56
已结题

C语言 后台运行 TCP Server 程序 无法接收超过1000的客户端连接?

问题描述

我遇到了一个奇怪的问题,当我手动运行一个TCP程序(./tcpServDemo)时,TCP服务器程序可以接收5000多个客户端连接,但当我在后台运行tcpServDemo(systemctl start tcpServDemo.service)时,它只能接收900多个客户端连接,

调试时,我发现TCP recv-Q队列已满。我修改了TCP参数(net.core.somaxconn=65500 net.ipv4.TCP_max_syn_backlog=409600),但它不起作用

我已经调试了好几天了。我真的不知道问题出在哪里?谁能看一看,谢谢大家!

OS: Centos7.9

TCP client.c 源码:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>

//#define SERVPORT 8088
 
int ServPort;
char ServerIP[32];

pthread_mutex_t lock;
int count;

void *cTcpConn(void *arg) {
    usleep(2000);
    int sockfd,sendbytes;
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(struct sockaddr_in));

    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
        printf("socket create failed.");
        return NULL;
    }


    //填充服务器地址信息
    serv_addr.sin_family    = AF_INET; //网络层的IP协议: IPV4
    serv_addr.sin_port      = htons(ServPort); //传输层的端口号
    serv_addr.sin_addr.s_addr   = inet_addr(ServerIP); //网络层的IP地址: 实际的服务器IP地址

    if((connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))) < 0) {
        printf("connect failed!\n");
        close(sockfd);
        return NULL;
    } else {
        pthread_mutex_lock(&lock);
        count++;
        pthread_mutex_unlock(&lock);
        printf("connect successful! count:%d\n", count);
    }


#if 0
    //3.发送消息给服务器端
    if((sendbytes = send(sockfd,"hello",5,0)) < 0) {
        perror("send");
        exit(1);
    }
#endif

    while(1) {
        sleep(10);
    }

    //4.关闭
    close(sockfd);

    return NULL;
}

int main(int argc, char **argv) {
    if (argc != 3) {
        printf("Usage: %s ServerIP ServerPort\n", argv[0]);
        return 0;
    }

    strncpy(ServerIP, argv[1], sizeof(ServerIP) - 1);
    ServPort = atoi(argv[2]);
    

    int i;
    pthread_t pid;

    for (i = 0; i < 8000; i++) {
        usleep(10000);
        if(0 != pthread_create(&pid, NULL, cTcpConn, NULL)) {
            printf("thread create failed.\n");
        }
    }

    while (1) {
        sleep(10);
    }
    return 0;
}

TCP Server Demo.c 如下:

tcpServDemo.c:

#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>


int main() {
    const int EVENTS_SIZE = 4096;
    char buff[1024];
    int eNum;

    int socketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    struct sockaddr_in sockAddr;
    sockAddr.sin_port = htons(8088);
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_addr.s_addr = htons(INADDR_ANY);

    if (bind(socketFd, (struct sockaddr *) &sockAddr, sizeof(sockAddr)) == -1) {
        return -1;
    }

    if (listen(socketFd, 10) == -1) {
        return -1;
    }

    int eFd = epoll_create(1);

    struct epoll_event epev;
    epev.events = EPOLLIN;
    epev.data.fd = socketFd;
    epoll_ctl(eFd, EPOLL_CTL_ADD, socketFd, &epev);
    
    int i;
    int count = 0;
    struct epoll_event events[EVENTS_SIZE];

    while (1) {
        eNum = epoll_wait(eFd, events, EVENTS_SIZE, -1);

        if (eNum == -1) {
            return -1;
        }
        for (i = 0; i < eNum; i++) {
            if (events[i].data.fd == socketFd) {
                if (events[i].events & EPOLLIN) {
                    struct sockaddr_in cli_addr;
                    socklen_t length = sizeof(cli_addr);
                    int fd = accept(socketFd, (struct sockaddr *) &cli_addr, &length);
                    if (fd > 0) {
                        count++;
                        epev.events = EPOLLIN | EPOLLET;
                        epev.data.fd = fd;

                        int flags = fcntl(fd, F_GETFL, 0);
                        if (flags < 0) {
                            continue;
                        }
                        if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
                            continue;
                        }

                        epoll_ctl(eFd, EPOLL_CTL_ADD, fd, &epev);
                        printf("client on line fd:%d-count:%d\n", fd, count);
                    }  else {
                        printf("accept failed.\n, fd:%d-errno:%d-strerror:%s\n", fd, errno, strerror(errno));
                    }
                }
            } else {
                if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) {
                    epoll_ctl(eFd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
                    close(events[i].data.fd);
                }
            }
        }
    }
}

/usr/lib/systemd/system/tcpServDemo.service:

[Unit]
Description= application service monitor daemon
After=network.target                                                                                                                                         

[Service]
User=root
Type=forking
ExecStart=/var/workstation/testcode/C-Code/tcpServDemo
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=1s

[Install]
WantedBy=multi-user.target graphic.target
  • 写回答

2条回答 默认 最新

  • yaohacker0225 2022-04-26 08:56
    关注

    经过这几天的测试,我把基本的结果以及我自己的解决方法写出来,欢迎大家讨论。

    通过 systemctl start xx.service 启动的服务,其实是运用了,Systemd 来管理系统和服务,其所占有的系统资源是通过 cgroup 来获取的。

    cgroup是Linux内核的一个功能,用来限制、控制,分离一个进程组的资源(如CPU、内存、磁盘输入输出等),
    当 systemctl 启动服务时,系统会分配一些预设的资源(CPU,内存等),这些资源可以通过systemctl 命令来控制,
    比如设置tcpServ.service 启动服务的内存为5G:

    systemctl set-property tcpServ.service MemoryLimit=5G
    

    回到本文的问题,通过 systemctl start tcpServ.service 启动 tcpServDemo 服务后,查看所占有的描述符资源:

    # systemctl show tcpServ.service | grep LimitNOFILE
    LimitNOFILE=1024
    

    显然看到 可以打开的最大fd 个数为 1024,这样就没有接收超过 1024 的 TCP 客户端连接了,我们需要调整这个值。通用的方法是调用 setrlimit 函数设置打开的最大文件描述符,示例代码如下:

    struct rlimit r;
    
    r.rlim_cur=10240;
    r.rlim_max=10240;
    
    if (setrlimit(RLIMIT_NOFILE,&r)<0){
    fprintf(stderr,"setrlimit error\n");
    }
    

    然后启动程序后, 再次查看 FD 个数。

    # systemctl show tcpServ.service | grep LimitNOFILE
    LimitNOFILE=10240
    

    以上是一个基本的解决方法,当然还有其他的方法,比如在 tcpServ.service 这个启动文件中,也可以设置需要的资源的参数 LimitNOFILE=10240。

    欢迎大家讨论!

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 4月26日
  • 已采纳回答 4月26日
  • 创建了问题 4月24日

悬赏问题

  • ¥15 BP神经网络控制倒立摆
  • ¥20 要这个数学建模编程的代码 并且能完整允许出来结果 完整的过程和数据的结果
  • ¥15 html5+css和javascript有人可以帮吗?图片要怎么插入代码里面啊
  • ¥30 Unity接入微信SDK 无法开启摄像头
  • ¥20 有偿 写代码 要用特定的软件anaconda 里的jvpyter 用python3写
  • ¥20 cad图纸,chx-3六轴码垛机器人
  • ¥15 移动摄像头专网需要解vlan
  • ¥20 access多表提取相同字段数据并合并
  • ¥20 基于MSP430f5529的MPU6050驱动,求出欧拉角
  • ¥20 Java-Oj-桌布的计算