糖儿飞教你学C++ Socket网络编程——24. 控制台版多线程TCP一对多通信程序

本节实现一个多线程TCP通信程序,该程序与项目二的程序相比,最大的特点就是可以实现一对多通信,即一个服务器端可以与若干个客户端同时通信,一个服务器端与3个相同的客户端同时通信的运行效果如图10-1所示。

图10-1 多线程TCP通信程序的界面(左上为服务器端,其余均为客户端)

10.2.1服务器端程序的原理

在TCP通信中,要使服务器端能够同时接受多个客户端的连接,则可以把accept()函数放到一个while循环中,这样accept()函数就会执行很多次,每当有客户端发来连接请求时,accept()函数就会接受连接,从而实现了服务器能接受多个客户端的连接。

每接受一个客户端连接后,就创建一个子线程单独与这个客户端进行通信,只要让主线程在accept函数之后派出新线程,然后让主线程继续listen监听,处理新的连接请求,让新线程自行与客户端通信即可,程序的流程如图10-2所示。

 

socket()

bind()

accept()

CreateThread()

closesocket()

recv()

listen()

服务器

send()

派生线程

主线程

传递通信套接字

接收sockConn

while

循环体

图10-2 TCP多线程通信的服务器端流程图

需要注意的是,子线程要与一个客户端通信,必须知道这个客户端对应的套接字,因此必须把accept()函数返回的套接字作为线程函数的参数传递给子线程。由于传递给线程函数的参数必须是一个指针类型,因此声明套接字时必须声明为Socket的指针类型,例如:

SOCKET *sockConn = new SOCKET;       // sockConn是一个指针类型

// *sockConn是指针指向的套接字

*sockConn=accept(sockSer,(SOCKADDR*)&addrCli,&len)

10.2.2服务器端程序制作步骤

本例只制作服务器端程序,客户端程序采用的是2.3.2节制作的程序,服务器端程序的制作步骤如下:

新建工程,选择“Win32 Console Application”,输入工程名(如MThreadS),在下一步中选择“一个简单的程序”。在“工程名.cpp”文件中,输入如下代码,编译,运行。

#include<iostream.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib")

DWORD WINAPI AnswerThread(LPVOID lparam); //线程函数声明

int main(){     

       WSADATA wsaData;    

       if(WSAStartup(MAKEWORD(2,2), &wsaData))        //初始化WinSock协议栈

       {     cout<<"加载Winsock协议栈失败!";

              WSACleanup();

              return 0;         }    

       SOCKET sockSer; //创建监听套接字 

       sockSer=socket(AF_INET,SOCK_STREAM,0);                      //初始化套接字

       SOCKADDR_IN addrSer,addrCli;        //服务器端要创建两个套接字地址

       addrSer.sin_family=AF_INET;

       addrSer.sin_port=htons(5566);

       addrSer.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");

      

       bind(sockSer,(SOCKADDR*)&addrSer,sizeof(SOCKADDR));        //绑定套接字

       listen(sockSer,5);

      

       int len=sizeof(SOCKADDR);

       cout<<"等待客户端连接…"<<endl;    

       while (true)     {                   //循环接受客户端请求,这里是关键

              SOCKET *sockConn = new SOCKET;             /* 创建通信套接字 */ 

              //将accept()函数放到while循环中,实现接受多个客户端连接

       if ((*sockConn=accept(sockSer,(SOCKADDR*)&addrCli,&len)) == INVALID_SOCKET) {

                     cout<< "accept failed !\n";

                     closesocket(*sockConn);      WSACleanup();

                     return -1;        }

              //创建新线程

              DWORD ThreadID;  //double word

              CreateThread(NULL, 0, AnswerThread, (LPVOID)sockConn, 0, &ThreadID); }    

       closesocket(sockSer);

       WSACleanup();

       return 0;  }

DWORD WINAPI AnswerThread(LPVOID lparam) {        //线程函数AnswerThread

       char sendbuf[256];

       char recvbuf[256];

       SOCKET *sockConn = (SOCKET *)lparam;       //从参数中获得sockConn

       while(1){

              recv(*sockConn,recvbuf,256,0);

              cout<<"客户端说:>"<<recvbuf<<endl;    

              cout<<"服务器说:>";

              cin>>sendbuf;

              if(strcmp(sendbuf,"bye")==0){

                     break;}

              send(*sockConn,sendbuf,strlen(sendbuf)+1,0);         

       }    

       return 0;  }

猜你喜欢

转载自blog.csdn.net/wuxia2118/article/details/88372259
今日推荐