基于Select模型的服务器

1. 结构体SOCKET_INFORMATION

    结构体SOCKET_INFORMATION用于记录服务器与每个客户端之间进行通信的信息,代码如下:

typedef struct _SOCKET_INFORMATION{
    CHAR Buffer[DATA_BUFSIZE];//发送和接收数据的缓冲区
    WSABUF  DataBuf; //定义发送和接受数据缓冲区的结构体,包括长度和内容
    SOCKET   Socket; //与客户端进行通信的socket
    DWORD   BytesSEND; //保存Socket发送的字节数
    DWORD   BytesRECV; //保存Socket接收的字节数
}SOCKET_INFORMATION, *LPSOCKET_INFORMATION;

  结构体SOCKET_INFORMATION中包含用于通信的Socket、发送和接收数据的缓冲区、发送和接收的字节数等。

    WSABUF是winsock2.h中定义的结构体, 用于保存缓冲区地址和长度,代码如下:

typedef struct _WSABUF{
    u_long  len;  //缓冲区的长度
    char FAR* buf; //指向缓冲区的指针
}

  2. 定义变量

    为了对所有与客户端进行通信的Socket信息进行统一管理,需要定义下面两个变量:

    //记录正在使用的套接字总数量

    DWORD TotalSockets = 0;

    //保存Socket信息对象的数组, FD_SETSIZE表示SELECT模型中允许的最大的Socket数量

    LPSOCKET_INFORMATION  SocketArray[FD_SETSIZE];

    3. 创建Socket信息

    调用CreateSocketInformation()函数可以为指定的Socket创建对应的SOCKET_INFORMATION结构体, 并将其添加到SocketArray数组中, 代码如下:

BOOL CreateSocketInformation(SOCKET s){
    LPSOCKET_INFORMATION SI;
    //为SI分配内存空间
    if((SI = (LPSOCKET_INFORMATION)GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION)) == NULL){
        printf("GlobalAlloc() failed with error %d\n", GetLastError());
        return FALSE;   
    }
    //初始化SI的值
    SI->Socket = s;
    SI->BytesSEND = 0;
    SI->BytesRECV = 0;
    //在SocketArray数组中添加一个新的元素, 用户保存SI对象
    SocketArray[TotalSockets++] = SI;
    return TRUE;
}

    4. 释放Socket信息

    调用FreeSocketInformation()函数可以删除指定的Socket对应的SOCKET_INFORMATION结构体, 并关闭该Socket, 代码如下:

    

void FreeSocketInformation(DWORD Index){
        //获取指定索引对应的LPSOCKET_INFORMATION
        LPSOCKET_INFORMATION SI = SocketArray[Index];
        DWORD i;
        closesocket(SI->Socket); //关闭Socket
        //释放指定的LPSOCKET_INFORMATION资源
        GlobalFree(SI);
        //将数组中的Index索引后面的元素前移
        for(i = Index; i < TotalSockets; i++){
            SocketArray[i] = SocketArray[i+1];
        }
        TotalSockets--;
}

   5. 主函数的变量定义

      

int _tmain(int argc, _TCHAR* argv[]){
    SOCKET           ListenSocket;             //监听socket
    SOCKET           AcceptSOcket;            //与客户端进行通信的socket
    SOCKADDR_IN InternetAddr;            //服务器地址
    WSADATA        wsaData;                   //用户初始化Socket环境
    INT                  Ret;                            //Winsock API的返回值
    FD_SET           WriteSet;                    //获取可写性Socket集合
    FD_SET           ReadSet;                     //获取可读性Socket集合
    DWORD          Total = 0;                     //处于就绪状态的Socket数量
    DWORD          SendBytes;                  //发送字节数
    DWORD          RecvBytes;                   //接收字节数
}

   6. 在主函数中启动监听客户端的连接请求

    主函数首先初始化Windows Sockets环境,并启动监听客户端的连接请求, 代码如下:

int _tmain(int argc, _TCHAR* argv[]){
    ....
    //初始化WinSock环境
    if((Ret = WSAStartup(0x0202, &wsaData)) != 0){
        printf("WSAStartup() failed with error %d\n", Ret);
        WSACleanup();
        reutrn -1;
    }
    //创建用于监听的Socket
    if((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET){
        printf("WSASocket() failed with error %d\n", WSAGetLastError());
        return -1;
    }
    //设置监听地址和端口号
    InternetAddr.sin_family = AF_INET;
    InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    InternetAddr.sin_port = htons(PORT);
    //绑定监听Socket到本地地址和端口
    if(bind(ListenSocket, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR){
        printf("bind() failed with error %d\n", WSAGetLastError());
        return -1;
    }
    //开始监听
    if(listen(ListenSocket, 5)){
        printf("listen() failed with error %d\n", WSAGetLastError());
        return -1;
    }
    //设置为非阻塞模式
    ULONG NonBlock = 1;
    if(ioctlsocket(ListenSocket, FIONBIO, &NonBlock) == SOCKET_ERROR){
        printf("ioctlsocket() failed with error %d\n", WSAGetLastError());
        return -1;
    }
    //为ListenSocket Socket创建对应的SOCKET_INFORMATION
    //这样就可以把ListenSocket添加到SocketArray数组中
    CreateSocketInformation(ListenSocket);
    ...
}

     7. 获取就绪状态的套接字

     

posted on 2014-07-09 17:38  冰尨  阅读(317)  评论(0)    收藏  举报

导航