非阻塞socket学习,select基本用法

server

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

#pragma comment(lib, "WS2_32.lib")

#define PORT 9999
#define DATA_BUFSIZE 8192

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

DWORD TotalSockets = 0;
LPSOCKET_INFORMATION SocketArray[FD_SETSIZE];

bool CreateSocketInformation(SOCKET s)
{
    LPSOCKET_INFORMATION SI;    //用于保存套接字的信息
    //为SI分配内存空间
    if ((SI = (LPSOCKET_INFORMATION)GlobalAlloc(GPTR, sizeof SOCKET_INFOMATION)) == NULL)
    {
        printf("GlobalAlloc()   failed   with   error   %d\n", GetLastError());
        return   FALSE;
    }

    //初始化SI的值
    SI->Socket = s;
    SI->BytesSEND = 0;
    SI->BytesRECV = 0;

    SocketArray[TotalSockets] = SI;
    TotalSockets++;

    return true;
}

void FreeSocketInformation(DWORD Index)
{
    LPSOCKET_INFORMATION SI = SocketArray[Index];
    DWORD i;

    closesocket(SI->Socket);
    GlobalFree(SI);
    if (Index != (TotalSockets - 1))
    {
        for (i = Index; i < TotalSockets; i++)
        {
            SocketArray[i] = SocketArray[i + 1];
        }
    }

    TotalSockets--;
}

int main()
{
    SOCKET ListenSocket;        //监听套接字
    SOCKET AcceptSocket;        //与客户端通信的套接字
    SOCKADDR_IN InternetAddr;    //服务器地址
    WSADATA wsaData;
    int Ret;

    FD_SET WriteSet;            //获取可写套接字集合
    FD_SET ReadSet;                //获取可读性套接字集合
    DWORD Total = 0;            //处于就绪状态的套接字
    DWORD SendBytes;            //发送套接字
    DWORD RecvBytes;            //接收的套接字

    //初始化WinSock环境
    if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData) != 0))
    {
        printf("WSAStartup()   failed   with   error   %d\n", Ret);
        WSACleanup();
        return -1;
    }

    //创建用于监听的套接字
    if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 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_un.S_addr = htonl(INADDR_ANY);
    InternetAddr.sin_port = htons(PORT);

    //绑定监听套接字到本地地址和端口
    if (bind(ListenSocket, (PSOCKADDR)&InternetAddr, sizeof InternetAddr) == INVALID_SOCKET)
    {
        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))
    {
        printf("ioctlsocket() failed with error %d\n", WSAGetLastError());
        return -1;
    }

    CreateSocketInformation(ListenSocket);//ListenSocket套接字创建对应的SOCKET_INFORMATION,把ListenSocket添加到SocketArray数组中

    while (true)
    {
        FD_ZERO(&ReadSet);//读套接字清零
        FD_ZERO(&WriteSet);//写套接字清零

        FD_SET(ListenSocket, &ReadSet);//向ReadSet中添加监听套接字ListenSocket

        for (DWORD i = 0; i < TotalSockets; ++i)
        {
            LPSOCKET_INFORMATION SocketInfo = SocketArray[i];
            FD_SET(SocketInfo->Socket, &ReadSet);
            FD_SET(SocketInfo->Socket, &WriteSet);
        }

        if((Total = select(0, &ReadSet, &WriteSet, NULL, NULL)) == SOCKET_ERROR){//刚开始为什么监听套接字没有可写返回,而与客户端连接的套接字总是可写???
            printf("select()   returned   with   error   %d\n",   WSAGetLastError());   
            return -1;  
        }

        for (DWORD i = 0; i < TotalSockets; i++)
        {
            LPSOCKET_INFORMATION SocketInfo = SocketArray[i];//SocketArray保存了所有监听的可读和可写的套接字
            if (FD_ISSET(SocketInfo->Socket, &ReadSet))//判断套接字是否可读
            {
                if (SocketInfo->Socket == ListenSocket)//对于监听套接字表示有新的客户端连接
                {
                    Total--;
                    if ((AcceptSocket = accept(ListenSocket, NULL, NULL)) != INVALID_SOCKET)
                    {
                        NonBlock = 1;
                        if (ioctlsocket(AcceptSocket, FIONBIO, &NonBlock) == SOCKET_ERROR)//设置AcceptSocket套接字为非阻塞套接字,这样与客户端通信就不会阻塞了
                        {
                            printf("ioctlsocket()   failed   with   error   %d\n",   WSAGetLastError());   
                            return -1;
                        }

                        if (CreateSocketInformation(AcceptSocket) == false)//将AcceptSocket添加到SocketArray数组中
                        {
                            return -1;
                        }
                    }
                    else
                    {
                        if (WSAGetLastError() != WSAEWOULDBLOCK)
                        {
                            printf("accept()   failed   with   error   %d\n",   WSAGetLastError());   
                            return -1;
                        }
                    }
                }
                else//接收数据
                {
                    Total--;
                    memset(SocketInfo->Buffer, 0, sizeof SocketInfo->Buffer);
                    SocketInfo->DataBuf.buf = SocketInfo->Buffer;
                    SocketInfo->DataBuf.len = DATA_BUFSIZE;

                    DWORD Flags = 0;
                    if (WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &RecvBytes, &Flags, NULL, NULL) == SOCKET_ERROR)
                    {
                        if (WSAGetLastError() != WSAEWOULDBLOCK)
                        {
                            printf("WSARecv()   failed   with   error   %d\n",   WSAGetLastError()); 
                            FreeSocketInformation(i);
                        }
                        continue;
                    }
                    else
                    {
                        SocketInfo->BytesRECV = RecvBytes;
                        SocketInfo->DataBuf.buf[RecvBytes] = '\0';
                        if (RecvBytes == 0)
                        {
                            FreeSocketInformation(i);
                            continue;
                        }
                        else
                        {
                            std::cout << SocketInfo->DataBuf.buf << std::endl;// 如果成功接收数据,则打印收到的数据
                        }
                    }
                }
            }
            else
            {
                if (FD_ISSET(SocketInfo->Socket, &WriteSet))//可写会一直被调用,判断大于其写缓冲的最低水位
                {
                    Total--;
                    SocketInfo->DataBuf.buf = SocketInfo->Buffer + SocketInfo->BytesSEND;// 初始化缓冲区位置
                    SocketInfo->DataBuf.len = SocketInfo->BytesRECV - SocketInfo->BytesSEND;// 初始化缓冲区长度

                    if (SocketInfo->DataBuf.len > 0)// 如果有需要发送的数据,则发送数据
                    {
                        if (WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0, NULL, NULL) == SOCKET_ERROR)
                        {
                            if (WSAEWOULDBLOCK != WSAGetLastError())
                            {
                                printf("WSASend()   failed   with   error   %d\n", WSAGetLastError()); 
                                FreeSocketInformation(i);
                            }
                            continue;
                        }
                        else
                        {
                            SocketInfo->BytesSEND += SendBytes;        //记录发送数据的字节数目
                            if (SocketInfo->BytesSEND == SocketInfo->BytesRECV)
                            {
                                SocketInfo->BytesSEND = 0;
                                SocketInfo->BytesRECV = 0;
                            }
                        }
                    }
                }
            }
        }
    }
    system("pause");
    return 0;
    
}

