socket 通信之select

  对于socket 通信,大家很多都用的单线程通信。同时只能监听一个端口,只能响应一个服务,select的方式可以解决多个socket 被连接的问题。一次可以分配多个资源,只要一个连接便可以进行通信。在网络已经有很多的select 的例子。不过很多例子没有真正体现到select的精妙之处。此函数主要是对多个文件描述符进行监听,直到某一个或者多个被连接。

  首先我们介绍fd_set这个结构:

  fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合 FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*)。

  在select使用这个结构之前,我们需要调用FD_SET,设置对应socket的标志位,网络生很多的例子错误就在此,这里必须要绑定多个socket才是真正体会了select的多fd监听。而且不能随意指定,比如FD_SET(0,XX),这样的fd实际已经被系统占用了。在select成功返回后检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。进而根据对应的fd进行读写操作。不多说,直接粘贴代码:

  1 #include <iostream>
  2 #include <sys/times.h>
  3 #include <sys/types.h>
  4 #include <unistd.h>
  5 #include <sys/socket.h>
  6 #include <sys/select.h>
  7 #include <cstdlib>
  8 #include <cstdio>
  9 #include <cstring>
 10 #include <string>
 11 #include <signal.h>
 12 #include <netinet/in.h>
 13 #include <arpa/inet.h>
 14 #include <errno.h>
 15 using namespace std;
 16 
 17 #define max(a,b) ((a)>(b)?(a):(b))
 18 
 19 static int listen_socket(int port)
 20 {
 21     sockaddr_in s_in;
 22     int s;
 23     int yes;
 24     if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
 25     {
 26         cout<<"socket error"<<strerror(errno)<<endl;
 27         return -1;
 28     }
 29     yes = 1;
 30     if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&yes,sizeof(yes)) == -1)
 31     {
 32         cout<<"setsockopt error"<<strerror(errno)<<endl;
 33         close(s);
 34         return -1;
 35     }
 36     
 37     memset(&s_in, 0, sizeof(s_in));
 38     s_in.sin_port = htons(port);
 39     s_in.sin_family =  AF_INET;
 40     s_in.sin_addr.s_addr = inet_addr("127.0.0.1");
 41     if(bind(s,(sockaddr*)&s_in, sizeof(s_in)) == -1)
 42     {
 43         cout<<"bind error"<<strerror(errno)<<endl;
 44         close(s);
 45         return -1;
 46     }
 47     listen(s,10);
 48     cout<<"socket -> setsockopt-> bind->listen"<<port<<endl;
 49     return s;
 50 }
 51 
 52 
 53 
 54 int main(int argi, char* args[])
 55 {    
 56     int h, h1;
 57     int fd1 = -1, fd2 = -2;
 58     
 59     if(argi !=  3)
 60     {    
 61     cout<<"Usage server listen_port1 listen_port2"<<endl;
 62         exit(-1);
 63         
 64     }
 65     
 66     int listen_port1 = atoi(args[1]);
 67     fd1 = listen_socket(listen_port1);
 68     int listen_port2 = atoi(args[2]);
 69     fd2 = listen_socket(listen_port2);//这里是建立了两个socket:cyjwdm0503
 70     if(fd1==-1 || fd2==-1)
 71     {
 72         exit(-1);
 73     }
 74     for(;;)
 75     {
 76         int r, nfds = 0;
 77         fd_set rfd,sfd;
 78 
 79         FD_ZERO(&rfd);
 80         FD_ZERO(&sfd);
 81         FD_SET(fd1, &rfd);//绑定对应的两个socket
 82         FD_SET(fd2,&rfd);
 83 
 84         int maxnum = max(6, fd1);
 85          maxnum = max(fd2, maxnum);
 86         cout<<"maxnum:"<<maxnum<<"\t"<<h<<endl;
 87 
 88         r = select(maxnum+1, &rfd, &sfd, NULL, NULL);//select的参赛为rfd,最大值为socket+1
 89         if( r == -1 && errno == EINTR)
 90         {
 91             cout<<strerror(errno)<<endl;
 92                 continue;    
 93         }
 94         if( r == -1)
 95         {
 96             cout<<"select error"<<strerror(errno)<<"\t"<<errno<<endl;
 97             return -1;
 98         }
 99 
100         //        for(int index=1; index<=5; index++)
101         {
102         int select_fd = 0;
103         if(FD_ISSET(fd1,&rfd))
104             select_fd = fd1;
105         else if(FD_ISSET(fd2,&rfd))
106             select_fd = fd2;
107         else 
108             select_fd = -1;
109         if(select_fd != -1)
110         {    
111                 cout<<"select retrun fd"<<select_fd<<endl;
112             sockaddr_in accept_addr;
113             socklen_t le = sizeof(accept_addr);//注意在accept时候的sockaddr_in 长度要初始化
114             int acc = accept(ddd, (sockaddr*)&accept_addr, &le);
115             
116             cout<<"accept port"<<accept_addr.sin_port<<endl;            
117 
118             if(-1 == acc)
119             {
120                 cout<<"accept error"<<strerror(errno)<<endl;
121                 return -1;
122             }
123             char buffer[1024] = "";
124             int ret = recv(acc, buffer, sizeof(buffer),0);
125             if(ret <= 0)
126             {
127                 cout<<"client close:"<<strerror(errno)<<"\t"<<errno<<endl;
128             }
129             else
130             {
131                 cout<<"buffer from client:"<<buffer<<endl;
132             }
133         
134         }
135         else
136             cout<<"select_fd = -1"<<endl;
137         }
138         cout<<"for over"<<endl;        
139 
140     }
141 }

  select 在监听对应的socket的rfd集合时,如果有对应的客户端链接两个socket之中的某一个,select 边能够返回对应的信息,然后调用FD_ISSET,获取对应被链接的socket ,进行accept ,收发信息。

  下面为客户端的示例代码:

 1 #include <iostream>
 2 #include <sys/socket.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <netdb.h>
 6 #include <cstdio>
 7 #include <cstdlib>
 8 #include <cstring>
 9 #include <string>
