Linux網路程式設計TCP( 1 )——socket
在學習主要程式碼之前先了解下TCP通訊兩端使用socket函式建立連線的流程:
1、客戶端和伺服器均使用socket函式各自建立一個套接字
2、伺服器用bind繫結IP地址和埠號
3、伺服器用listen開啟監聽模式
3、伺服器用accept阻塞自己,等待客戶端的連線請求
4、客戶端通過connect發出連線請求
5、伺服器收到連線請求後,建立連線,之後雙方可用recv(接收)和send(傳送)進行通訊
通訊結束後雙方通過close()關閉套接字
int socket(int domain,int type,int protocol);
引數:1、指明所用協議族 常用的有AF_INET AF_INET6 AF_LOCAL AF_ROUTE
在TCP連線中一般使用AF_INET表示用ipv4和16位埠號
2、套接字型別。SOCK_STREAM:TCP SOCK_DGRAM:UDP SOCK_RAW:允許對底層訪問
3、協議類別。可以為0讓系統自動選擇
返回值若小於0說明發生錯誤,否則返回一個值表示建立套接字,類似每個人的身份證號一樣,
之後都用套接字描述符來形容這個值
int bind(int sockfd,const struct sockaddr*,socklen_t addrlen)
引數:1、套接字描述符
2、協議地址,用於指定協議族、埠和IP地址
3、第二個引數的長度
發生錯誤返回-1,繫結成功返回0; 該函式用於將某個套接字與協議地址關聯起來
int listen(int sockfd,int backlog)
引數:1、套接字描述符
2、接受請求的最大長度
僅被伺服器呼叫,將套接字變為被動連線,將套接字變為監聽套接字,成功返回0,失敗返回-1
int accept(int sockfd, struct sockaddr* addr,socklen_t *addrlen);
引數:1、套接字描述符
2、用於存放客戶端的協議地址資訊
3、第二個引數的長度,注意,這裡不要直接用sizeof,會出錯。要定義一個socklen_t的變數。
伺服器阻塞式等待客戶端請求,成功連線後,客戶端相關資訊會被儲存到第二個引數裡。
失敗返回-1 成功返回客戶端的套接字描述符
int connect(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
引數:1、套接字描述符
2、伺服器的協議地址
3、第二個引數的長度
客戶端發起連線請求並連線 成功返回0 失敗返回-1
int send(int sockfd,const void* msg,int len,int flags)
引數:1、傳送端套接字描述符
2、傳送資料緩衝區
3、第二個引數的長度
4、0個或多個標誌的組合體,可直接設定為0 或用“|”連在一起。
MSG_DONTWAIT:非阻塞操作
MSG_DONTROUTE:告知網際層協議,目的主機在網路,不需要查詢路由表
MSG_NOSIGNAL:動作不願被SIGPIPE訊號中斷
MSG_OOB:表示接受到需要優先處理的資料
用於通訊間傳送資料
int recv(int sockfd,void buf,int len,unsigned int flags)
引數:1、接受端套接字描述符
2、接受緩衝區基地址
3、以位元組計算接受換乘區長度
4、0或多個標誌的組合體
MSG_DONTWAIT:設定非阻塞操作
MSG_PEEK:接收資料後,在接收佇列保留原有資料
MSG_WAITALL:阻塞操作
MSG_OOB:表示接受到需要優先處理的資料
用於通訊接收資料
樣例:
客戶端
1 #include<unistd.h> 2 #include<cstdio> 3 #include<cstring> 4 #include<stdlib.h> 5 #include<sys/socket.h> 6 #include<arpa/inet.h> 7 #include<netinet/in.h> 8 #include<string.h> 9 using namespace std; 10 int main() 11 { 12 int sockfd; 13 struct sockaddr_in sock_addr; 14 int i,j,op; 15 sockfd=socket(AF_INET,SOCK_STREAM,0); 16 if(sockfd<0) 17 { 18 printf("套接字建立失敗\n"); 19 return -1; 20 } 21 bzero(&sock_addr,sizeof(sock_addr)); 22 sock_addr.sin_family=AF_INET; 23 sock_addr.sin_port=htons(8000); 24 25 op=connect(sockfd,(struct sockaddr*)&sock_addr,sizeof(sock_addr)); 26 if(op<0) 27 { 28 printf("connect error\n"); 29 close(sockfd); 30 return -1; 31 } 32 33 while(1) 34 { 35 char buf[1010]=""; 36 37 fgets(buf,sizeof(buf),stdin); 38 send(sockfd,buf,strlen(buf),0); 39 //printf("send:%s\n",buf); 40 if((op=recv(sockfd,buf,strlen(buf),0))>0) 41 { 42 buf[op]='\0'; 43 printf("%s\n",buf); 44 } 45 } 46 close(sockfd); 47 return 0; 48 }
伺服器:
伺服器端改用select實現非阻塞連線
1 #include<iostream> 2 #include<sys/socket.h> 3 #include<arpa/inet.h> 4 #include<netinet/in.h> 5 #include<unistd.h> 6 #include<cstdlib> 7 #include<cstdio> 8 #include<string.h> 9 #include<pthread.h> 10 #include<sys/select.h> 11 #include<algorithm> 12 #include<sys/time.h> 13 #include<sys/types.h> 14 using namespace std; 15 16 int main() 17 { 18 int sockfd,sfd; 19 struct sockaddr_in sock_addr; 20 int i,j,op; 21 int maxfd,maxi,n; 22 23 fd_set rset,allset; 24 int client[1024]; 25 char buf[100]; 26 27 sockfd=socket(AF_INET,SOCK_STREAM,0); 28 if(sockfd<0) 29 { 30 printf("套接字建立失敗\n"); 31 return -1; 32 } 33 34 bzero(&sock_addr,sizeof(sock_addr)); 35 sock_addr.sin_family=AF_INET; 36 sock_addr.sin_port=htons(8000); 37 sock_addr.sin_addr.s_addr=htons(INADDR_ANY); 38 39 op=bind(sockfd,(struct sockaddr*)&sock_addr,sizeof(sock_addr)); 40 if(op<0) 41 { 42 printf("套接字繫結失敗\n"); 43 close(sockfd); 44 return -1; 45 } 46 47 op=listen(sockfd,4);//4表示套接字最大維護連線數目 48 if(op<0) 49 { 50 printf("listen error\n"); 51 close(sockfd); 52 return -1; 53 } 54 int clientfd; 55 56 struct sockaddr_in client_addr; 57 bzero(&client_addr,sizeof(client_addr)); 58 socklen_t len=sizeof(struct sockaddr); 59 60 maxfd=sockfd; 61 maxi=-1; 62 for(i=0;i<1024;i++) 63 client[i]=-1; 64 65 FD_ZERO(&allset); 66 FD_SET(sockfd,&allset);//構造監聽套接字檔案描述符 67 68 while(1) 69 { 70 //sleep(5); 71 rset=allset; 72 op=select(maxfd+1,&rset,NULL,NULL,NULL); 73 if(op<0) 74 { 75 cout<<"select error\n"; 76 return -1; 77 } 78 79 if(FD_ISSET(sockfd,&rset)) 80 { 81 printf("111111\n"); 82 clientfd=accept(sockfd,(struct sockaddr*)&client_addr,&len); 83 printf("222222\n"); 84 85 if(clientfd<0) 86 { 87 printf("accept error\n"); 88 return -1; 89 } 90 91 char clientip[32]=""; 92 inet_ntop(AF_INET,&client_addr.sin_addr,clientip,16); 93 printf("ip:%s,port:%d\n",clientip,ntohs(client_addr.sin_port)); 94 95 for(i=0;i<1024;i++) 96 { 97 if(client[i]<0) 98 { 99 client[i]=clientfd; 100 break; 101 } 102 else cout<<clientfd<<endl; 103 } 104 105 if(i==1024) 106 { 107 cout<<"to many clients\n"; 108 return -1; 109 } 110 111 FD_SET(clientfd,&allset); 112 if(clientfd>maxfd) 113 maxfd=clientfd; 114 115 maxi=max(maxi,i); 116 117 if(--op==0) 118 continue; 119 } 120 //sleep(5); 121 for(i=0;i<=maxi;i++) 122 { 123 sfd=client[i]; 124 if(sfd<0) 125 continue; 126 if(FD_ISSET(sfd,&rset)) 127 { 128 cout<<op<<endl; 129 if((n=read(sfd,buf,90))==0) 130 { 131 cout<<"close"<<sfd<<endl; 132 close(sfd); 133 FD_CLR(sfd,&allset); 134 client[i]=-1; 135 } 136 else if(n>0) 137 { 138 buf[n]='\0'; 139 cout<<"recv data:"<<buf<<endl; 140 bzero(&buf,sizeof(buf)); 141 cout<<"send:"; 142 fgets(buf,sizeof(buf),stdin); 143 send(sfd,buf,strlen(buf),0); 144 } 145 if(--op==0) 146 break; 147 } 148 } 149 //cout<<"oooooooooooooooooooo"<<endl; 150 } 151 cout<<"wryyyyyyyyyyyyyyyyy"<<endl; 152 close(clientfd); 153 close(sockfd); 154 return 0; 155 }
參考書籍——《Linux網路程式設計》