网络编程实战学习笔记(十三)-select多路复用

网络编程实战学习笔记(十三)-select多路复用

Day20

基础知识

1.select 方法是多个 UNIX 平台支持的非常常见的 I/O 多路复用技术,它通过描述符集合来表示检测的 I/O 对象,通过三个不同的描述符集合来描述 I/O 事件 :可读、可写和异常。但是 select 有一个缺点,那就是所支持的文件描述符的个数是有限的。在 Linux 系统中,select 的默认最大值为 1024。

demo

client
//
// Created by root on 11/9/19.
//
//#include"lib/common.h"

#include<iostream>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <unistd.h>
#include "message_objecte.h"
#include    <signal.h>

#define    SERV_PORT      43211
#define    MAXLINE        4096
#define    UNIXSTR_PATH   "/var/lib/unixstream1.sock"
#define    LISTENQ        1024
#define    BUFFER_SIZE    4096
#define MAXLINE 4096

int tcp_client(const std::string &ip,int16_t port){
    //1.创建
    int socket_fd=socket(AF_INET,SOCK_STREAM,0);

    //2.初始化
    struct sockaddr_in clien_addr;
    bzero(&clien_addr,sizeof(clien_addr));
    clien_addr.sin_port=htons(port);
    clien_addr.sin_family=AF_INET;
    inet_pton(AF_INET,ip.c_str(), &clien_addr.sin_addr);

    socklen_t len= sizeof(clien_addr);

    //3.连接
    int rc=connect(socket_fd,(struct sockaddr*)&clien_addr,len);
    if(rc<0){
        error(1,errno,"connect error");
        return -1;
    }
    return socket_fd;

}

int main()
{
    int socket_fd=tcp_client("127.0.0.1",SERV_PORT);

    char recv_line[MAXLINE]={0},send_line[MAXLINE]={0};
    int n;

    fd_set readmask;
    fd_set allreads;
    FD_ZERO(&allreads);//初始化
    FD_SET(0,&allreads);//设置标准IO感知
    FD_SET(socket_fd,&allreads);//设置套接字感知

    for(;;){
        //每次检测重新设置套接字描述符
        readmask=allreads;
        int rc=select(socket_fd+1,&readmask,NULL,
                NULL,NULL);
        if(rc<=0){
            error(1,errno,"select failed");
        }
        if(FD_ISSET(socket_fd,&readmask)){
            n=read(socket_fd,recv_line,sizeof(recv_line));
            if(n<0){
                error(1,errno,"read failed");
            }else if(n==0){
                error(1,0,"server closed");
            }
            recv_line[n]=0;
            fputs(recv_line,stdout);
            fputs("\n",stdout);
        }
        if(FD_ISSET(STDIN_FILENO,&readmask)){
            if(fgets(send_line,MAXLINE,stdin)!=NULL){
                int i=strlen(send_line);
                if(send_line[i-1]=='\n'){
                    send_line[i-1]=0;
                }
                printf("now sending %s\n",send_line);

                //不能用size_t 因为size_t是unsigned int不会小于0
                ssize_t rt=write(socket_fd,send_line,strlen(send_line));
                if(rt<0){
                    error(1,errno,"write failed");
                }
                printf("send bytes:%zu\n",rt);

            }
        }
    }
}
server
//
// Created by root on 11/9/19.
//
//#include"lib/common.h"

//homework_server  只支持一次服务一个客户端,这里用select多路复用的方式实现下server
#include<iostream>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <unistd.h>
#include "message_objecte.h"
#include    <signal.h>

#define    SERV_PORT      43211
#define    MAXLINE        4096
#define    UNIXSTR_PATH   "/var/lib/unixstream1.sock"
#define    LISTENQ        1024
#define    BUFFER_SIZE    4096
#define MAXLINE 4096
static int count;

const int fds_nums=64;

static void sig_int(int signo){
    printf("\nreceived %d datagrams\n",count);
    exit(0);
}


int main()
{
    int fds_array[64];//fds_array[64]={-1} 只把第一元素初始话为了-1;
    memset(fds_array,-1, sizeof(fds_array));

    //1.create
    int listen_fd=socket(AF_INET,SOCK_STREAM,0);

    //2.init
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_port=htons(SERV_PORT);
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=INADDR_ANY;

    //3.set reuse(SOL_SOCKET 套接字级别)
    int on=1;
    setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

    //4.bind
    socklen_t len= sizeof(server_addr);
    int rt1=bind(listen_fd,(struct sockaddr*)&server_addr,len);
    if(rt1<0){
        error(1,errno,"bind failed");
    }

    //5.listen
    int rt2=listen(listen_fd,1024);
    if(rt2<0){
        error(1,errno,"listen failed");
    }


    fd_set allreads;
    fd_set readmasks;
    FD_ZERO(&allreads);
    FD_SET(0,&allreads);
    FD_SET(listen_fd,&allreads);
    int max_fd=-1;//最大文件描述符的下标

    int i=0;
    fds_array[i]=listen_fd;

    struct timeval timeout={5,0};//5s 0毫秒
    int done=0;
    while(!done)
    {
        max_fd = -1;
        int i=0;
        for(;i<fds_nums;++i){
            if(fds_array[i]>0){
                //设置fds_array[i]的位
                FD_SET(fds_array[i],&allreads);
                //计算最大fd
                max_fd=max_fd>fds_array[i]?max_fd:fds_array[i];
            }
        }
        readmasks=allreads;
        //最后timeout为空表示阻塞等待
        int ret=select(max_fd+1,&readmasks,NULL,NULL,NULL);
        switch(ret){
            case 0:
                //超时而且没有任何描述符就绪
                printf("timeout...\n");
                break;
            case -1:
                perror("select error");
                exit(1);
            default:
            {
                i=0;
                for(;i<fds_nums;++i){
                    if(fds_array[i]==listen_fd &&
                    FD_ISSET(listen_fd,&readmasks)){//有连接进来
                        //是监听套接字,且就绪
                        struct sockaddr_in client_addr;
                        socklen_t len= sizeof(client_addr);
                        int connfd=accept(listen_fd,(struct sockaddr*)&client_addr,&len);
                        if(connfd<0){
                            error(1,errno,"connect error");
                        }
                        printf("get a new client: %s:%d\n",inet_ntoa(client_addr.sin_addr),
                                ntohs(client_addr.sin_port));

                        //将客户端加描述符集合
                        int j=0;
                        for(;j<fds_nums;++j){
                            if(fds_array[j]==-1){
                                fds_array[j]=connfd;
                                break;
                            }
                        }
                        if(j==fds_nums){
                            //空间已用完
                            close(connfd);
                        }
                        }
                        else{//有事件感知
                            if(fds_array[i]>0&&
                            FD_ISSET(fds_array[i],&readmasks)){
                                char readbuf[1024]={0};
                                char writebuf[1024]={0};
                                //从fd_array[i]这个客户端读取数据
                                ssize_t rt=read(fds_array[i],readbuf,sizeof(readbuf)-1);
                                if(rt<0){
                                    error(1,errno,"read error");
                                }else if(rt==0){
                                    printf("client close");
                                    close(fds_array[i]);
                                    fds_array[i]=-1;
                                }else{
                                    readbuf[rt]=0;
                                    printf("recvice:%s\n",readbuf);
                                    sprintf(writebuf,"Hi,%s\n",readbuf);
                                    printf("send %s\n",writebuf);
                                    fflush(stdout);//刷新输出缓冲,否则输出信息不会打出来
                                    send(fds_array[i],writebuf,strlen(writebuf),0);

                                }
                            }
                        }
                    }

                    }
            break;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值