300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 多线程+SOCKET编程实现qq群聊的服务端和客户端

多线程+SOCKET编程实现qq群聊的服务端和客户端

时间:2018-11-02 04:10:20

相关推荐

多线程+SOCKET编程实现qq群聊的服务端和客户端

多线程+SOCKET编程实现qq群聊的服务端和客户端

标签(空格分隔): 多线程 网络编程 线程同步

一、设计思路

1、服务端

每来一个客户端连接,服务端起一个线程维护;将收到的消息转发给所有的客户端;某个连接断开,需要处理断开的连接;

2、客户端

连接服务器;与服务器进行通信;下线机制;用户名定义

二、服务端具体实现

#include <iostream>#include <WinSock2.h>#include <process.h>#pragma comment(lib,"ws2_32.lib")using namespace std;/**********************************************服务端:*1、每来一个连接,服务端起一个线程维护*2、将收到的消息转发给所有的客户端*3、某个连接断开的处理*4、用互斥体进行线程同步**********************************************/#define CLIENT_MAX 256//最多的客户连接数#define RECV_BUFF 256//接收buff的最大容量int clientCnt = 0;//记录当前的客户连接数HANDLE hMutex;//定义互斥体内核对象,凡是全局变量涉及到资源竞争的问题,都要定义互斥体SOCKET sockClients[CLIENT_MAX];static unsigned WINAPI threadCli(void* arg);void sendMsg(char Msg[RECV_BUFF], int iLen);int main(void) {WORD wVersion;WSADATA wsaData;int err;HANDLE hThread;wVersion = MAKEWORD(1, 1);//初始化套接字库err = WSAStartup(wVersion, &wsaData);if (err != 0) {return err;}if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {//清理套接字库WSACleanup();return -1;}hMutex = CreateMutex(NULL, FALSE, NULL);//创建监听套接字SOCKET sockSvr = socket(AF_INET, SOCK_STREAM, 0);//准备绑定的信息SOCKADDR_IN addrSvr;memset(&addrSvr, 0, sizeof(addrSvr));addrSvr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addrSvr.sin_family = AF_INET;addrSvr.sin_port = htons(6000);//绑定到本机if (bind(sockSvr, (SOCKADDR*)&addrSvr, sizeof(SOCKADDR)) == SOCKET_ERROR) {cout << "bind error:" << GetLastError() << endl;return -1;}//监听listen(sockSvr, 5);cout << "开始监听" << endl;SOCKADDR_IN addrCli;int len = sizeof(SOCKADDR);while (true) {//接收连接请求SOCKET sockConnet = accept(sockSvr, (SOCKADDR*)&addrCli, &len);WaitForSingleObject(hMutex, INFINITE);sockClients[clientCnt++] = sockConnet;ReleaseMutex(hMutex);//每来一个连接,服务端起一个线程维护hThread=(HANDLE) _beginthreadex(NULL, 0, threadCli, (void*)&sockConnet, 0, NULL);cout << "Client ip:" << inet_ntoa( addrCli.sin_addr )<< endl;cout << "当前连接数:" << clientCnt << endl;}closesocket(sockSvr);//清理套接字库WSACleanup();system("pause");return 0;}//处理客户端线程的函数unsigned WINAPI threadCli(void* arg){//获取客户端SocketSOCKET sockCli = *((SOCKET*)arg);char recvBuf[RECV_BUFF] = {0 };int iLen=0;//服务端接收到客户端的消息以广播的方式转发所有连接的客户端while (1) {//iLen记录发送的字节数if ((iLen = recv(sockCli, recvBuf, RECV_BUFF, 0))!=-1) {sendMsg(recvBuf, iLen);}else {break;}}cout << "当前连接的客户数量为:" << clientCnt << endl;WaitForSingleObject(hMutex, INFINITE);//处理断开的连接for (int i = 0; i < clientCnt; i++) {if (sockClients[i] == sockCli) {while (i++< clientCnt) {sockClients[i] = sockClients[i + 1];}}}clientCnt--;cout << "断开连接后的客户连接数量为:" << clientCnt << endl;ReleaseMutex(hMutex);closesocket(sockCli);return 0;}//广播消息给其他的客户端void sendMsg(char Msg[RECV_BUFF], int iLen) {WaitForSingleObject(hMutex, INFINITE);for (int i = 0; i < clientCnt; i++) {send(sockClients[i], Msg, iLen, 0);}ReleaseMutex(hMutex);}

三、客户端具体实现

#include <WinSock2.h>#include <iostream>#include <process.h>using namespace std;#pragma comment(lib, "ws2_32.lib")#define BUFF_SIZE 256#define NAME_SIZE 32unsigned WINAPI sendMsg(void* arg);unsigned WINAPI recvMsg(void* arg);char nickName[NAME_SIZE] = "[DEFAULT]";char Msg[BUFF_SIZE] = {0 };//保存控制台输入的数据//argc为指针数组元素的个数int main(int argc,char *argv[]){WORD wVersion;WSADATA wsaData;int err;HANDLE hSendThread;HANDLE hRecvThread;wVersion = MAKEWORD(1, 1);err = WSAStartup(wVersion, &wsaData);if (err != 0){return err;}if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1){WSACleanup();return -1;}// 创建套接字SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN addrSrv;memset(&addrSrv, 0, sizeof(addrSrv));addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(6000);//向服务器发起连接请求if (connect(sockCli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == SOCKET_ERROR) {cout << "connect error:" << GetLastError() << endl;return -1;}//获取控制台输入的昵称,保存到nickName中sprintf_s(nickName,NAME_SIZE, argv[1]);//起一个线程发送数据给服务器hSendThread =(HANDLE) _beginthreadex(NULL, 0, sendMsg, (void*)&sockCli, 0, NULL);//起一个线程接收服务器发来的数据hRecvThread = (HANDLE)_beginthreadex(NULL, 0, recvMsg, (void*)&sockCli, 0, NULL);WaitForSingleObject(hSendThread, INFINITE);WaitForSingleObject(hRecvThread, INFINITE);// 关闭套接字closesocket(sockCli);WSACleanup();// 暂停system("pause");return 0;}//发送数据给服务器的线程unsigned WINAPI sendMsg(void* arg) {SOCKET sockCli = *((SOCKET*)arg);char szNameMsg[BUFF_SIZE + NAME_SIZE] = {0 };while (1) {fgets(Msg, BUFF_SIZE, stdin);//从控制台获取输入的数据if (!strcmp(Msg, "q\n") || !strcmp(Msg, "Q\n")) {//按下q或者Q,断开当前客户端的连接closesocket(sockCli);exit(-1);}else {sprintf_s(szNameMsg, "[%s]%s", nickName, Msg);//发送数据到服务端send(sockCli, szNameMsg, strlen(szNameMsg), 0);}}return 1;}//接收服务器数据的进程unsigned WINAPI recvMsg(void* arg) {int iLen = 0;SOCKET sockCli = *((SOCKET*)arg);char szNameMsg[BUFF_SIZE + NAME_SIZE] = {0 };while (1) {iLen = recv(sockCli, szNameMsg, BUFF_SIZE + NAME_SIZE - 1, 0);if (iLen != -1) {szNameMsg[iLen] = '\0';//接收到的数据输出到控制台fputs(szNameMsg, stdout);}else {return -1;}}return 1;}

四、运行效果

1、先启动服务端,再新建两个客户端连接,发送一些测试数据:

2、客户端A断开连接:

3、所有客户端都下线:

功能基本实现。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。