网络编程 Select的使用

网络编程中,tcp编程中,服务器端为了能同时和多个client端通讯,需要分多个线程,每个线程对应一个客户端,而当服务器要处理很多客户端的时候,就不能这样做,这是就利用了IO的多路复用机制。
IO的多路复用:
数据结构中有个set(集合),这个IO多路复用的意思是,我用一个集合来保存所有我要监听的客户端文件描述符(用accept接口接收到的client的文件描述符),用select接口取出有响应的文件描述符进行处理。好处:一个线程可以响应多个客户端。
举例:
为了钓鱼(为客户端提供服务),我放了多根鱼竿(用一个set存了多个文件描述符),有动静的时候提这个杆(select接口)。
以下是可用的简洁代码:
客户端:

//
//  server.c
//  test
//
//  Created by 郭朋振 on 6/18/1399 AP.
//  Copyright © 1399 郭朋振. All rights reserved.
//
#if 1
#include <stdio.h>
#include <stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>

#include<netinet/in.h>
 #include <arpa/inet.h>
#include <unistd.h>
#include <stdbool.h>

#include <pthread.h>
#include <sys/fcntl.h>
#define ERR_EXIT(m)\
    do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

int main(void)
{
    int listenfd;
    fd_set fds;
    struct timeval timeout = {30,0};
    int watch_fd_list[10];//10是我监听的个数
    for(int i=0;i<10;i++)
    {
        watch_fd_list[i] = -1;
    }
    
    if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))<0)
    {
        ERR_EXIT("ERROR");
    }
    if(fcntl(listenfd,F_SETFL,O_NONBLOCK))
    {
        printf("nonblock failed\n");
        exit(1);
    }
    
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
    {
        ERR_EXIT("error");
    }
    if(listen(listenfd, SOMAXCONN)<0)
    {
        ERR_EXIT("listen");
    }
    watch_fd_list[0] = listenfd;//0索引存的fd是固定的服务器的描述符
    int maxfd = listenfd;//这个是select接口所需的参数
    
    FD_ZERO(&fds);//set的clean作用一样,清空
    FD_SET(listenfd,&fds);//插入set的操作
    
    struct sockaddr_in client_sockaddr;
    
    int goon = 1;
    while(goon){
        
        FD_ZERO(&fds);
        FD_SET(listenfd,&fds);
        
        for(int i = 0;i<10;i++)
        {
            if(watch_fd_list[i]!=-1)
            {
                FD_SET(watch_fd_list[i],&fds);
            }
        }
        
        switch (select(maxfd+1,&fds,NULL,NULL,&timeout)) {
            case -1:
                printf("select fail\n");
                goon = 0;
                break;
            case 0:
                printf("select timeout\n");
                FD_ZERO(&fds);
                for(int i=1;i<10;i++)
                {
                    shutdown(watch_fd_list[i], 2);
                    watch_fd_list[i] = -1;
                }
                FD_CLR(listenfd,&fds);
                FD_SET(listenfd,&fds);
                continue;
            default:
                if(FD_ISSET(listenfd,&fds))
                {
                    int socklen = sizeof(client_sockaddr);
                    int new_client_fd = accept(listenfd, (struct sockaddr *)&client_sockaddr, &socklen);
                    if(new_client_fd< 0)
                    {
                        printf("accept error\n");
                        exit(1);
                    }
                    printf("%s,%d\n",inet_ntoa(client_sockaddr.sin_addr),new_client_fd);
                    for(int i=1; i<10;i++)
                    {
                        if(-1 == watch_fd_list[i])
                        {
                            watch_fd_list[i] = new_client_fd;
                            break;
                        }
                    }
                    FD_SET(new_client_fd,&fds);
                    if(maxfd < new_client_fd)
                    {
                        maxfd = new_client_fd;
                    }
                    continue;
                }
                else{
                    for(int i=1;i <=10;i++)
                    {
                        if(-1 == watch_fd_list[i])
                            continue;
                        if(!FD_ISSET(watch_fd_list[i],&fds))
                        {
                            continue;
                        }
                        char buf[128];
                        
                        int len = recv(watch_fd_list[i], buf, 128, 0);
                        if(len<0)
                        {
                            printf("recv error\n");
                            exit(1);
                        }
                        buf[len] = 0;
                        
                        struct sockaddr_in sockaddr;
                        getpeername(watch_fd_list[i], (struct sockaddr *)&sockaddr,  sizeof(sockaddr));
                        printf("%s from %s\n",buf,inet_ntoa(sockaddr.sin_addr));                         
                    }
                }
                break;
        }
    }
    return 0;
}
#endif


客户端:

//
//  client.c
//  test
//
//  Created by 郭朋振 on 6/18/1399 AP.
//  Copyright © 1399 郭朋振. All rights reserved.
//
#if 1
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>

#define ERR_EXIT(m)\
    do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

int main(void)
{
    int sock;
    if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))<0)
    {
        ERR_EXIT("ERROR");
    }
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
    {
        ERR_EXIT("connect");
    }
    char sendbuf[1024] = {0};
    char recvbuf[1024] = {0};
    while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)
    {
        write(sock,sendbuf,strlen(sendbuf));
        //read(sock,recvbuf,sizeof(recvbuf));
        //fputs(recvbuf,stdout);
        memset(sendbuf, 0, sizeof(sendbuf));
        //memset(recvbuf,0,sizeof(recvbuf));
    }
    close(sock);
    return 0;
}
#endif

使用说明:
1 客户端可以多开几个,服务端开一个;
2 每个客户端单独和服务器通讯的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值