Java教程

套接字的读写操作在阻塞、非阻塞模式下的表现

本文主要是介绍套接字的读写操作在阻塞、非阻塞模式下的表现,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

背景

  1. 了解套接字的读写操作在阻塞模式、非阻塞模式下的表现会有差异,但是不知道read()write()操作套接字时具体的效果;
  2. read()想要读取的数据字节数并不一定等于实际读取到的字节数,如何使读操作实际读取的字节数与想要获取的字节数一样?write()类同。

读写操作什么情况下会被阻塞?

  1. 读(read):读套接字是将套接字接收缓存区中数据读取到应用层中,如果此时接收缓存区中为空、没有待读取的数据,读操作将被阻塞;
  2. 写(write):同理,写套接字是将应用层中数据写到套接字发送缓存区中,如果此时发送缓存区中剩余空间大小为0、没有可写空间,写操作将被阻塞。

套接字设置为阻塞/非阻塞模式下,read()/write()表现差异

image-20210822165105215

除上之外,read()write()有一重要区别需要注意,阻塞模式下:

  • 接收缓存区中有数据,read()将数据读取至应用层,即使缓存数据长度小于想要获取长度read()也在读取后返回;
  • write()将发送缓存区中剩余空间写完后,仍有待写入数据没有写完,它将继续等待直到将所有数据写入发送缓存区中。

嗯,read()在努力,write()在尽力。

实用的读/写函数

/**@brief: 从套接字读取n个字节数据
* @param[i]: const int sockFd, 套接字ID
* @param[i]: const int n, 待读取字节数
* @param[o]: char *pBuf, 数据缓存区
* @return: 返回成功读取的字节数,失败返回-1
*/
size_t readn(char *pBuf, const int sockFd, const size_t n)
{
  size_t nLeft = n; // 剩余待读取的字节数
  ssize_t nRead = 0; // 当前此读取的字节数

  if (NULL == pBuf || sockFd < 0 || n <= 0)
  {
    CLIENT_MAIN_DEBUG(ERROR, "readn, param is invalid.\n");
    return FAILURE;
  }

  while (nLeft > 0)
  {
    nRead = read(sockFd, pBuf, nLeft);
    if (nRead < 0) // 读取异常处理
    {
      if (EINTR == errno) // 中断异常,重新读取
      {
        CLIENT_MAIN_DEBUG(DEBUG, "read is interrupted by others.\n");
        continue;
      }
      else
      {
        CLIENT_MAIN_DEBUG(ERROR, "read failed, %s.\n", strerror(errno));
        return FAILURE;
      }
    }
    else if (0 == nRead) // 读取到文件结束符(FIN)
    {
      CLIENT_MAIN_DEBUG(DEBUG, "read the end of file.\n");
      return (n - nLeft);
    }
    else
    {
      nLeft -= nRead;
      pBuf += nRead;
    }
  }

  return n;
}
/**@brief: 向套接字写n个字节数据
* @param[i]: const int sockFd, 套接字ID
* @param[i]: const int n, 待读取字节数
* @param[i]: const char *pBuf, 数据缓存区
* @return: 返回成功写入的字节数,失败返回-1
*/
size_t writen(const int sockFd, const size_t n, const char *pBuf)
{
  size_t nLeft = n; // 剩余待写入的字节数
  ssize_t nWrite = 0; // 当前写入的字节数

  if (sockFd < 0 || n <= 0 || NULL == pBuf)
  {
    CLIENT_MAIN_DEBUG(ERROR, "writen, param is invalid.\n");
    return FAILURE;
  }

  while (nLeft > 0)
  {
    nWrite = write(sockFd, pBuf, nLeft);
    if (nWrite <= 0) // 写入异常处理
    {
      if (EINTR == errno) // 中断异常,重新写入
      {
        CLIENT_MAIN_DEBUG(DEBUG, "write is interrupted by others.\n");
        continue;
      }
      else
      {
        CLIENT_MAIN_DEBUG(ERROR, "write failed, %s.\n", strerror(errno));
        return FAILURE;
      }
    }
    else
    {
      nLeft -= nWrite;
      pBuf += nWrite;
    }
  }

  return n;
}

为什么读写操作时,套接字一般被设置为阻塞模式、而不设置为非阻塞模式?

套接字为非阻塞模式时,读写操作被阻塞则直接返回,如果还想使read()/write()实际读写的数据与想要操作的数据相同,那么就需要更为复杂的异常处理逻辑。而对于单纯的读写套接字来说,非阻塞并没有带来其他的便利。综上来说,读写套接字时将其设置为非阻塞?大可不必

这篇关于套接字的读写操作在阻塞、非阻塞模式下的表现的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!