windows网络编程(九)——IOCP+多线程实现简单的聊天(windows服务器端 windows客户端)

来源:互联网 发布:sqlserver读写分离 编辑:程序博客网 时间:2024/05/19 19:33

IOCP思维导图:



1.服务器端

#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdlib.h>#include <process.h>#include <winsock2.h>#include <windows.h>#pragma comment(lib,"ws2_32.lib");//加载ws2_32.dll#define BUF_SIZE 100#define READ3#defineWRITE5typedef struct    // socket info{SOCKET hClntSock;SOCKADDR_IN clntAdr;} PER_HANDLE_DATA, *LPPER_HANDLE_DATA;typedef struct    // buffer info{OVERLAPPED overlapped;WSABUF wsaBuf;char buffer[BUF_SIZE];int rwMode;    // READ or WRITE 读写模式} PER_IO_DATA, *LPPER_IO_DATA;unsigned int  WINAPI EchoThreadMain(LPVOID CompletionPortIO);void ErrorHandling(char *message);SOCKET ALLCLIENT[100];int clientcount = 0;HANDLE hMutex;//互斥量int main(int argc, char* argv[]){hMutex = CreateMutex(NULL, FALSE, NULL);//创建互斥量WSADATAwsaData;HANDLE hComPort;SYSTEM_INFO sysInfo;LPPER_IO_DATA ioInfo;LPPER_HANDLE_DATA handleInfo;SOCKET hServSock;SOCKADDR_IN servAdr;int  i;DWORD recvBytes = 0,flags = 0;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup() error!");hComPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);//创建CP对象GetSystemInfo(&sysInfo);//获取当前系统的信息for (i = 0; i < sysInfo.dwNumberOfProcessors; i++)_beginthreadex(NULL, 0, EchoThreadMain, (LPVOID)hComPort, 0, NULL);//创建=CPU个数的线程数hServSock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);//不是非阻塞套接字,但是重叠IO套接字。memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = htonl(INADDR_ANY);servAdr.sin_port = htons(1234);bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr));listen(hServSock, 5);while (1){SOCKET hClntSock;SOCKADDR_IN clntAdr;int addrLen = sizeof(clntAdr);hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &addrLen);handleInfo = (LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA));//和重叠IO一样handleInfo->hClntSock = hClntSock;//存储客户端套接字WaitForSingleObject(hMutex, INFINITE);//线程同步ALLCLIENT[clientcount++] = hClntSock;//存入套接字队列ReleaseMutex(hMutex);memcpy(&(handleInfo->clntAdr), &clntAdr, addrLen);CreateIoCompletionPort((HANDLE)hClntSock, hComPort, (DWORD)handleInfo, 0);//连接套接字和CP对象//已完成信息将写入CP对象ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));//存储接收到的信息memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));ioInfo->wsaBuf.len = BUF_SIZE;ioInfo->wsaBuf.buf = ioInfo->buffer;//和重叠IO一样ioInfo->rwMode = READ;//读写模式WSARecv(handleInfo->hClntSock, &(ioInfo->wsaBuf),//非阻塞模式1, &recvBytes, &flags, &(ioInfo->overlapped), NULL);}CloseHandle(hMutex);//销毁互斥量return 0;}unsigned int WINAPI EchoThreadMain(LPVOID pComPort)//线程的执行{HANDLE hComPort = (HANDLE)pComPort;SOCKET sock;DWORD bytesTrans;LPPER_HANDLE_DATA handleInfo;LPPER_IO_DATA ioInfo;DWORD flags = 0;while (1)//大循环{GetQueuedCompletionStatus(hComPort, &bytesTrans,//确认“已完成”的I/O!!(LPDWORD)&handleInfo, (LPOVERLAPPED*)&ioInfo, INFINITE);//INFINITE使用时,程序将阻塞,直到已完成的I/O信息写入CP对象sock = handleInfo->hClntSock;//客户端套接字if (ioInfo->rwMode == READ)//读写模式(此时缓冲区有数据){puts("message received!");if (bytesTrans == 0)    // 连接结束{WaitForSingleObject(hMutex, INFINITE);//线程同步closesocket(sock);int i = 0;while (ALLCLIENT[i] == sock){ i++; }ALLCLIENT[i] = 0;//断开置0ReleaseMutex(hMutex);free(handleInfo); free(ioInfo);continue;}int i = 0;for (; i < clientcount;i++){if (ALLCLIENT[i] != 0)//判断是否为已连接的套接字{if (ALLCLIENT[i] != sock){LPPER_IO_DATA newioInfo;newioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));//动态分配内存memset(&(newioInfo->overlapped), 0, sizeof(OVERLAPPED));strcpy(newioInfo->buffer, ioInfo->buffer);//重新构建新的内存,防止多次释放freenewioInfo->wsaBuf.buf = newioInfo->buffer;newioInfo->wsaBuf.len = bytesTrans;newioInfo->rwMode = WRITE;WSASend(ALLCLIENT[i], &(newioInfo->wsaBuf),//回声1, NULL, 0, &(newioInfo->overlapped), NULL);}else{memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));ioInfo->wsaBuf.len = bytesTrans;ioInfo->rwMode = WRITE;WSASend(ALLCLIENT[i], &(ioInfo->wsaBuf),//回声1, NULL, 0, &(ioInfo->overlapped), NULL);}}}ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));//动态分配内存memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));ioInfo->wsaBuf.len = BUF_SIZE;ioInfo->wsaBuf.buf = ioInfo->buffer;ioInfo->rwMode = READ;WSARecv(sock, &(ioInfo->wsaBuf),//再非阻塞式接收1, NULL, &flags, &(ioInfo->overlapped), NULL);}else{puts("message sent!");free(ioInfo);}}return 0;}void ErrorHandling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);}


