socket数据收发

socket读写

TCP协议是面向流的,read和write调用的返回值往往小于参数指定的字节数。对于read调用,如果接收缓冲区中有20字节,请求读100个字节,就会返回20。对于write调用,如果请求写100个字节,而发送缓冲区中只有20个字节的空闲位置,那么write会阻塞,直到把100个字节全部交给发送缓冲区才返回。但如果socket文件描述符中有O_NONBLOCK标志,则write不阻塞,直接返回20。

为避免这些情况干扰主程序逻辑,确保读写所请求的字节数,应包装read和write函数。

当设置socket为非阻塞模式时,要用select()或epoll()判断什么时候可正常写入或读出。

  • write

sszie_t write(int fd, const void *buf, size_t count);

return:成功,返回写入的字节数;失败-1。

在网络程序中,当我们向socket写时有两种可能:

  1. write的返回值大于0,表示写了部分或者全部的数据。
  2. 返回值小于0,此时出现了错误。我们要根据错误类型来处理。如果错误为EINTR表示在写的时候出现了中断错误,应重试。如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接,返回-1)。
  3. 此外还有可能socket为非阻塞(O_NONBLOCK),当缓冲区满时,立即返回0。(我的想法) ----错误想法,fd设置为非阻塞时,不能写立即返回-1,设置errno为EAGAIN或EWOULDBLOCK(man write ERRORS)。
  4. 返回0时,表示未写数据,继续写即可。

int my_write(int fd, void *buffer, int len){

  int bytes_left;

  int written_bytes;

  char *ptr;

  ptr = buffer;

  bytes_left = len;

  while(bytes_left > 0){

    written_bytes = write(fd, ptr, bytes_left);

    if(writte_bytes <= 0){   //出错了

      if(errno == EINTR && written_bytes < 0)    // 中断错误,继续写。

        writen_bytes = 0;

      else         //其他错误,直接退出

        return -1;                    

    }

bytes_left -= writen_bytes;

ptr += written_bytes;
}

return len;

}

  • read

ssize_t read(int fd, void *buf, size_t count);

return:成功,返回读取到的字节数;失败-1。

返回值为0,表示已经读到文件的结束;返回值小于0 表示出现了错误。如果错误为EINTR说明读是由中断引起的(继续读),如果是ECONNREST表示网络连接出了问题。

read()阻塞时,一直等,等于0表示文件结束或网络连接关闭;read()非阻塞,无数据可读时立即返回-1,errno为EAGAIN(若一直读,一直返回-1,errno为EAGAIN)。

int my_read(int fd,void *buffer,int length){

  int bytes_left;

  int bytes_read;

  char *ptr;

  bytes_left=length;

  while(bytes_left>0){

    bytes_read=read(fd,ptr,bytes_left);

    if(bytes_read<0){

      if(errno==EINTR)          // 中断引起的,继续读

        bytes_read=0;

      else

        return(-1);          //其他原因,直接退出

    }

    else if(bytes_read==0)     // 对方close连接(收到FIN包)

      break;

    bytes_left-=bytes_read;

    ptr+=bytes_read;

  }

return(length-bytes_left);

}

  • recvfrom

int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr * from, int *fromlen);

  • sendto

int sendto(int sockfd, const void *msg, int len, unsigned int flags, struct sockaddr *to, int tolen);

注:如果对信息的来源不感兴趣,可以将from和fromlen设置为NULL。

原文地址:https://www.cnblogs.com/embedded-linux/p/4986192.html