zoukankan      html  css  js  c++  java
  • winSockets编程(四)阻塞模式(服务端)

    在阻塞模式下,在I/O操作完成前,执行的操作函数将一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,套接字函数立即返回,而不管I/O是否完成。

    重点知识和思想:

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    1、不管阻塞和非阻塞模式,最关键的两个数据是通过accept()函数返回的客户端套接字客户端地址

    原因在于:

    客户端套接字相当于一个句柄(这样理解其实不正确,但是容易理解),也相当于独一无二的标识ID。只有通过这个客户端套接字标识,才知道向哪一个客户端返回数据。(比如,服务器收到N个客户端的连接请求的情况下...)。

    2、如何知道客户端关闭连接?

    通过recv()函数,如果返回为0,表示客户端关闭连接。

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    #生产者和消费者模式

    #完整代码#

    #include <iostream>
    #include <WINSOCK2.H>
    #pragma comment(lib, "wsock32.lib")
    
    #define SERVER_EXIT_OK              0        //服务器正常退出;
    #define SERVER_DLL_REEOR            1        //调用Windows sockets DLL失败;
    #define SERVER_API_ERROR            2        //调用Windows sockets API失败;
    #define SERVERPORT                  5555     //服务器TCP端口;
    
    char    bufRecv[MAX_PATH];                    //读缓冲区;
    char    bufSend[MAX_PATH];                    //写缓冲区;
    SOCKET  sServer;                              //服务器监听套接字;
    SOCKET  sClient;                              //接受客户端套接字;
    BOOL    bConning;                             //与客户端的连接状态;
    
    void    InitMember(void);                    //初始化成员变量; 
    int     ExitClient(int nExit);                //客户端退出;
    BOOL    RecvLine(SOCKET s, char* buf);        //读取一行数据;
    BOOL    SendLine(SOCKET s, char* buf);        //发送一行数据;
    int     HandleSocketError(char *str);        //对Windows sockets API调用错误处理;
    void    ShowSocketMsg(char* str);            //显示错误信息;
    
    int main(int argc, char* argv[])
    {
        InitMember();
        WSADATA    wsaData;                //Windows sockets DLL版本信息
        int        retVal;                    //调用Windows sockets API返回值        
        retVal = WSAStartup(MAKEWORD(1, 1), &wsaData);
        if (0 != retVal)
        {
            ShowSocketMsg("找不到可用的Socket DLL!");
            return SERVER_DLL_REEOR;
        }
        if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
        {
            ShowSocketMsg("Can not find a usable Windows Sockets dll!");
            WSACleanup();
            return SERVER_DLL_REEOR;
        }
    
        //创建套接字;
        /*******************************************************/
        sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (INVALID_SOCKET == sServer)
        {
            return HandleSocketError("Failed socket()!");
        }
        /********************************************************/
    
        //服务器套接字地址;
        SOCKADDR_IN addrServ;
        addrServ.sin_family = AF_INET;
        addrServ.sin_port = htons(SERVERPORT);
        addrServ.sin_addr.s_addr = INADDR_ANY;
    
        retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));//绑定套接字;
    
        if (SOCKET_ERROR == retVal)
        {
            closesocket(sServer);
            return HandleSocketError("Failed bind()!");
        }
    
        retVal = listen(sServer, 1);//开始监听; 
        if (SOCKET_ERROR == retVal)
        {
            closesocket(sServer);
            return HandleSocketError("Failed listen()!");
        }
            
        std::cout << "Server succeeded!" <<std::endl;//等待客户端的连接;
        std::cout << "Waiting for new clients..." <<std::endl;
    
        /***********接受客户端请求****************************/
        sockaddr_in addrClient;
        int addrClientlen = sizeof(addrClient);
        sClient = accept(sServer, (sockaddr FAR*)&addrClient, &addrClientlen);  //阻塞位置
        if (INVALID_SOCKET == sClient)
        {
            closesocket(sServer);
            return HandleSocketError("Failed accept()!");
        }
        else
        {
            bConning = TRUE;//客户端请求成功;
        }
    
        //显示客户端的IP和端口;
        char *pClientIP = inet_ntoa(addrClient.sin_addr);
        u_short  clientPort = ntohs(addrClient.sin_port);
        std::cout << "Accept a client." <<std::endl;
        std::cout << "IP: " << pClientIP <<std::endl;
        std::cout << "Port: " << clientPort <<std::endl;
        
        if (!RecvLine(sClient, bufRecv))//接收客户端数据;
        {
            return    ExitClient(SERVER_API_ERROR);
        }
        
        std::cout << bufRecv <<std::endl;//显示客户端数据;
        
        strcpy_s(bufSend, "Hello,Client!
    ");//向客户端发送数据;
        if (!SendLine(sClient, bufSend))
        {
            return    ExitClient(SERVER_API_ERROR);
        }
        std::cout << "Server exiting..." <<std::endl;//显示退出信息;
        return ExitClient(SERVER_EXIT_OK);
    }
    
    void InitMember(void)
    {    
        memset(bufRecv, 0, MAX_PATH);
        memset(bufSend, 0, MAX_PATH);
        sServer = INVALID_SOCKET;
        sClient = INVALID_SOCKET;    
        bConning = FALSE;
    }
    
    int ExitClient(int nExit)
    {
        closesocket(sServer);
        closesocket(sClient);
        WSACleanup();
        return nExit;
    }
    
    BOOL RecvLine(SOCKET s, char* buf)
    {
        BOOL    retVal = TRUE;            //返回值
        BOOL    bLineEnd = FALSE;        //行结束
        int        nReadLen = 0;            //读入字节数
        int        nDataLen = 0;            //数据长度
        while (!bLineEnd && bConning)    //与客户端连接 没有换行
        {
            nReadLen = recv(s, buf + nDataLen, 1, 0); //阻塞位置
            if (SOCKET_ERROR == nReadLen)
            {
                int nErrCode = WSAGetLastError();//错误代码
                if (WSAENOTCONN == nErrCode)
                {
                    ShowSocketMsg("The socket is not connected!");
    
                }
                else if (WSAESHUTDOWN == nErrCode)
                {
                    ShowSocketMsg("The socket has been shut down!");
    
                }
                else if (WSAETIMEDOUT == nErrCode)
                {
                    ShowSocketMsg("The connection has been dropped!");
                }
                else if (WSAECONNRESET == nErrCode)
                {
                    ShowSocketMsg("The virtual circuit was reset by the remote side!");
                }
                else
                {
                }
    
                retVal = FALSE;    //读数据失败
                break;            //跳出循环                        
            }
    
    
            if (0 == nReadLen)//客户端关闭
            {
                retVal = FALSE;    //读数据失败
                break;            //跳出循环            
            }
    
            //读入数据
            if ('
    ' == *(buf + nDataLen))    //换行符
            {
                bLineEnd = TRUE;            //接收数据结束
            }
            else
            {
                nDataLen += nReadLen;        //增加数据长度
            }
        }
    
        return retVal;
    }
    
    BOOL SendLine(SOCKET s, char* str)
    {
        int retVal;//返回值
        retVal = send(s, str, strlen(str), 0);  //阻塞位置
    //错误处理 if (SOCKET_ERROR == retVal) { int nErrCode = WSAGetLastError();//错误代码 if (WSAENOTCONN == nErrCode) { ShowSocketMsg("The socket is not connected!"); } else if (WSAESHUTDOWN == nErrCode) { ShowSocketMsg("The socket has been shut down!"); } else if (WSAETIMEDOUT == nErrCode) { ShowSocketMsg("The connection has been dropped!"); } else { } return FALSE; //发送失败 } return TRUE; //发送成功 } int HandleSocketError(char *str) { ShowSocketMsg(str); //显示错误消息 WSACleanup(); //卸载Windows socket DLL return SERVER_API_ERROR;//退出应用程序 } void ShowSocketMsg(char* str) { MessageBox(NULL, str, "SERVER ERROR", MB_OK); }
  • 相关阅读:
    C++ 容器元素的存储和获取
    【C++沉思录】代理类
    mysql 编码测试
    理解字符编码
    linux mount
    mysql delimiter
    mysql 求时间段平均值
    mysql Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
    PostgreSQL体系架构与内存结构
    PostgreSQL中的The Oversized-Attribute Storage Technique(TOAST:超大属性存储技术)
  • 原文地址:https://www.cnblogs.com/tinaluo/p/7691363.html
Copyright © 2011-2022 走看看