程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> socket 通信之select,socket通信select

socket 通信之select,socket通信select

編輯:C++入門知識

socket 通信之select,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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved