socket api- c/s模式:服务多次读写,客户多次写读(同步处理多客户,多线程). IO模式:阻塞t
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket api- c/s模式:服务多次读写,客户多次写读(同步处理多客户,多线程). IO模式:阻塞t相关的知识,希望对你有一定的参考价值。
服务端:
1)多线程处理客户connect。主线程,处理用户输入,二线程处理accpet,每次有新的connect,就建立新线程处理每个客户端。
2)二线程阻塞在accept,其他新线程阻塞在read。
3)当客户端close。服务端对应线程,read得到0. 之后也进行close(这一部上次实验没做,导致服务socket一直在close wait ,而客户一直在fin wait 2.)
所以客户端socket阻塞在内核的time wait。而服务端阻塞的内核的 close wait。
缺点:
服务端一直轮循accept。
server:
#include <iostream> #include <sys/socket.h>//{socket_type.h}:socket,AF_INET,SOCK_STREAM,PF_INET #include <netdb.h>//{<netinet/in.h>}:IPPROTO_TCP #include <sys/errno.h> #include <string.h> #include <stdio.h>//perror #include <fcntl.h> #include <unistd.h>//close. #include <time.h> #include <thread> #include <arpa/inet.h>//INET_PTON using namespace std; const int MAXLINE=1024*4; typedef struct sockaddr_in SA; int g_cmd; void Accpetthread(int serverFD); void threadProcess(int serverTempFD); int main() { //socket->addr->bind->listen->accept(read ,write) int serverFD; int intflag; g_cmd=0; SA serverAddr; bzero(&serverAddr,sizeof(serverAddr)); serverFD=socket(AF_INET,SOCK_STREAM,IPPROTO_IP); if(serverFD==-1) { perror("create()"); return -1; } //serverAddr.sin_addr.s_addr=htonl(INADDR_ANY); inet_pton(AF_INET,"127.0.0.1",&serverAddr.sin_addr); serverAddr.sin_family=AF_INET; serverAddr.sin_port=htons(3006); //serverAddr.sin_zero?? intflag=bind(serverFD,(sockaddr*)&serverAddr,sizeof(sockaddr)); if(intflag==-1) { perror("bind()"); return -1; } listen(serverFD,10);//max queue? thread a=thread(Accpetthread,serverFD); a.detach(); int cmd; cout<<"exist:input 88"<<endl; for(;;) { cin>>cmd; if(cmd==88) { g_cmd=88; break; } } cout<<"close listen"<<endl; close(serverFD);//close 之后,地址不能使用.要等待2ml return 0; } // void Accpetthread(int serverFD) { //这里其实没有什么用。因为是阻塞在下面的语句。 while(g_cmd!=88) { int serverTempFD=accept(serverFD,0,0); //之前没有判断。导致-1.也去处理了。 if(serverTempFD!=-1) { thread bg=thread(threadProcess,serverTempFD); bg.detach(); } } cout<<"not accept"<<endl; } void threadProcess(int serverTempFD) { while(g_cmd!=88) { char readbuf[MAXLINE]; int sizeread= read(serverTempFD,readbuf,MAXLINE-1); if(sizeread==-1) { perror("read"); close(serverTempFD); break; } if(sizeread>0) { readbuf[sizeread]=‘\0‘;//以免溢出,插入结束符号. char writebuff[MAXLINE+10]; //snprintf如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0‘) int tempsize= snprintf(writebuff,MAXLINE+10-1,"%s:%d\n",readbuf,strlen(readbuf)); cout<<writebuff<<":"<<strlen(writebuff)<<endl; write(serverTempFD,writebuff,strlen(writebuff)); } else { cout<<"server close tempfd,and will send ack+1 & server fin"<<endl; //上一次实验,忘记了这个close。应该2端都关闭。这样服务端的临时端口就可以马上关闭。而客户端也可以尽快到达外time wait状态。 //因为服务端的close是发送 server fin,和ack+1.发送完就closed,最后接收一个客户的ack。 //不过通过这次错误的实验,突然就搞清楚了。为什么服务端的wait close是2个msl。(因为怕客户端再次使用同一个端口,而分不清, //而客户端,假如是最极端的情况。发送了close,最多一个msl之后进入 wait time,而服务端要等待客户的最后一次ack,又是最多 //一个msl,所以只要2个msl,服务端就可以安全再次放开之前打算关闭的端口,) close(serverTempFD); break; } } }
client:
#include <iostream> #include <sys/socket.h>//{socket_type.h}:socket,AF_INET,SOCK_STREAM,PF_INET #include <netdb.h>//{<netinet/in.h>}:IPPROTO_TCP #include <sys/errno.h> #include <string.h> #include <stdio.h>//perror,fgets #include <fcntl.h> #include <unistd.h>//close. #include <time.h> #include <netinet/in.h> #include <arpa/inet.h>//INET_PTON #include <string> const int MAXLINE=1024*4; using namespace std; int main() { //socket->connect->read. int socketClientFD; int statusFlag; socketClientFD=socket(PF_INET,SOCK_STREAM,IPPROTO_IP); if(socketClientFD==-1) { perror("socket()"); return -1; } struct sockaddr_in serverAddr; bzero(&serverAddr,sizeof(serverAddr)); serverAddr.sin_family=AF_INET; inet_pton(AF_INET,"127.0.0.1",&serverAddr.sin_addr); //printf("%0x,%0x,%0x,%0x",((char*)&serverAddr.sin_addr)[0],((char*)&serverAddr.sin_addr)[1],((char*)&serverAddr.sin_addr)[2],((char*)&serverAddr.sin_addr)[3]); serverAddr.sin_port=htons(3006); statusFlag=connect(socketClientFD,(sockaddr*)&serverAddr,sizeof(serverAddr)); if(statusFlag==-1) { perror("connect()"); return -1; } char writeChar[MAXLINE]; char buff[MAXLINE]; ////和fgets(line,MAXLINE,stdin);的作用是一样的。都会只读取vmaxline-1的数量。最后加入0x00.但是用这个跨平台。而且fgets会读取换行符号。 while(true) { cin.getline(writeChar,MAXLINE); cout<<writeChar<<endl; if(writeChar[0]==‘8‘&&writeChar[1]==‘8‘&&writeChar[2]==‘\0‘) { cout<<"88"<<endl; close(socketClientFD); break; } else { if(strlen(writeChar)>0) { statusFlag= write(socketClientFD,writeChar,strlen(writeChar));//写的时候只发送字符。不发送结束符。所以用strlen. if(statusFlag==-1) { perror("write()"); return -1; } statusFlag=read(socketClientFD,buff,MAXLINE-1);//这里会读入任何符号。包括结束符号。所以要求写的时候,不要发送结束符号。因此要求用strlen,确定实际字符。 if(statusFlag>0) { buff[statusFlag]=‘\0‘; cout<<buff<<flush; } else if(statusFlag==0) { cout<<endl; break; } else { perror("read()"); return -1; } } else { break; } } } //fgets(getLine,MAXLINE,cin); // statusFlag= write(socketClientFD,writeChar,strlen(writeChar));//写的时候只发送字符。不发送结束符。所以用strlen. // // if(statusFlag==-1) // { // perror("write()"); // return -1; // } // // for(;;) // { // //bzero(buff,11);//每次要清空,更安全点。虽然后面已经buff[statusFlag]=‘\0‘; // statusFlag=read(socketClientFD,buff,10);//这里会读入任何符号。包括结束符号。所以要求写的时候,不要发送结束符号。因此要求用strlen,确定实际字符。 // if(statusFlag>0) // { // buff[statusFlag]=‘\0‘; // cout<<buff<<flush; // } // else if(statusFlag==0) // { // cout<<endl; // break; // } // else // { // perror("read()"); // return -1; // } // } // // cout<<"eixist:input 88."<<endl; // int cmd; // while(1) // { // cin>>cmd; // // if(cmd==88) // { // close(socketClientFD); // break; // } // } return 0; }
以上是关于socket api- c/s模式:服务多次读写,客户多次写读(同步处理多客户,多线程). IO模式:阻塞t的主要内容,如果未能解决你的问题,请参考以下文章
socket api- c/s模式:服务读写,客户写读. IO模式:阻塞
socket api- c/s模式:服务写,客户读. IO模式:阻塞
socket api- c/s模式:全双工 ;IO模式:同步阻塞,select,多路复用。