网络编程基础第五讲非阻塞模型

2019-05-25 16:46:48 浏览数 (1)

一丶简介

    通过上一讲.我们了解到了阻塞模式. recv/send IO操作不完成.不会进行返回.迭代模式就是只服务一个连接.对这个连接进行读写.

    非阻塞模式就是 IO没有完成.可以立即进行返回.

    我们可以通过方法 ioctlsocket进行设置为非阻塞

例子:

代码语言:javascript复制
int iMode = 1; //为1表示非阻塞. 为0 表示阻塞.

int nRet = ioctlsocket(SOCKET, FIONBIO,(u_long *)&iMode);

示例代码:

代码语言:javascript复制
#include <winsock2.h>
#include <stdio.h>

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

void main()
{
//-------------------------
// Initialize Winsock
WSADATA wsaData;
int iResult;
u_long iMode = 0;

iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != NO_ERROR)
  printf("Error at WSAStartup()n");

//-------------------------
// Create a SOCKET object.
SOCKET m_socket;
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_socket == INVALID_SOCKET) {
  printf("Error at socket(): %ldn", WSAGetLastError());
  WSACleanup();
  return;
}

//-------------------------
// Set the socket I/O mode: In this case FIONBIO
// enables or disables the blocking mode for the 
// socket based on the numerical value of iMode.
// If iMode = 0, blocking is enabled; 
// If iMode != 0, non-blocking mode is enabled.

iResult = ioctlsocket(m_socket, FIONBIO, &iMode);
if (iResult != NO_ERROR)
  printf("ioctlsocket failed with error: %ldn", iResult);
  

}

二丶完整代码解析

  非阻塞模式就是直接数据返回. 所以我们跟阻塞是的代码不一样的地方就是判断返回值. 因为返回值是立即返回.所以要判断数据是否接受到.连接是否接受到.这个是最大的区别.

代码语言:javascript复制
// Server.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")



int main()
{
   
        WSADATA wsaData;
        int iResult;
        u_long iMode = 0;

        iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (iResult != NO_ERROR)
            printf("Error at WSAStartup()n");

        //-------------------------
        // Create a SOCKET object.
        SOCKET m_socket;
        m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (m_socket == INVALID_SOCKET) {
            printf("Error at socket(): %ldn", WSAGetLastError());
            WSACleanup();
            return 0;
        }

        //设置套接字为非阻塞模式
        iResult = ioctlsocket(m_socket, FIONBIO, &iMode);
        if (iResult != NO_ERROR)
            printf("ioctlsocket failed with error: %ldn", iResult);

        //主要是进行连接的时候.代码不一样了.因为是非阻塞.所以返回的错误是资源暂时不可用.
        sockaddr_in hClientAddr;
        int nAddrSize = sizeof(hClientAddr);
        SOCKET hClientSocket;
        while (true)
        {
            hClientSocket = accept(m_socket,(sockaddr *) &hClientAddr, &nAddrSize);
            if (INVALID_SOCKET == hClientSocket) //因为是立即返回.所以错误会是INVALID_SOCKET 所以我们要进行错误码判断.
            {
                
                int iCode = GetLastError();     //使用那个都可以
                int nCode = WSAGetLastError();
                if (nCode == WSAEWOULDBLOCK) //资源暂时不可用.所以延迟继续进行连接 具体查询WSALastError()关于套接字的返回错误.
                {

                    Sleep(100);
                    continue;
                }
                else
                {
                    printf("延迟连接出错rn");
                    closesocket(m_socket);
                    WSACleanup();
                    break; //否则错误.
                }
            }
        }

        //recv一样会返回.
        char szRecvBuffer[0x1000] = { 0 };
        while (true)
        {
            RtlZeroMemory(szRecvBuffer, sizeof(szRecvBuffer));  //清空缓冲区.
            if (SOCKET_ERROR == recv(hClientSocket,szRecvBuffer,0x1000,0))//非阻塞一样判断
            {
                int iErrCode = GetLastError();
                if (iErrCode == WSAEWOULDBLOCK)
                {
                    /*
                    暂时无法接受.延迟继续接受.
                    注意这一次接受的数据我们需要保存起来.下次接受还要保存.最后判断数据是否完整.
                    非阻塞就是这样做的.所以具体写法自己实现
                    */
                    Sleep(100);
                    continue;
                    
                }
                else if (iErrCode == WSAETIMEDOUT || iErrCode == WSAENETDOWN)
                {
                    //超时跟网络中断.打印输出并且释放资源
                    printf("网络中断.或者接受数据超时rn");
                    closesocket(hClientSocket);
                    closesocket(m_socket);
                    WSACleanup();
                    break;
                }

            }   
            //成功接受.打印出来.
            szRecvBuffer[0x1000] = ''; //加个结尾.
            printf(szRecvBuffer);
        }
    return -1;
}

0 人点赞