欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于select模型的server

程序员文章站 2022-05-07 22:58:52
...

前面一篇介绍了IO模型。其中重点介绍了IO多路转接中的三种模型,包括了select,poll,epoll三种。下面就是基于select模型编写的服务器与客户机,两者可以进行交互。

 

基于select模型的server

服务器端代码:k

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/select.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>

int fdArray[sizeof(fd_set)*8];
int startup(int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock<0){
        perror("socket");
        exit(2);
    }
    //为了防止服务器主动断开连接,无法连接重启的问题
    int opt = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htonl(INADDR_ANY);
    local.sin_port = htons(port);
    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
    {
        perror("bind");
        exit(3);
    }
    if(listen(sock,5)<0)
    {
        perror("listen");
        exit(4);
    }
    return sock;
}
// ./select_server 8080
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        printf("Usage: %s port\n",argv[0]);
        return 1;
    }
    int listen_sock = startup(atoi(argv[1]));
    //已经创建listen_sock   
    fdArray[0] = listen_sock;//合法的描述符
    int num = sizeof(fdArray)/sizeof(fdArray[0]);
    int i = 1;
    for(i;i<num;i++)
    {
        fdArray[i] = -1;
    }
    while(1)
    {
        fd_set rfds;
        FD_ZERO(&rfds);//清空文件描述符集
        //查找所有文件描述符集最大的
        int max = fdArray[0];//永远不会出错,因为第一个永远是有效的
        for(i = 0;i<num;i++)
        {
            //将fdArray中所有有效的文件描述符设置进了文件描述符集rfds
            if(fdArray[i]>=0)
            {
                //这就是合法的描述符
                FD_SET(fdArray[i],&rfds);
                if(max<fdArray[i]){
                  max = fdArray[i];
                }
            }
        }
        struct timeval timeout = {5,0};
        switch(select(max+1,&rfds,NULL,NULL,&timeout))
        {
            case 0:
                printf("timeout\n");
                break;
            case -1:
                perror("select");
                break;
            default:
                //走到这里,说明至少有一个文件描述符上的事件已经就绪了

                {
                    for(i=0;i<num;i++)
                    {

                        if(fdArray[i]==-1){
                            continue;
                        }
                        if(i==0 && fdArray[i]==listen_sock && FD_ISSET(fdArray[i],&rfds)){
                            //已知哪个文件描述符就绪了并且能够获得新连接

                            struct sockaddr_in client;
                            socklen_t len = sizeof(client);
                            int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
                            if(new_sock<0){
                                perror("accept");
                                continue;
                            }

                            //不敢被阻塞,
                            //添加到数组里
                            for(i=0;i<num;i++)
                            {
                                if(fdArray[i]==-1){
                                    break;
                                }
                            }
                            //找到一个没有被使用的位置
                            if(i<num){
                                fdArray[i] = new_sock;
                            }//如果没有被添加说明数组满了,不能再处理新连接了
                            else{
                                close(new_sock);
                            }

                            //新连接要么已经放到数组里要么被关闭了
                            continue;
                        }
                        //走到这里说明是普通的读事件就绪
                        if(FD_ISSET(fdArray[i],&rfds))
                        {
                            char buf[1024] = {0};
                            ssize_t s = read(fdArray[i],buf,sizeof(buf)-1);
                            if(s==0){
                                printf("client quit");
                                close(fdArray[i]);//关闭当前的文件描述符
                                fdArray[i]=-1;//并且将数组里的值设为-1,不然下次可能会再被读到
                            }
                            if(s<0){
                                perror("read");
                                close(fdArray[i]);
                                fdArray[i]=-1;
                                break;
                            }
                            printf("client: %s\n",buf);
                            write(fdArray[i],buf,strlen(buf));
                        }
                    }
                }
                break;
        }
    }

    return 0;
}

基于select模型的server

 

客户端代码:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        printf("Usage :./select_client [port]");
        return 1;
    }

    //
    struct sockaddr_in client;
    client.sin_port = htons(atoi(argv[1]));
    client.sin_family = AF_INET;
    client.sin_addr.s_addr = htonl(INADDR_ANY);
    //创建套接字
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        return 1;
    }
    //连接服务器
    if(connect(sock,(struct sockaddr*)&client,sizeof(client))<0)
    {
        perror("connect");
        return 1;
    }
    for(;;)
    {
        //输入消息并刷新缓冲区
        printf("client >");
        fflush(stdout);
        //将消息读到buf里
        char buf[1024] = {0};
        read(0,buf,sizeof(buf)-1);
        //将消息写给文件描述符
        if(write(sock,buf,strlen(buf))<0){
            perror("write");
            continue;
        }
        //将服务器返回的消息写到buf里   
        int ret = read(sock,buf,sizeof(buf)-1);
        if(ret<0){
            perror("read");
            continue;
        }
        if(ret==0)
        {
            printf("server close\n");
            break;
        }
        printf("server:%s\n",buf);
    }
    close(sock);
    return 0;
}

这样通过客户端与服务器的交互就可以互通消息了。

相关标签: select