并发服务器

  1. 多进程方式

  1. 实现思路

每当accept一个连接,就会创建一个子进程来处理该连接并进行通信。

  1. 服务器

int main()
{
    int sock = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons( 8899 );
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    if ( -1 == bind(sock,(struct sockaddr *)&ser,sizeof(ser) ) ){
        perror("bind error\n");
    }

    listen(sock,10);      
    printf("wait listen\n");
    
    int fd;
    while(1){
        struct sockaddr_in cli;
        int len = sizeof( cli );
        fd = accept(sock,(struct sockaddr *)&cli,&len);
        printf("new client connected\n");
        pid_t pid = fork();
        if(pid == 0)
        {
            break;
        }
    }

    //read/write
    while(1){
        char buf[20] = {0};
        read(fd, buf , 20);
        printf(" [%d] send %s \n",fd,buf);
        write(fd,"send successed",20);
    }
    close(sock);
}
  1. 客户端

int main()
{
    int sock = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons( 8899 );
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    /*    int connect (int sockfd , const struct sockaddr *addr,socklen_t len);*/    
   if ( -1 == bind(sock,(struct sockaddr *)&ser,sizeof(ser) ) ){
        perror("bind error\n");
    }

    //write/read 循环写
    while(1)
    {
        sleep(1);
        write( sock , "HAVE A GOOD DAY!" ,20);
        char buf[20] = {0};
        read( sock , buf , 20); 
        printf("%s \n",buf);
    }
    
    close(sock);
}
  1. 多线程方式

  1. 实现思路

为每个新加入的客户端创建一个线程,在线程函数内执行自己的输入输出操作。

  1. 服务端

void * routine(void * arg)
{
    int fd  =(int)arg; 
    while(1){
        sleep(1);
        write(fd,"hello cli",9);
        char buf[20] = {0};
        read(fd,buf,sizeof(buf));
        printf("fd = %d , buf = %s \n",fd,buf);
    }
}
int main()
{
    int sock = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons( 8899 );
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    int ret = bind(sock,(struct sockaddr *)&ser,sizeof(ser));
    if ( ret < 0){
        perror("bind error\n");
    }

    listen(sock,10);
    printf("wait listen\n");
    
    int fd;
    while(1){
        struct sockaddr_in cli;
        int len = sizeof( cli );
        int fd = accept(sock,(struct sockaddr *)&cli,&len);
        printf("new client connected\n");
        
        pthread_t tid;
        pthread_create( &tid ,NULL,routine,(void *)fd);
        //不能使用join,因为join会将主函数阻塞
        pthread_detach( tid );
    }
    close(sock);
}
  1. 客户端

int main()
{
    int sock = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons( 8899 );
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    /*    int connect (int sockfd , const struct sockaddr *addr,socklen_t len);*/    
    int ret = connect( sock,(struct sockaddr *)&ser,sizeof(ser));
    if(ret < 0){
        perror("connect error\n");
        exit( -1 );
    }
    //read/write
    while(1)
    {
        char buf[10] = {0};
        
        read( sock , buf ,sizeof(buf));
        printf("receive:%s \n",buf); 
        
        write(sock,"hello cli",9);
    }
    
    close(sock);
}
  1. select方式

  1. 实现思路

select系统调用同时监视用多个文件句柄,程序会阻塞在select函数这里,当有一个或多个句柄发生变化时就会执行相应的操作,最大只能监视1024个客户端。

  1. select函数

  1. int select(   int nfds,fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);

int nfds                    描述符内最大值加1

fd_set *readfds            读            不需要时传NULL

fd_set *writefds           写            不需要时传NULL

fd_set *exceptfds         异常         不需要时传NULL

struct timeval *timeout    等待时间 : 0为不等

>0等待几秒或几毫秒

NULL为死等

因为readfds、writefds、exceptfds不仅是输入也是输出,所以在select时需要注意当运行过后,值是否会改变,是否需要重新赋值

  1. 返回值

0 :没有就位,且超时

-1:有错

 >0:有人准备好

  1. fd_set 是需要监测的fd的集合,包含很多个fd
  1. fd_set操作函数:

void FD_CLR (int fd,fd_set *set);    从集合里面移出

int FD_ISSET (int fd, fd_set *set);       是否在集合中

void FD_SET (int fd, fd_set *set);       放入集合

void FD_ZERO (fd_set *set);           清空集合

  1. 服务端

int main()
{
    int sock = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons( 8899 );
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    int ret = bind(sock,(struct sockaddr *)&ser,sizeof(ser));
    if ( ret < 0){
        perror("bind error\n");
    }

    listen(sock,5);        //int listen(int sockfd,int backlog最大长度);
    printf("wait listen\n");

    int fd[5];            //存放连接到服务器的客户端的描述符
    int idx = -1;        //存放新连接的下标
    fd_set ofds;        //存放需要监测的描述符
    FD_SET( sock,&ofds);        //将客户端也放入ofds中监测,监测是否有人请求连接
    int max_fd = sock;        //存放最大描述符
    while(1){
        fd_set rfds = ofds;
        int ret = select(max_fd+1,&rfds,NULL,NULL,NULL);
        if ( ret == -1 ){
            perror("select error\n");
        }else {
            if (FD_ISSET(sock,&rfds)){        //有人请求连接
                idx++;
                struct sockaddr_in cli;    
                int len = sizeof( cli );    
                fd[idx] = accept(sock,(struct sockaddr *)&cli,&len);    //接受连接
                printf("connect success\n");
                FD_SET( fd[idx],&ofds);            //将新连接存入句柄中监测
                max_fd = (fd[idx] > max_fd)?fd[idx]:max_fd;    //检测最大描述符是否需要改变 
            }else{
                for(int i = 0; i <= idx;i++)            //检测每个已存入的连接是否发生变化
                {
                    char buf[20] = {0};
                    if( FD_ISSET(fd[i],&rfds)){
                        read( fd[i] , buf ,sizeof(buf));
                        printf("fd[ %d ] receive %s", fd[i] , buf );
                        write(fd[i],"send successed",20);
                    }
                }
            }    
        }
        
    }
    close(sock);
}
  1. 客户端