client

#include <winsock.h>
#include <iostream>
#define BUFSIZE 64
#define PORT 9999

#pragma comment(lib, "WS2_32.lib")
int main()
{
    WSADATA wsaData;
    SOCKET sClient;

    sockaddr_in addrServ;
    char buf[BUFSIZE];
    int retVal;

    if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
    {
        std::cout << "WSAStartup失败!" << std::endl;
        return -1;
    }

    sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (INVALID_SOCKET == sClient)
    {
        std::cout << "socket() 错误!" << std::endl;
        WSACleanup();
        return -1;
    }

    addrServ.sin_family = AF_INET;
    addrServ.sin_port = htons(PORT);
    addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    retVal = connect(sClient, (LPSOCKADDR)&addrServ, sizeof(addrServ));
    if (SOCKET_ERROR == retVal)
    {
        std::cout << "connect 错误!" << std::endl;
        closesocket(sClient);
        WSACleanup();
        return -1;
    }

    while(true)
    {
        std::cout << "输入要发给服务器的内容:" << std::endl;
        char msg[BUFSIZE];
        std::cin.getline(msg, BUFSIZE);
        ZeroMemory(buf, BUFSIZE);
        strcpy(buf, msg);
        retVal = send(sClient, buf, strlen(buf), 0);
        if (SOCKET_ERROR == retVal)
        {
            std::cout << "发送失败" << std::endl;
            closesocket(sClient);
            WSACleanup();
            return -1;
        }

        retVal = recv(sClient, buf, sizeof buf, 0);
        std::cout << "从服务器端接收:" << buf << std::endl;
        if (strcmp(buf, "quit") == 0)
        {
            std::cout << "quit" << std::endl;
            break;
        }
    }
    closesocket(sClient);
    WSACleanup();
    return 0;
}

 

posted @ 2016-01-23 19:57  zzyoucan  阅读(609)  评论(0编辑  收藏  举报