read()
、write()
操作套接字时具体的效果;read()
想要读取的数据字节数并不一定等于实际读取到的字节数,如何使读操作实际读取的字节数与想要获取的字节数一样?write()
类同。除上之外,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()
实际读写的数据与想要操作的数据相同,那么就需要更为复杂的异常处理逻辑。而对于单纯的读写套接字来说,非阻塞并没有带来其他的便利。综上来说,读写套接字时将其设置为非阻塞?大可不必!