socket api- c/s模式:全双工 ;IO模式:同步阻塞,select,多路复用。
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket api- c/s模式:全双工 ;IO模式:同步阻塞,select,多路复用。相关的知识,希望对你有一定的参考价值。
server:
client:
知识点:
1)服务端的多线程改为select复用来处理多用户。
应该服务器瓶颈是在网络。CPU应该不是瓶颈,如果网络不是瓶颈,想再提高性能,可以考虑增加线程处理。
1.标准输入可以另用线程,从select 分离出去。
2.serverFD的accpet,也可以另用线程,分离出去。不过就要处理共享数据的tempserver的Fd集合了,加锁。所以这个感觉还是不要分离出去好。毕竟集合的添加删除是个频繁操作。
3.多用户的处理,倒是可以用多线程处理。根据集合大小的情况。超过某个数量。就新开线程处理集合的某部分。新开的线程明显应该从某个数量检测,而select要从0开始检测fd,这个倒是浪费性能的事情。
未处理点:
1)还是要分行进行任务的处理。
#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 <arpa/inet.h>//INET_PTON #include <chrono> #include <vector> #include <algorithm> using namespace std; const int MAXLINE=1024*4; typedef struct sockaddr_in SA; string 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=""; 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(3007); //serverAddr.sin_zero?? intflag=bind(serverFD,(sockaddr*)&serverAddr,sizeof(sockaddr)); if(intflag==-1) { perror("bind()"); return -1; } listen(serverFD,10);//max queue? string cmd; cout<<"exist:input 88"<<endl; //stdin->cmd .socket->read&write. socket -> accept. fd_set fdset; int maxsocketFD=serverFD>STDIN_FILENO?serverFD+1:serverFD+1; vector<int> fdArrays; while(true) { FD_ZERO(&fdset); FD_SET(serverFD,&fdset); FD_SET(STDIN_FILENO,&fdset); //int max_id= max_element(fdArrays.begin(),fdArrays.end()); for(int tempfdid: fdArrays) { FD_SET(tempfdid,&fdset); maxsocketFD=maxsocketFD>tempfdid?maxsocketFD:tempfdid+1; SA peerSA; socklen_t slen; int gresult= getpeername(tempfdid, (sockaddr*)&peerSA,&slen); if(gresult==-1) { perror("getpeername"); } else { cout<<peerSA.sin_port<<endl; } } select(maxsocketFD,&fdset,0,0,0); if(FD_ISSET(STDIN_FILENO,&fdset)) { cin>>cmd; if(cmd=="88") { close(serverFD); break; } } if(FD_ISSET(serverFD,&fdset)) { //accept.& save it to array. int serverTempFD=accept(serverFD,0,0); if(serverTempFD==-1) { perror("accept"); } else { fdArrays.push_back(serverTempFD); } } for(int tempfdid: fdArrays) { if(FD_ISSET(tempfdid,&fdset)) { char readbuf[MAXLINE]; bzero(readbuf,MAXLINE); int sizeread= read(tempfdid,readbuf,MAXLINE-1); if(sizeread==-1)//-1到底是个什么状态?是彻底不能连接还是可以重试? { perror("read"); fdArrays.erase(find(fdArrays.begin(),fdArrays.end(),tempfdid)); close(tempfdid); break; } else if(sizeread==0)//peer close or shutdown wr. { fdArrays.erase(find(fdArrays.begin(),fdArrays.end(),tempfdid)); close(tempfdid); break; } else { readbuf[sizeread]=‘\0‘;//以免溢出,插入结束符号. char writebuff[MAXLINE+10]; //snprintf如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0‘) snprintf(writebuff,MAXLINE+10-1,"%s:%d\n",readbuf,strlen(readbuf)); cout<<writebuff<<flush; write(tempfdid,writebuff,strlen(writebuff)); } } } } return 0; }
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(3007); statusFlag=connect(socketClientFD,(sockaddr*)&serverAddr,sizeof(serverAddr)); if(statusFlag==-1) { perror("connect()"); return -1; } char writeChar[MAXLINE]; char buff[MAXLINE]; fd_set fdset;//定义描述符集。 FD_ZERO(&fdset);//初始化描述符集。 int endflag=0; while(true) { //1)如果没有会触发很多次shutdown(socketClientFD,SHUT_WR);。,并且导致服务端,read 的时候会收到-1. //这样直接就关闭connect。客户端可能读不到任何数据。 //2)为什么每次都要重新设置? if(endflag==0) { FD_SET(STDIN_FILENO,&fdset);//打开标准输入bit位。 } else { FD_CLR(STDIN_FILENO,&fdset);//与其后面取消。还不如根绝标志这里设置。这样还统一一点。代码更清晰. } FD_SET(socketClientFD,&fdset);//打开客户socket bit位。 int maxCheckFDCount=socketClientFD>STDIN_FILENO?socketClientFD+1:STDIN_FILENO+1;//只有2个描述符,肯定是除标准外的那个+1. select(maxCheckFDCount,&fdset,0,0,0);//只关心接收描述符。 if(FD_ISSET(STDIN_FILENO,&fdset)) { //cin.getline(writeChar,MAXLINE); bzero(writeChar,MAXLINE); cout<<"stdin fire"<<endl; int n=read(STDIN_FILENO,writeChar,MAXLINE); if(n==0) { cout<<"wirte over"<<endl; shutdown(socketClientFD,SHUT_WR); endflag=1; //close(socketClientFD); } else { statusFlag= write(socketClientFD,writeChar,strlen(writeChar));//写的时候只发送字符。不发送结束符。所以用strlen. if(statusFlag==-1) { perror("write()"); return -1; } } } if(FD_ISSET(socketClientFD,&fdset)) { bzero(buff,MAXLINE); statusFlag=read(socketClientFD,buff,MAXLINE-1); cout<<"start read"<<endl; if(statusFlag==0)//非正常关闭。因为这个cs模式下,服务端是被动关闭。程序是不可能得到0的。只有客户端close后才会由内核得到0. { cout<<"server close."<<endl; //close(socketClientFD);//到这里关闭。 break; } else if(statusFlag==-1) { perror("read()"); return -1; } else { buff[statusFlag]=‘\0‘; cout<<buff<<flush; } } } return 0; }
以上是关于socket api- c/s模式:全双工 ;IO模式:同步阻塞,select,多路复用。的主要内容,如果未能解决你的问题,请参考以下文章
socket api- c/s模式:服务多次读写,客户多次写读(同步处理多客户,多线程,). IO模式:select 阻塞,多路复用《客户端select,应对服务端奔溃和利用socket的全双工发送和
socket api- c/s模式:服务写,客户读. IO模式:阻塞
socket api- c/s模式:服务读写,客户写读. IO模式:阻塞