socket阻塞与非阻塞读写

网络协议一般都是由head和body构成。

socket在实际应用中有2种方式,阻塞和非阻塞。

使用setsockopt()可以在2种方式之间切换。

/**
*
* 设置非阻塞模式(for Win32)
* 
**/
int nNonBlocking = 1;
ioctlsocket(sockListen,FIONBIO,&nNonBlocking);


先看阻塞模式的读写

/**
*
* 功能 - 阻塞模式读取指定字节协议内容
* 
* 参数 - sockfd, 套接字句柄
*      - pBuffer, 接收缓冲区
*      - nBufferLen, 接收缓冲区大小
*      - nBytesToRead, 需要读取的大小
*
* 返回值
* true  - 成功读取nBytesToRead个字节
* false - 发生错误(SOCKET_ERROR), 连接关闭(0)
*
**/
bool ReadMessage(int sockfd  
                , char * pBuffer  
                , size_t nBufferLen  
                , size_t nBytesToRead)  
{  
      int nRead = 0, nFinished = 0;  
      while (nFinished < nBytesToRead)  
      {  
            if (0 >= (nRead = recv(sockfd  
                                  , pBuffer + nFinished  
                                  , nBytesToRead - nFinished  
                                  , 0)))  
                 break;  
            nFinished += nRead;  
      }  
      if (nFinished == nBytesToRead)  
      {  
            return true;  
      }  
      return false;  
}  

非阻塞模式:

先看一个辅助函数, 检测缓冲区是否就绪

/**
*
* 功能 - 测试指定socket的数据是否可读
* 
* 参数 - hSocket, 需要测试的socket句柄
*
* 返回值
* -1 SOCKET_ERROR
* 0  BUSY
* 1  READY
**/
int IsRecvBufReady(SOCKET hSocket)
{
	fd_set sockSet; 
	FD_ZERO(&sockSet);
	FD_SET(hSocket,&sockSet);

	struct timeval interval;

	interval.tv_sec = 0;
	interval.tv_usec = 0;

	int nPending = select(0   // 忽略 
			, sockSet // 可读
			, NULL    // 可写
			, NULL    // 错误
			, &interval); // 超时
	return nPending;
}


/**
*
* 功能 - 读取指定字节
*
* 参数 - hSocket, socket句柄
*      - pBuffer, 接收缓冲区
*      - nBufferLen, 缓冲区大小
*      - nBytesToRead, 需要读取的指定大小
*
* 返回值
* -1  发生错误
* 1   成功
* 0   读取的字节数小于nBytesToRead, 下次读取
**/
int ReadMessage(SOCKET hSocket
               , char * pBuffer
               , size_t nBufferLen
               , size_t nBytesToRead)
{
    int nStatus = -1;
    size_t nRead = 0, nFinished = 0;
    while (0 > (nStatus = IsRecvBufReady(hSocket)))
    {
        if (0 >= (nRead = recv(hSocket
                              , pBuffer + nFinished
                              , nBytesToRead - nFinished
                              , 0)))
            return -1;
            
        nFinished += nRead;
        
        if (nFinished == nBytesToRead)
            return 1;
    }
    
    return nStatus;
}


常用的socket属性设置setsockopt

/**
*
* 开启NOLINGER配置
* 立即关闭socket
* 不等待缓冲区的数据发送出去
* 避免出现大量的FIN_WAIT_2状态的socket占用资源
* 
* 备注: tcp连接关闭的过程
* 
*        Me                            Peer
* 1,   close()      -> [FIN] ->          
* 2,   FIN_WAIT_1                    CLOSE_WAIT
* 3,                <- [ACK] <-     
* 4,   FIN_WAIT_2                    
* 5,                <- [FIN] <-      close()
* 6,   TIME_WAIT                     LAST_ACK
* 7,                -> [ACK] ->      
* 8,   CLOSED                        CLOSED
**/
struct linger noLinger;
noLinger.l_onoff = 1;
noLinger.l_linger = 0;
setsockopt(hSocket, SOL_SOCKET, SO_LINGER, (const char *)&noLinger, sizeof(noLinger));

/**
*
* No-Nagle
* 不需要在缓冲区中整合小包碎片
* 立刻发送
* 不用凑齐链路层的一个帧大小
* 
**/
int NoNagleFlag = 1;
setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&NoNagleFlag, sizeof(NoNagleFlag));

/**
*
* 设置超时
* 注意: linux和win的参数不一样
*
**/
int nTimeout = 6000;
setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(const char*)&nTimeout,sizeof(nTimeout))
	


源代码;

http://hi.csdn.net/attachment/201112/20/0_1324361094v7Tx.gif

你可能感兴趣的:(socket阻塞与非阻塞读写)