惠州酒店网站建设东莞网站建设优化技术
多线程 + socket 编程
服务端的设计:
1、每来一条消息,服务端起一个线程(安排一个工人)维护
2、将收到的消息转发给所有的客户端
3、某个连接断开,需要处理断开的连接
//多线程+socket编程的一个联合使用
//用互斥体进行线程同步 socket编程 临界区 全局变量#include <WinSock2.h>
#include <iostream>
#include <windows.h>
#include <process.h>#pragma comment(lib, "ws2_32.lib")#define MAX_CLNT 256
#define MAX_BUF_SIZE 256SOCKET clntSocks[MAX_CLNT]; //所有的连接的客户端socket
HANDLE hMutex;
int clntCnt = 0; //最大连接数// 服务端的设计:
//
// 1、每来一条消息,服务端起一个线程(安排一个工人)维护
//
// 2、将收到的消息转发给所有的客户端
//
// 3、某个连接断开,需要处理断开的连接//发送给所有的客户端
void SendMsg(char* szMsg, int iLen)
{WaitForSingleObject(hMutex, INFINITE);int i = 0;for (i = 0; i < clntCnt; i++){send(clntSocks[i], szMsg, iLen, 0);}ReleaseMutex(hMutex);
}//处理客户端连接的函数
unsigned WINAPI HandleClient(void* arg)
{//接收传递过来的参数SOCKET hClntSock = *((SOCKET*)arg);int iLEN = 0, i;char szMsg[MAX_BUF_SIZE] = { 0 };//进行数据的收发 循环接收//接收到客户端的数据while ((iLEN = recv(hClntSock, szMsg, sizeof(szMsg),0)) != 0){SendMsg(szMsg, iLEN);}// 3、某个连接断开,需要处理断开的连接WaitForSingleObject(hMutex, INFINITE);for (i = 0; i < clntCnt; i++){if (hClntSock == clntSocks[i]){//移位while (i < clntCnt){clntSocks[i] = clntSocks[i + 1];i++;}break;}}clntCnt--;printf("此时连接数目:%d", clntCnt);ReleaseMutex(hMutex);closesocket(hClntSock);return 0;
}
int main()
{// 加载套接字库WORD wVersionRequested;WSADATA wsaData;int err;HANDLE hThread;wVersionRequested = MAKEWORD(1, 1);// 初始化套接字库err = WSAStartup(wVersionRequested, &wsaData);if (err != 0){return err;}if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1){WSACleanup();return -1;}//---创建一个互斥对象hMutex = CreateMutex(NULL, FALSE, NULL);// 新建套接字SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(9190);// 绑定套接字到本地IP地址,端口号9190if (bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == SOCKET_ERROR){printf("bind ERRORnum = %d\n", GetLastError());return -1;}// 开始监听if(listen(sockSrv, 5) == SOCKET_ERROR)//队列容量5,当这五个没有被accept的时候,再来一个就会报错{printf("listen ERRORnum = %d\n", GetLastError());return -1;}printf("start listen\n");SOCKADDR_IN addrCli;int len = sizeof(SOCKADDR);while (true){// 接收客户连接 sockConn此时来的客户端连接SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);//1、每来一条消息,服务端起一个线程(安排一个工人)维护客户端的连接//每来一个连接,全局数组应该加一个成员,最大连接数加1WaitForSingleObject(hMutex, INFINITE);clntSocks[clntCnt++] = sockConn;ReleaseMutex(hMutex);//死锁 必须等待上一条语句结束才能进行下一条语句hThread = (HANDLE)_beginthreadex(NULL, 0, HandleClient,(void*)&sockConn, 0, NULL);printf("Connect client IP; %s \n", inet_ntoa(addrCli.sin_addr));printf("Connect client num; %d \n", clntCnt);}closesocket(sockSrv);WSACleanup();return 0;
}
客户端的设计:
1、连接服务器
2、与服务器进行通信
3、下线机制
// 1、接收服务端的消息 安排一个工人(起一个线程接收消息)
// 2、发送消息给服务端 安排一个工人(起一个线程发送消息)
// 3、退出机制
#include <WinSock2.h>
#include <iostream>
#include <windows.h>
#include <process.h>#pragma comment(lib, "ws2_32.lib")#define NAME_SIZE 20
#define BUF_SIZE 256char szName[NAME_SIZE] = "[DEFAULT]";
char szMsg[BUF_SIZE];//发送消息给服务端
unsigned WINAPI SendMsg(void* arg)
{//接收传递过来的参数SOCKET hClntSock = *((SOCKET*)arg);char szNameMsg[NAME_SIZE + BUF_SIZE]; //名字 + 消息 //循环接收来自于控制台的消息while (1){fgets(szMsg, BUF_SIZE, stdin);//阻塞在这一句//退出机制 当收到Q\q 退出if (!strcmp(szMsg, "Q\n") || !strcmp(szMsg, "q\n")){closesocket(hClntSock);exit(0);}sprintf(szNameMsg, "%s %s", szName, szMsg);//字符串拼接send(hClntSock, szNameMsg, (int)strlen(szNameMsg), 0);//发送}return 0;
}
//接收来自服务端的消息
unsigned WINAPI RecvMsg(void* arg)
{//接收传递过来的参数SOCKET hClntSock = *((SOCKET*)arg);char szNameMsg[NAME_SIZE + BUF_SIZE]; //名字 + 消息 int iLen = 0;while (1){iLen = recv(hClntSock, szNameMsg, NAME_SIZE + BUF_SIZE - 1, 0);//服务端断开if (iLen == -1){return -1;}//szNameMsg的0到iLen-1都是收到的数据 iLen个szNameMsg[iLen] = 0;///接收到消息的输出到控制台fputs(szNameMsg, stdout);}return 0;
}
//带参数的main函数,用命令行启动,在当前目录按下 shift + 鼠标右键 cmd
int main(int argc, char* argv[])
{// 加载套接字库WORD wVersionRequested;WSADATA wsaData;int err;SOCKET hSock;SOCKADDR_IN servAdr;HANDLE hSendThread,hRecvThread;wVersionRequested = MAKEWORD(1, 1);// 初始化套接字库err = WSAStartup(wVersionRequested, &wsaData);if (err != 0){return err;}if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1){WSACleanup();return -1;}sprintf(szName, "[%s]", argv[1]);//1、建立sockethSock = socket(PF_INET, SOCK_STREAM, 0);//2、配置端口和地址memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");servAdr.sin_port = htons(9190);servAdr.sin_family = AF_INET;//3、连接服务器if (connect(hSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR){printf("connect error error code = %d\n", GetLastError());return -1;}// 4、发送消息给服务端 安排一个工人(起一个线程发送消息)hSendThread = (HANDLE)_beginthreadex(NULL, 0, SendMsg, (void*)&hSock, 0, NULL);// 5、接收服务端的消息 安排一个工人(起一个线程接收消息)hRecvThread = (HANDLE)_beginthreadex(NULL, 0, RecvMsg, (void*)&hSock, 0, NULL);//等待内核对象的信号发送变化WaitForSingleObject(hSendThread, INFINITE);WaitForSingleObject(hRecvThread, INFINITE);// 6、关闭套接字closesocket(hSock);WSACleanup();return 0;}