虚位以待(AD)
虚位以待(AD)
首页 > 操作系统 > Windows系列 > windows网络编程(九)——IOCP+多线程实现简单的聊天(windows服务器端 windows客户端

windows网络编程(九)——IOCP+多线程实现简单的聊天(windows服务器端 windows客户端
类别:Windows系列   作者:码皇   来源:<a href="http://blog.csdn.net/wxf2012301351" target="_blank" rel="nofoll   点击:

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

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 READ 3#define WRITE 5typedef 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);
    //创建互斥量 WSADATA wsaData;
    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;
    //断开置0 ReleaseMutex(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);
    //重新构建新的内存,防止多次释放free newioInfo->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);
    }
    </windows.h></winsock2.h></process.h></stdlib.h></stdio.h>

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, "qn") || !strcmp(msg, "Qn")) {
    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 -1n");
    return -1;
    }
    if (str_len == 0)//读取结束 {
    printf("return 0n");
    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 -1n");
    return -1;
    }
    if (str_len == 0) {
    printf("return 0n");
    return 0;
    //读取结束 }
    strcat(lyfstr, name_msg);
    count = str_len + count;
    }
    while (count < totalnum);
    lyfstr[count] = '
    '
    ;
    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);
    }
    </process.h></windows.h></string.h></stdlib.h></stdio.h>

3.结果

4.不足

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

相关热词搜索: