Linux高并发服务器解决方案

Linux高并发服务器案例演示

在网络通信中,我们常常的服务器经常会受到成千上万的请求提示,而电脑会根据请求建立相对应的socket链接,但是接触过Linux网络编程的人都知道,Linux链接和客户端建立连接,会经过四步(这里以TCP说明)

第一步,创建socket对应的描述符,这里设置好socket的协议类型以及通信类型(TCP/UDP)

      #include <sys/types.h>/* See NOTES */

      #include<sys/socket.h>

      int socket(int domain, int type, int protocol);

      具体的使用方法可以使用man手册查看socket函数

第二步,绑定端口,以及相应的ip地址(服务器不用设置)

       #include <sys/types.h> /* See NOTES */

       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

       具体的使用方法可以使用man手册查看bind函数

        

第三步,监听socket,并且设置最大监听数

    #include <sys/types.h> /* SeeNOTES */

     #include<sys/socket.h>

      int listen(int sockfd, int backlog);

      具体的使用方法可以使用man手册查看listen函数

第四步,接受客户端的连接

    #include <sys/types.h>      /* See NOTES */

     #include<sys/socket.h>

     int accept(int sockfd, struct sockaddr *addr, socklen_t*addrlen);

     具体的使用方法可以使用man手册查看accept函数

注意:

         由于上述四步socket建立链接中,accept是阻塞的,意味着如果有很多人同时发出socket请求的时候,服务器只会接收到一少部分的连接,其他都在阻塞队列排队,甚至丢失!甚至建立链接后一旦涉及服务器读写操作时候,涉及到读写,也是会遇到阻塞的!(假设每个用户连接服务器需要0.1秒,那么10000人连接的话则需要1000秒)这样会让服务器浪费大量时间在阻塞过程,所以我们要做的,就是尽可能的让系统发挥出他的性能,让更多的人能够连接系统,却不用花那么长时间。

具体实现方法:

         使用epoll分路技术,让accept不陷入阻塞,当那个客户端发出请求,则处理发出请求的客户端信息,这时候因为客户端发出请求,则必定有读写操作,所以读写操作不用去掉阻塞。

服务器代码:


//模拟接收每个客户端发来的请求后并往dbg.txt文件写入一个字节

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/stat.h>

#include <sys/epoll.h>

#include <arpa/inet.h>

#include <sys/wait.h>

#include <stdlib.h>

#include <fcntl.h>

#include <string.h>

#include <errno.h>

#include <unistd.h>

#include <stdio.h>

#include <signal.h>

void sig_handle(int sig)

{

    printf("recv signal :%d\n",sig);

}

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

{

    signal(SIGPIPE,sig_handle);

    if (argc<2)

    {

        printf("usage:%s + [count]\n",argv[0]);

        return 0;

    }

    unlink("dbg.txt");

    int dbg = open("dbg.txt",O_CREAT|O_APPEND|O_RDWR,0666);

    int count = atoi(argv[1]);

    int fd = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in addr;

    memset(&addr,0,sizeof(addr));

    addr.sin_family = AF_INET;

    addr.sin_port = htons(9988);

    int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));

    if (ret ==-1)

    {

        perror("bind");

        return 0;

    }

    listen(fd,250);

    int is_child_process = 0;//判断在哪个进程中,父进程0,子进程1

    for (int i = 0 ; i < count ; i++)

    {

        pid_t pid = fork();

        if (pid==0)

        {

            is_child_process = 1;

            break;

        }

    }

 

    struct epoll_event ev;

    ev.events = EPOLLIN|EPOLLET;

    ev.data.fd = fd;

    int epfd = epoll_create(1024);//建立epfd的描述符

    int flags = fcntl(fd,F_GETFL);

    flags |= O_NONBLOCK;

    fcntl(fd,F_SETFL,flags);

 

    epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);

    while (1)

    {

        struct epoll_event evs[10];

        int process_count = epoll_wait(epfd,evs,10,5000);

        if (process_count == 0) continue;//如果监听的进程都没有事件产生,则再次进入循环,继续监听

        for (int i = 0 ; i < process_count ;i++)

        {

            if (evs[i].data.fd == fd)

            {

                //当进程中的socket描述符是server的socket本身时候,则accept否则就直接操作

                int ret = accept(evs[i].data.fd,NULL,NULL);

                if (ret == -1)

                {

                    printf("errno:%s",strerror(errno));

                    //其他错误,直接exit

                    break;

                }

                ev.data.fd = ret;

                epoll_ctl(epfd,EPOLL_CTL_ADD,ret,&ev);

            }

            else

            {

                //read or write

                char buf[1024];

 

                int ret = read(evs[i].data.fd,buf,sizeof(buf));

                if (ret == -1)

                {

                    perror("read");

                    if (errno == EINTR)

                        break;

                    exit(0);

                }

                else if (ret == 0)

                {

                    //normal exit

                    close(evs[i].data.fd);

                    break;

                }

                //printf("recv data %s from pid:%d\n",buf,getpid());

                write(dbg,"1",1);

            }

        }

    }

    if (!is_child_process)

    {

        for (int i = 0 ; i < count; i ++)

        {

            wait(NULL);//等待所有的子进程退出为止

        }

    }

    return 0;

}

测试客户端向服务器发请求代码:


//模拟有20000个客户端同时向服务器发请求

#include <sys/types.h>

#include <sys/socket.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/wait.h>

#include <string.h>

#define PROCESS_COUNT 20000

void func(int argc,char * argv[])

{

    int fd =socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in addr;

    addr.sin_family = AF_INET;

    addr.sin_port = htons(9988);

    addr.sin_addr.s_addr =inet_addr("127.0.0.1");

    connect(fd,(structsockaddr*)&addr,sizeof(addr));

    if (argc==2)

       write(fd,argv[1],strlen(argv[1]));

    else

        write(fd,"1",1);

    char buf[1024];

    //recv(fd,buf,sizeof(buf),0);

}

 

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

{

    for (int i = 0 ; i <PROCESS_COUNT; i++)

    {

        pid_t pid = fork();

        if (pid == 0)

        {

            func(argc,argv);

            return 0;

        }

    }

    for (int i = 0 ; i <PROCESS_COUNT; i++)

    {

        wait(NULL);

    }

 

    return 0;

}

结果分析:不同的电脑测试结果略有不同,我的客户端定义有20000个同时向服务器发请求,结果处理的请求大概有15500左右,不同的电脑最高并发数略有不同。成功的解决了多用户同时向一个服务器发请求的问题

猜你喜欢

转载自blog.csdn.net/jgm20475/article/details/81203911