2.客户端

#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdlib.h>#include <string.h>#include <windows.h>#include <process.h> #define BUF_SIZE 1000#define NAME_SIZE 20#pragma comment(lib, "ws2_32.lib")  //加载 ws2_32.dll  unsigned WINAPI SendMsg(void * arg);//发送信息函数unsigned WINAPI RecvMsg(void * arg);//接受信息函数void ErrorHandling(char * msg);//错误返回函数int haveread = 0;char NAME[50];//[名字]char ANAME[50];char msg[BUF_SIZE];//信息int main(int argc, char *argv[]){printf("请输入网名:");scanf("%s", NAME);WSADATA wsaData;SOCKET hSock;SOCKADDR_IN servAdr;HANDLE hSndThread, hRcvThread;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup() error!");hSock = socket(PF_INET, SOCK_STREAM, 0);memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = inet_addr("127.0.0.1");servAdr.sin_port = htons(1234);if (connect(hSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)ErrorHandling("connect() error");int resultsend;puts("Welcome to joining our chatting room!\n");sprintf(ANAME, "[%s]", NAME);hSndThread =(HANDLE)_beginthreadex(NULL, 0, SendMsg, (void*)&hSock, 0, NULL);//写线程hRcvThread =(HANDLE)_beginthreadex(NULL, 0, RecvMsg, (void*)&hSock, 0, NULL);//读线程WaitForSingleObject(hSndThread, INFINITE);//等待线程结束WaitForSingleObject(hRcvThread, INFINITE);closesocket(hSock);WSACleanup();system("pause");return 0;}unsigned WINAPI SendMsg(void * arg)   // send thread main{SOCKET sock = *((SOCKET*)arg);char name_msg[NAME_SIZE + BUF_SIZE];char padd[2];fgets(padd, 2, stdin);//多余的'\n'printf("\n send message:");while (1){{fgets(msg, BUF_SIZE, stdin);if (!strcmp(msg, "q\n") || !strcmp(msg, "Q\n")){closesocket(sock);exit(0);}sprintf(name_msg, "[%s] %s", NAME, msg);char numofmsg = strlen(name_msg) + '0';char newmsg[100]; newmsg[0] = numofmsg; newmsg[1] = 0;//第一个字符表示消息的长度strcat(newmsg, name_msg);int result = send(sock, newmsg, strlen(newmsg), 0);if (result == -1)return -1;//发送错误}}return NULL;}unsigned WINAPI RecvMsg(void * arg)  // read thread main{SOCKET sock = *((SOCKET*)arg);char name_msg[NAME_SIZE + BUF_SIZE];int str_len = 0;while (1){{char lyfstr[1000] = { 0 };int totalnum = 0;str_len = recv(sock, name_msg, 1, 0);//读取第一个字符!获取消息的长度if (str_len == -1)//读取错误{printf("return -1\n");return -1;}if (str_len == 0)//读取结束{printf("return 0\n");return 0;//读取结束}totalnum = name_msg[0] - '0';int count = 0;do{str_len = recv(sock, name_msg, 1, 0);name_msg[str_len] = 0;if (str_len == -1)//读取错误{printf("return -1\n");return -1;}if (str_len == 0){printf("return 0\n");return 0;//读取结束}strcat(lyfstr, name_msg);count = str_len + count;} while (count < totalnum);lyfstr[count] = '\0';printf("\n");strcat(lyfstr, "\n");fputs(lyfstr, stdout);printf(" send message:");fflush(stdout);memset(name_msg, 0, sizeof(char));}}return NULL;}void ErrorHandling(char * msg){fputs(msg, stderr);fputc('\n', stderr);exit(1);}


3.结果


4.不足

未考虑断开连接后的内存处理相关的问题,只考虑了正常连接时的聊天功能。

阅读全文
1 0