int main()
{
    int sock = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons( 8899 );
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    int ret = connect( sock,(struct sockaddr *)&ser,sizeof(ser));
    if(ret < 0){
        perror("connect error\n");
        exit( -1 );
    }
    
    fd_set ofds;
    FD_SET(0,&ofds);        //键盘输入
    FD_SET(sock,&ofds);     //传输数据
    while(1){    
        //因为select中rfds不仅是输入也是输出,当执行完一次后rfdsd的值会改变,所以需要不断将初始值ofds赋给rfds   
        fd_set rfds = ofds;    
        
        int ret = select( sock+1,&rfds,NULL,NULL,NULL);
        if ( ret == -1 ){
            perror("select error\n");
        }else{
            char buf[20] = {0};
            if( FD_ISSET(sock,&rfds)){            //检测到有数据传入
                read( sock , buf ,sizeof(buf));
                printf("receive:%s \n",buf);        
            }
            if( FD_ISSET(0,&rfds)){                //检测到有键盘输入
                int ret  = read( 0 , buf ,sizeof(buf));    //标准输入
                write(sock,buf,ret);
            }
        }
    }
    
    close(sock);
}
  1. poll方式

  1. 实现思路

和select的本质是一样的,但没有最大文件描述符限制

  1. poll函数

  1. int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  1. struct pollfd *fds

struct pollfd{

int fd;            //文件描述符

short events;     //监测事件

short revents;     //事件监测结果

};

events、revents:

POLLIN:普通或带外优先数据可读

POLLHUP:程序挂起

  1. nfds_t nfds                //需要监测的文件数量 
  1. int timeout                //毫秒级等待

0:立即返回

>0:等待xx毫秒

-1:阻塞等

  1. 服务端

#define SA struct sockaddr

int main()
{
    int sfd = socket(AF_INET,SOCK_STREAM,0);
    
    struct sockaddr_in s;
    s.sin_family = AF_INET;
    s.sin_port = htons(8899);
    s.sin_addr.s_addr = inet_addr("127.0.0.1");
    int len = sizeof(s);
    
    if( -1 == bind(sfd,(SA *)&s,len)){
        perror("bind error\n");
        return -1;
    }
    listen(sfd,10);
    printf("wait connect\n");

    struct pollfd p[10];
    p[0].fd = sfd;
    p[0].events = POLLIN;
    int num = 1;

    while(1){
        int ret = poll(p,10,-1);
        if(ret == 0){
            printf("timeout\n");
        }
        else if(ret == -1){
            perror("select error\n");
        }else{
            if(p[0].revents & POLLIN){
                printf("0x%x \n",p[0].revents);
                int cfd = accept(sfd,(SA *)&s,&len);
                p[num].fd = cfd;
                p[num].events = POLLIN | POLLHUP;
                printf("new client connected total %d\n",num);
                num++;
            }
            int i = 1;
            for( i = 1; i<num;i++){
                if(p[i].revents & POLLHUP){        //客户端关闭,关闭连接
                    close(p[i].fd);
                    p[i].fd = p[num].fd;
                    num--;
                    printf("a client left, rest %d connect \n",num-1);
                }else if(p[i].revents & POLLIN){    //客户端传来数据
                    char buf[100] = {0};
                    read(p[i].fd,buf,sizeof(buf));
                    printf("fd [%d] receive: %s \n",p[i].fd,buf);
                    write(p[i].fd,"write successed",20);
                }
            }
        }
    }
    close(sfd);
    return 0;
}
  1. 客户端

#define SA struct sockaddr

int main()
{
    int sfd = socket(AF_INET,SOCK_STREAM,0);
    
    struct sockaddr_in s;
    s.sin_family = AF_INET;
    s.sin_port = htons(8899);
    s.sin_addr.s_addr = inet_addr("127.0.0.1");
    int len = sizeof(s);
    
    if( -1 == connect(sfd,(SA *)&s,len)){
        perror("connect error\n");
        return -1;
    }
    printf("connect successed\n");

    struct pollfd p[2];
    p[0].fd = sfd;
    p[0].events = POLLIN | POLLHUP;
    p[1].fd = 0;
    p[1].events = POLLIN | POLLHUP;
    
    while(1){
        int ret = poll(p,2,-1);
        if(ret == 0){
            printf("timeout\n");
        }
        else if(ret == -1){
            perror("select error\n");
        }else{
            char buf[100] = {0}; 
            if(p[1].revents & POLLIN){
                read(0,buf,100);
                write(sfd,buf,strlen(buf));
            }
            if(p[0].revents & POLLHUP){
                printf("server shut down \n");
                close( p[0].fd );
                return 0;
            }else if(p[0].revents & POLLIN){
                memset(buf,100,0);
                read(sfd,buf,sizeof(buf));
                printf("%s \n",buf);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值