select模型

一、使用select模型,解决基本C/S模型中,accept()、recv()、send()阻塞的问题

二、select模型与C/S模型的不同点

  • C/S模型中accept()会阻塞一直傻等socket来链接
  • select模型只解决accept()傻等的问题,不解决recv(),send()执行阻塞问题 
其实select模型解决了 实现多个客户端链接,与多个客户端分别通信
两个模型都存在recv(),send()执行阻塞问题由于服务器端,客户端不需要(客户端只有一个socket,可以通过加线程解决同时recv和send)

server.cpp 

  • #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <termios.h>
    #include <signal.h>
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <ctype.h>
    #define SET_PORT 8000
    int main(int argc, char *argv[])
    {
        int sockfd,connfd;//监听套接字 和连接套接字
        struct sockaddr_in serveraddr;
        int i;//主要用于各种for循环的i
        int on = 1;//只有下方设置可重复性使用的端口用到
         //1.创建监听套接字
        sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 
        setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) ;//设置为可重复使用的端口
        
        //2.bind(通信需要套接字 我把我家的地址 门牌号绑上去 ip和端口)
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port = htons(SET_PORT);
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
        bind(sockfd, (struct sockaddr *)&serveraddr,sizeof(serveraddr));
        //3.监听 和服务器连接的总和
        listen(sockfd,128) ;  
        int maxfd = sockfd;
        //初始化两个集合 一个数组
        int client[FD_SETSIZE];//数组用于存放客服端fd 循环查询使用 FD_SETSIZE 是一个宏 默认已经最大1024
        fd_set rset;//放在select中 集合中都是有动静的fd 一般就一个
        fd_set allset;//只做添加fd
        FD_ZERO(&rset);//清空的动作
        FD_ZERO(&allset);//清空的动作
        
        //先将监听套接字放入
        FD_SET(sockfd,&allset);
        int nready;
        
        //初始化数组都为-1 应为标识符从0开始
        for(i = 0; i < FD_SETSIZE; i++)
            client[i] = -1;
        while(1)
        {
        	//非常关键的一步
            rset = allset;//保证每次循环 select能监听所有的文件描述符 因为rset只会剩下有动静的
            nready = select(maxfd+1,&rset,NULL,NULL,NULL);//参数一
            
            //新客户端
            if(FD_ISSET(sockfd,&rset))
            {
                struct sockaddr_in clientaddr;  
                memset(&clientaddr,0,sizeof(clientaddr));
                socklen_t len = sizeof(clientaddr);
                
                connfd = accept(sockfd, (struct sockaddr*)&clientaddr,&len);
                
                char ipstr[128];//打印用到
                printf("client ip%s ,port %d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),
                       ntohs(clientaddr.sin_port));
                
                //做的事情一:文件df放入数组
                for(i = 0; i < FD_SETSIZE; i++)
                {	if(client[i] < 0)
                    {
                        client[i] = connfd;
                        break;//一定要记得及时跳出 易错点
                    }
                }
                //做的事情二:放入集合
                FD_SET(connfd,&allset);//放入集合
                //防止超出范围//select的第一个参数必须是监视的文件描述符+1 如果不断有新的客户连接 最大值不断变大 超出就赋值
                if(connfd > maxfd)
                    maxfd = connfd;
                
                //下方表示 如果同一时刻只有 一个动静 就无需进入下方的else判断处理 如果不止一个 nready-1 再进入下方判断
                if(--nready <= 0)  
                	continue;
                
            }
            else
            {
                //已连接FD产生可读事件
                for(i = 0; i < FD_SETSIZE; i++)//FD_SEISIZE 是宏 1024  //循环从数组取出元素比对
                {		
                    
                    if(FD_ISSET(client[i],&rset))
                    {
                        connfd = client[i];
                        char buf[1024] = {0};
                        int nread ;
                        nread = read(connfd, buf, sizeof(buf));
                        if(nread == 0)//表示客服端断开链接
                        {
                            //四步处理 打印说明 从集合中删除  从数组中删除 关闭客服端
                            printf("client is close..\n");
                            FD_CLR(connfd, &allset);
                            client[i] = -1;
                            close(connfd);
                        }
                        else//正常读到处理
                        {
                            for(int j=0;j<strlen(buf);j++)
                            {
                                buf[j]=toupper(buf[j]); 
                            }
                            write(connfd,buf,nread);
                            memset(buf,0,1024); 
                        }
                       //下方表示如果同意时刻如果可读事件只有一个 无需再将数组元素进行循环比对 直接跳出
                        //不必让循环走完 浪费时间
                        if(--nready <= 0)
                            break;
                    }
                }
            }
        }    
        return 0;
    }
    
    

    client.cpp

  • #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    int main(int argc,char* argv[])
    {
    	int sockfd;
    	struct sockaddr_in sfdaddr; //指定要连接服务器的结构体 ip 端口
    	int len;
    	//char buf[1024];
    	char wbuf[1024];
    	char rbuf[1024];
    	//1.socket  通信用套接字,创建一个socket
    	sockfd = socket(AF_INET,SOCK_STREAM,0);         
    	
    	char ipstr[] = "127.0.0.1";           //本机测试ip
    	//初始化地址
    	bzero(&sfdaddr,sizeof(sfdaddr));
    	
    	sfdaddr.sin_family = AF_INET;
    	sfdaddr.sin_port = htons(8000);
    	
    	inet_pton(AF_INET,ipstr,&sfdaddr.sin_addr.s_addr); //转换ip 保存到结构体内 
    	
    //2.connect  主动连接服务器
    	connect(sockfd,(struct sockaddr *)&sfdaddr,sizeof(sfdaddr));	
    	//3.读写
    #if 1
    	
        while(1)
    	{        
            memset(wbuf,0,1024);
            memset(rbuf,0,1024);
            scanf("%s",wbuf);
            write(sockfd,wbuf,strlen(wbuf));
    		len=read(sockfd,rbuf,sizeof(rbuf));
    		//write(STDOUT_FILENO,buf,len);
            printf("%s\n",rbuf);		
    	}
    	
    #endif	
    	//4.close
    	close(sockfd);
    	return 0;
    }
    

     

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值