windows下基于异步通知IO模型的回声服务器和客户端的实现

cuiweisaidelike 2020-05-11

1. 利用异步io通知模型实现回声服务器端

#include <stdio.h>
#include <string.h>
#include <winsock2.h>

#define BUF_SIZE 100

void CompressSockets(SOCKET hSockArr[], int idx, int total);
void CompressEvents(WSAEVENT hEventArr[], int idx, int total);
void ErrorHandling(char *msg);

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    SOCKET hServSock, hClntSock;
    SOCKADDR_IN servAdr, clntAdr;

    SOCKET hSockArr[WSA_MAXIMUM_WAIT_EVENTS]; 
    WSAEVENT hEventArr[WSA_MAXIMUM_WAIT_EVENTS];
    WSAEVENT newEvent;
    WSANETWORKEVENTS netEvents;    //保存发生的事件类型信息和错误信息的结构体变量

    int numOfClntSock=0;
    int strLen, i;
    int posInfo, startIdx;
    int clntAdrLen;
    char msg[BUF_SIZE];
    
    if(argc!=2) {
        printf("Usage: %s <port>\n", argv[0]);
        exit(1);
    }
    if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        ErrorHandling("WSAStartup() error!");

    hServSock=socket(PF_INET, SOCK_STREAM, 0);
    memset(&servAdr, 0, sizeof(servAdr));
    servAdr.sin_family=AF_INET;
    servAdr.sin_addr.s_addr=htonl(INADDR_ANY);
    servAdr.sin_port=htons(atoi(argv[1]));

    if(bind(hServSock, (SOCKADDR*) &servAdr, sizeof(servAdr))==SOCKET_ERROR)
        ErrorHandling("bind() error");

    if(listen(hServSock, 5)==SOCKET_ERROR)
        ErrorHandling("listen() error");

    newEvent=WSACreateEvent();    //创建manual_reset模式non-signaled状态的事件对象
    /* 指定hServSock套接字句柄为newEvent的监视对象,希望监视的事件类型为: 有新的连接请求 */
    if(WSAEventSelect(hServSock, newEvent, FD_ACCEPT)==SOCKET_ERROR)
        ErrorHandling("WSAEventSelect() error");

    hSockArr[numOfClntSock]=hServSock;
    hEventArr[numOfClntSock]=newEvent;
    numOfClntSock++;

    while(1)
    {
        /* 验证是否发生事件,成功时返回发生事件的对象信息,只要有一个事件对象的状态变为signaled时就返回
         * 可通过以宏的方式申明的WSA_MAXIMUN_WAIT_EVENTS常量得知WSAWaitMulltipleEvents函数可以同时监听的最大事件对象数,该常量为64
         * 通过该函数可以得到转为signaled状态的事件对象中的第一个(按数组中的保存顺序)索引值, 返回值减去常量WSA_WAIT_EVENT_0,可以得到
         * 转变为signaled状态的事件对象句柄
         */
        posInfo=WSAWaitForMultipleEvents(
            numOfClntSock, hEventArr, FALSE, WSA_INFINITE, FALSE);
        startIdx=posInfo-WSA_WAIT_EVENT_0;
        /* 获取所有signaled状态的事件对象 */
        for(i=startIdx; i<numOfClntSock; i++)
        {
            int sigEventIdx=
                WSAWaitForMultipleEvents(1, &hEventArr[i], TRUE, 0, FALSE);
            if((sigEventIdx==WSA_WAIT_FAILED || sigEventIdx==WSA_WAIT_TIMEOUT))
            {
                continue;
            }
            else
            {    /* 确定与hSockArr[sigEventIdx]套接字句柄相连接的事件对象hEventArr[sigEventIdx]转变为signaled状态的原因,
                  * 发生的事件类型信息和错误信息保存在netEvents结构体变量中
                  */
                sigEventIdx=i;
                WSAEnumNetworkEvents(
                    hSockArr[sigEventIdx], hEventArr[sigEventIdx], &netEvents);
                if(netEvents.lNetworkEvents & FD_ACCEPT)
                {    /* 发生连接请求事件 */
                    if(netEvents.iErrorCode[FD_ACCEPT_BIT]!=0)
                    {
                        puts("Accept Error");
                        break;
                    }
                    /* 接受连接请求 */
                    clntAdrLen=sizeof(clntAdr);
                    hClntSock=accept(
                        hSockArr[sigEventIdx], (SOCKADDR*)&clntAdr, &clntAdrLen);
                    /* 指定hClntSock套接字句柄为newEvent的监视对象,希望监视的事件类型为: 有需要接收的数据和断开连接请求 */
                    newEvent=WSACreateEvent();
                    WSAEventSelect(hClntSock, newEvent, FD_READ|FD_CLOSE);

                    hEventArr[numOfClntSock]=newEvent;
                    hSockArr[numOfClntSock]=hClntSock;
                    numOfClntSock++;
                    puts("connected new client...");
                }

                if(netEvents.lNetworkEvents & FD_READ)
                {    /* 发生有需要接收的数据事件 */
                    if(netEvents.iErrorCode[FD_READ_BIT]!=0)
                    {
                        puts("Read Error");
                        break;
                    }
                    strLen=recv(hSockArr[sigEventIdx], msg, sizeof(msg), 0);
                    send(hSockArr[sigEventIdx], msg, strLen, 0);
                }

                if(netEvents.lNetworkEvents & FD_CLOSE)
                {
                    if(netEvents.iErrorCode[FD_CLOSE_BIT]!=0)    
                    {
                        puts("Close Error");
                        break;
                    }
                    WSACloseEvent(hEventArr[sigEventIdx]);
                    closesocket(hSockArr[sigEventIdx]);
                    
                    numOfClntSock--;
                    CompressSockets(hSockArr, sigEventIdx, numOfClntSock);
                    CompressEvents(hEventArr, sigEventIdx, numOfClntSock);
                }
            }
        }
    }
    WSACleanup();
    return 0;
}