10 #include <iostream>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <errno.h>
14 
15 using namespace std;
16 
17 int main(int argi, char* args[])
18 {
19     if( argi<3)
20     {
21         cout<<args[0]<<"\t"<<"host port msg..."<<endl;
22         return -1;
23     }
24     sockaddr_in s_in;
25     memset(&s_in, 0, sizeof(s_in));
26     s_in.sin_family = AF_INET;
27     s_in.sin_addr.s_addr = inet_addr("127.0.0.1");//(args[1]);
28     s_in.sin_port = htons(atoi(args[1]));
29 
30     cout<<"port"<<"\t"<<args[1]<<endl;
31     int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
32     if( s == -1)
33     {
34         cout<<"socket error"<<strerror(errno)<<endl;
35     }
36 
37     socklen_t length  = sizeof(sockaddr_in);
38     if(-1 == connect(s, (sockaddr*)&s_in, length))
39     {
40         cout<<"connect error"<<strerror(errno)<<endl;
41         close(s);        
42         return -1;
43     }
44     for(;;)
45     {
46     char buffer[512];
47     48     if(strcmp(args[2],"q")==0)
49         break;
50     ssize_t size =    send(s, buffer, strlen(buffer),0);
51     cout<<"socket num:"<<s<<endl;
52    sprintf(buffer,"%s%s",args[2],"www.cnblogs.com/cyjwdm0503");
53     size = send(s,args[2],strlen(args[2]),0);
54     if( -1 == size)
55     {
56         cout<<"write error"<<strerror(errno)<<endl;
57         close(s);
58         return -1;
59     }
60     }
61     close(s);
62 
63 }

虽然这里是以Linux为示例,Windows和这个也类似的,转载请注明来源地址http://www.cnblogs.com/cyjwdm0503

上面如果有问题,请最好留言给我,大家交流。

转载于:https://www.cnblogs.com/cyjwdm0503/p/4766546.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值