socket一个服务端连接多个客户端,socket连接多个服务器
程序名是:TcpSocketOneServerToMulClient
程序功能:实现单台服务器对多台客户端通信功能的小程序。
PS:这是继上次简单的Tcp Windows Socket编程之后的又一个程序。程序实现还是不是很严谨,需要改进~
两个。查看效果:
三个。写作思路:
按照前面的程序思路,如果要实现单台服务器与多台客户端程序之间的通信,本次程序编写试图从多线程的角度考虑问题:
在服务器的实现中,main函数可以作为主线程,不断接收客户端的连接请求。
创建一个新子线程3354。每次连接客户端时,都会为该客户端创建一个新的子线程,用于接收信息并将其显示在屏幕上。
然后,创建一个新的子线程,专门用于在本地发送消息。
在客户端的实现中,主线程负责连接到服务器,创建一个新的子线程用于从服务器接收信息,然后创建一个子线程用于从客户端向服务器发送信息。
一般来说,也可以理解为单个服务器的进程使用本服务器中的线程与多个客户端进程进行通信。
四个。程序源代码:
【cpp】查看plaincopyprint?
//oneservermain。CPP #包含iostream #包含CST dio #包含字符串#包含向量#包含迭代器#包含算法#包含Winsock 2。h #包含窗口。h usingnamespacetdHANDLEbufferMutex//令其能互斥成功正常通信的信号量句柄SOCKETsockConn//客户端的套接字向量套接字clientSocketGroupintmain(){//加载窝动态链接库(dll)WORDwVersionRequested;WSADATAwsaData//这结构是用于接收WjndowsSocket的结构信息的wVersionRequested=MAKEWORD(2,2);//请求2.2版本的套接字库interr=wsa startup(wVersionRequested,wsa data);如果(呃!=0){ return-1;//返回值为零的时候是表示成功申请wsa startup } if(LOBYTE(wsa数据。w版)!=2HIBYTE(wsaData.wVersion)!=2){//检测是否2.2版本的窝库WSACleanup();return-1;}//创建窝操作,建立流式套接字,返回套接字号SOCK SRV SOCKETsockSrv=socket(AF _ INET,SOCK_STREAM,0);//套接字sockSrv与本地地址相连SOCKADDR _ INaddrSrvaddrSrv.sin _ addr .S_un .s _ addr=htonl(在addr _ ANY中);//将INADDR_ANY转换为网络字节序,调用htonl(long型)或htons(整型)addr SRV . sin _ family=AF _ INETaddr SRV。sin _ port=htons(6000);if(SOCKET_ERROR==bind(sockSrv,(SOCKADDR*) addrSrv,sizeof(SOCKADDR))){//第二参数要强制类型转换return-1;}//将套接字设置为监听模式(连接请求),听()通知三氯苯酚服务器准备好接收连接听(sockSrv,20);cout 服务器已成功就绪,若服务器想发送信息给客户端,可直接输入内容后按回车. n ;//接受(),接收连接,等待客户端连接buffer mutex=创建信号量(NULL,1,1,NULL);DWORDWINAPISendMessageThread(LPVOIDIpParameter);DWORDWINAPIReceiveMessageThread(LPVOIDIpParameter);HANDLEsendThread=CreateThread(NULL,0,SendMessageThread,NULL,0,NULL);while(true){//不断等待客户端请求的到来sockConn=accept(sockSrv,NULL,NULL);if(SOCKET_ERROR!=sock conn){ clientsocketgroup。push _ back(袜子连接);} HANDLEreceiveThread=CreateThread(NULL,0,ReceiveMessageThread,(LPVOID)sockConn,0,NULL);WaitForSingleObject(bufferMutex,INFINITE);//P(资源未被占用)if(NULL==接收线程){ printf( ncretaphreadanswerthread()失败. n );} else { printf( ncreareceiveclientthreadok . n’);}ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)} WaitForSingleObject(发送线程,无限);//等待线程结束关闭句柄(发送线程);关闭句柄(缓冲区互斥);WSACleanup();//终止对套接字库的使用printf( n );系统("暂停");return 0 } DWORDWINAPISendMessageThread(LPVOIDIpParameter){ while(1){ string talk;getline(cin,talk);WaitForSingleObject(bufferMutex,INFINITE);//P(资源未被占用)/* if( quit ==talk){释放信号量(buffer mutex,1,NULL);//V(资源占用完毕)return0} else */{ talk。追加( n );} printf( ISay:( quit 退出):);cout talk for(inti=0;我clientsocketgroup。size();i){//send(clientSocketGroup[i],talk.c_str(),talk.size(),0);//发送信息send(clientSocketGroup[i],talk.c_str(),200,0);//发送信息}ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)} return 0 } DWORDWINAPIReceiveMessageThread(LPVOIDIpParameter){ SOCKET客户端SOCKET=(SOCKET)(LPVOID)IpParameter;while(1){ charrecvBuf[300];recv(ClientSocket,recvBuf,200,0);WaitForSingleObject(bufferMutex,INFINITE);//P(资源未被占用)if(recvBuf[0]== q recvBuf[1]== u recvBuf[2]== I recvBuf[3]== t recvBuf[4]== 0 ){ vector SOCKET:iterator result=find(clientsocketgroup。begin()、clientSocketGroup.end()、客户端套接字);clientSocketGroup.erase(结果);关闭套接字(客户端套接字);ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)printf( n注意:AClienthasleave.n ,200,0);打破;}printf(%sSays:%sn , OneClient ,recvBuf);//接收信息ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)} return0}//OneServerMain.cpp
#包括输入输出流
#包括cstdio
#包含字符串
#包括字符串
#包含矢量
#包含迭代器
#包含算法
#包含Winsock2.h
#包含Windows.h
使用命名空间标准
处理bufferMutex//令其能互斥成功正常通信的信号量句柄
插座sockConn//客户端的套接字
向量套接字clientSocketGroup
int main()
//加载窝动态链接库(动态链接库)
要求单词版本
WSADATA wsaData//这结构是用于接收Wjndows插座的结构信息的
wVersionRequested=MAKEWORD( 2,2);//请求2.2版本的套接字库
int err=wsa startup(wVersionRequested,wsa data);
如果(呃!=0 ) {
return-1;//返回值为零的时候是表示成功申请WSAStartup
if ( LOBYTE( wsaData.wVersion)!=2 HIBYTE( wsaData.wVersion)!=2 ) { //检测是否2.2版本的窝库
WSACleanup();
return-1;
//创建窝操作,建立流式套接字,返回套接字号sockSrv
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
//套接字sockSrv与本地地址相连
SOCKADDR _ IN addrSrv
addrSrv.sin_addr .S_un .s _ addr=htonl(在addr _ ANY中);//将INADDR_ANY转换为网络字节序,调用htonl(long型)或htons(整型)
addrSrv.sin _ family=AF _ INET
地址服务。sin _ port=htons(6000);
if(SOCKET_ERROR==bind(sockSrv,(SOCKADDR*) addrSrv,sizeof(SOCKADDR))){ //第二参数要强制类型转换
return-1;
//将套接字设置为监听模式(连接请求),听()通知三氯苯酚服务器准备好接收连接
听(sockSrv,20);
cout 服务器已成功就绪,若服务器想发送信息给客户端,可直接输入内容后按回车. n ;
//接受(),接收连接,等待客户端连接
buffer mutex=创建信号量(NULL,1,1,NULL);
DWORD WINAPI sendmessage线程(LPVOID IpParameter);
DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter);
HANDLE send thread=CreateThread(NULL,0,SendMessageThread,NULL,0,NULL);
while(true){ //不断等待客户端请求的到来
sockConn=accept(sockSrv,NULL,NULL);
if (SOCKET_ERROR!=sockConn){
clientsocketgroup。push _ back(袜子连接);
HANDLE receive thread=CreateThread(NULL,0,ReceiveMessageThread,(LPVOID)sockConn,0,NULL);
WaitForSingleObject(bufferMutex,INFINITE);//P(资源未被占用)
if(NULL==receiveThread) {
printf( ncrethread应答线程()失败. n );
否则{
printf( n创建接收客户端线程成功. n );
ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)
WaitForSingleObject(发送线程,无限);//等待线程结束
关闭句柄(发送线程);
关闭句柄(缓冲区互斥);
WSACleanup();//终止对套接字库的使用
printf( n );
系统("暂停");
返回0;
WaitForSingleObject(bufferMutex,INFINITE);//P(资源未被占用)
/* if(退出==说话){
ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)
返回0;
else*/
说话。追加( n );
printf(我说:(退出退出):);
cout谈话
for(int I=0;我clientsocketgroup。size();i){
//send(clientSocketGroup[i],talk.c_str(),talk.size(),0);//发送信息
send(clientSocketGroup[i],talk.c_str(),200,0);//发送信息
ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)
返回0;
recv(ClientSocket,recvBuf,200,0);
WaitForSingleObject(bufferMutex,INFINITE);//P(资源未被占用)
if(recvBuf[0]== q recvBuf[1]== u recvBuf[2]== I recvBuf[3]= t recvBuf[4]= 0 ){
向量套接字:迭代器结果=find(clientsocketgroup。begin()、clientSocketGroup.end()、客户端套接字);
clientSocketGroup.erase(结果);
关闭套接字(客户端套接字);
ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)
printf( n注意:客户已经离开.n ,200,0);
打破;
printf(%s说:%sn ,一个客户端,recvBuf);//接收信息
ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)
返回0;
【cpp】查看plaincopyprint?
//mul客户端main。CPP # include iostream # include CST dio # include string # include cstring # include Winsock 2。h #包含窗口。h usingnamespacetdSOCKETsockClient//连接成功后的套接字HANDLEbufferMutex//令其能互斥成功正常通信的信号量句柄intmain(){//加载窝动态链接库(dll)WORDwVersionRequested;WSADATAwsaData//这结构是用于接收WjndowsSocket的结构信息的wVersionRequested=MAKEWORD(2,2);//请求2.2版本的套接字库interr=wsa startup(wVersionRequested,wsa data);如果(呃!=0){//返回值为零的时候是表示成功申请WSAStartup返回-1;}if(LOBYTE(wsaData.wVersion)!=2HIBYTE(wsaData.wVersion)!=2){//检查版本号是否正确WSACleanup();return-1;}//创建窝操作,建立流式套接字,返回套接字号SOCK client SOCK client=socket(AF _ INET,SOCK_STREAM,0);if(sock client==INVALID _ SOCKET){ printf( Erroratsocket():% LD n ,WSAGetLastError());WSACleanup();return-1;}//将套接字sockClient与远程主机相连//intconnect(SOCKETs,conststructsockaddr*name,int name len);//第一个参数:需要进行连接操作的套接字//第二个参数:设定所需要连接的地址信息//第三个参数:地址的长度SOCKADDR _ INaddrSrvaddrSrv.sin _ addr .S_un .s _ addr=inet _ addr( 127。0 .0 .1 );//本地回路地址是127.0.0.1;addr SRV . sin _ family=AF _ INETaddr SRV。sin _ port=htons(6000);connect(sockClient,(SOCKADDR*) addrSrv,sizeof(SOCKADDR));cout 本客户端已准备就绪,用户可直接输入文字向服务器反馈信息。 n ;//send(sockClient, n注意:AClienthasenter.n ,strlen(注意:AClienthasenter.n) 1,0);发送(sockClient, n注意:AClienthasenter.n ,200,0);buffer mutex=创建信号量(NULL,1,1,NULL);DWORDWINAPISendMessageThread(LPVOIDIpParameter);DWORDWINAPIReceiveMessageThread(LPVOIDIpParameter);HANDLEsendThread=CreateThread(NULL,0,SendMessageThread,NULL,0,NULL);HANDLEreceiveThread=CreateThread(NULL,0,ReceiveMessageThread,NULL,0,NULL);WaitForSingleObject(发送线程,无限);//等待线程结束关闭socket(sock客户端);关闭句柄(发送线程);关闭手柄(接收线程);关闭句柄(缓冲区互斥);WSACleanup();//终止对套接字库的使用printf(结束链接. n’);printf( n );系统("暂停");return 0 } DWORDWINAPISendMessageThread(LPVOIDIpParameter){ while(1){ string talk;getline(cin,talk);WaitForSingleObject(bufferMutex,INFINITE);//P(资源未被占用)if(退出==talk){ talk。push _ back( 0 );//send(sockClient,talk.c_str(),talk.size(),0);send(sockClient,talk.c_str(),200,0);打破;} else {说话。追加( n );} printf( n say:( quit 退出):);cout talk//send(sockClient,talk.c_str(),talk.size(),0);//发送信息send(sockClient,talk.c_str(),200,0);//发送信息ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)} return 0 } DWORDWINAPIReceiveMessageThread(LPVOIDIpParameter){ while(1){ charrecvBuf[300];recv(sockClient,recvBuf,200,0);WaitForSingleObject(bufferMutex,INFINITE);//P(资源未被占用)printf(%sSays:%sn , Server ,recvBuf);//接收信息ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)} return0}//MulClientMain.cpp
#包括输入输出流
#包括cstdio
#包含字符串
#包括字符串
#包含winsock2.h
#包含Windows.h
使用命名空间标准
SOCKET sockClient//连接成功后的套接字
处理bufferMutex//令其能互斥成功正常通信的信号量句柄
int main()
//加载窝动态链接库(动态链接库)
要求单词版本
WSADATA wsaData//这结构是用于接收Wjndows插座的结构信息的
wVersionRequested=MAKEWORD( 2,2);//请求2.2版本的套接字库
int err=wsa startup(wVersionRequested,wsa data);
如果(呃!=0 ) { //返回值为零的时候是表示成功申请WSAStartup
return-1;
if ( LOBYTE( wsaData.wVersion)!=2 HIBYTE( wsaData.wVersion)!=2 ) { //检查版本号是否正确
WSACleanup();
return-1;
//创建窝操作,建立流式套接字,返回套接字号sockClient
sockClient=socket(AF_INET,SOCK_STREAM,0);
if(sock client==INVALID _ SOCKET){
printf(套接字处的错误():%ldn ,WSAGetLastError());
WSACleanup();
return-1;
//将套接字sockClient与远程主机相连
//int connect( SOCKET s,const struct sockaddr* name,int name len);
//第一个参数:需要进行连接操作的套接字
//第二个参数:设定所需要连接的地址信息
//第三个参数:地址的长度
SOCKADDR _ IN addrSrv
addrSrv.sin_addr .S_un .s _ addr=inet _ addr( 127。0 .0 .1 );//本地回路地址是127.0.0.1;
addrSrv.sin _ family=AF _ INET
地址服务。sin _ port=htons(6000);
connect(sockClient,(SOCKADDR*) addrSrv,sizeof(SOCKADDR));
cout 本客户端已准备就绪,用户可直接输入文字向服务器反馈信息。 n ;
//send(sockClient, n注意:客户端已进入.n ,strlen(注意:一个客户已经进入.n) 1,0);
发送(sockClient, n注意:客户端已进入.n ,200,0);
buffer mutex=创建信号量(NULL,1,1,NULL);
DWORD WINAPI sendmessage线程(LPVOID IpParameter);
DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter);
HANDLE send thread=CreateThread(NULL,0,SendMessageThread,NULL,0,NULL);
HANDLE receive thread=CreateThread(NULL,0,ReceiveMessageThread,NULL,0,NULL);
//send(sockClient,talk.c_str(),talk.size(),0);
send(sockClient,talk.c_str(),200,0);
打破;
否则{
说话。追加( n );
printf( n我说:(退出退出):);
cout谈话
//send(sockClient,talk.c_str(),talk.size(),0);//发送信息
send(sockClient,talk.c_str(),200,0);//发送信息
ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)
返回0;
recv(sockClient,recvBuf,200,0);
WaitForSingleObject(bufferMutex,INFINITE);//P(资源未被占用)
printf(%s说:%sn , Server ,recvBuf);//接收信息
ReleaseSemaphore(bufferMutex,1,NULL);//V(资源占用完毕)
返回0;
五。存在问题:
1.将客户端异常退出(不按程序要求输入"退出"退出),而直接用ALT F4关闭后,服务器会出现死循环显示乱码的情况。
2.在未启动服务器前提下,直接启动客户端时出现死循环乱码情况。
3.服务器输入"退出"时不能正常退出。