void CompressSockets(SOCKET hSockArr[], int idx, int total)
{
    int i;
    for(i=idx; i<total; i++)
        hSockArr[i]=hSockArr[i+1];
}
void CompressEvents(WSAEVENT hEventArr[], int idx, int total)
{
    int i;
    for(i=idx; i<total; i++)
        hEventArr[i]=hEventArr[i+1];
}
void ErrorHandling(char *msg)
{    
    fputs(msg, stderr);
    fputc(‘\n‘, stderr);
    exit(1);
}

2.回声客户端实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>

#define BUF_SIZE 1024
void ErrorHandling(char *message);

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    SOCKET hSocket;
    char message[BUF_SIZE];
    int strLen;
    SOCKADDR_IN servAdr;

    if(argc!=3) {
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1);
    }

    if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
        ErrorHandling("WSAStartup() error!"); 

    hSocket=socket(PF_INET, SOCK_STREAM, 0);   
    if(hSocket==INVALID_SOCKET)
        ErrorHandling("socket() error");
    
    memset(&servAdr, 0, sizeof(servAdr));
    servAdr.sin_family=AF_INET;
    servAdr.sin_addr.s_addr=inet_addr(argv[1]);
    servAdr.sin_port=htons(atoi(argv[2]));
    
    if(connect(hSocket, (SOCKADDR*)&servAdr, sizeof(servAdr))==SOCKET_ERROR)
        ErrorHandling("connect() error!");
    else
        puts("Connected...........");
    
    while(1) 
    {
        fputs("Input message(Q to quit): ", stdout);
        fgets(message, BUF_SIZE, stdin);
        
        if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
            break;

        send(hSocket, message, strlen(message), 0);
        strLen=recv(hSocket, message, BUF_SIZE-1, 0);
        message[strLen]=0;
        printf("Message from server: %s", message);
    }
    
    closesocket(hSocket);
    WSACleanup();
    return 0;
}

void ErrorHandling(char *message)
{
    fputs(message, stderr);
    fputc(‘\n‘, stderr);
    exit(1);
}

相关推荐