当前位置:编程学习 > JAVA >>

socket函数read write send和recv 收集整理

1.send   recv   是面向套接口的文件描述符
read   write   需要的是文件描述符

read和write也可以对socket的fd进行处理
二者在BSD4.4上是一致的,最终会调到统一的内核处理函数,只是recv和send多些选项(flag)


What   is   the   difference   between   read()   and   recv()?
From   Andrew   Gierth   (andrew@erlenstar.demon.co.uk):

read()   is   equivalent   to   recv()   with   a   flags   parameter   of   0.   Other   values   for   the   flags   parameter   change   the   behaviour   of   recv().   Similarly,   write()   is   equivalent   to   send()   with   flags   ==   0.

It   is   unlikely   that   send()/recv()   would   be   dropped;   perhaps   someone   with   a   copy   of   the   POSIX   drafts   for   socket   calls   can   check...

Portability   note:   non-unix   systems   may   not   allow   read()/write()   on   sockets,   but   recv()/send()   are   usually   ok.   This   is   true   on   Windows   and   OS/2,   for   example.

=================================================================================================================

一旦,我们建立好了tcp连接之后,我们就可以把得到的fd当作文件描述符来使用。

由此网络程序里最基本的函数就是read和write函数了。
ssize_t write(int fd, const void*buf,size_t nbytes);
write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1. 并设置errno变量. 在网络程序中,当我们向套接字文件描述符写时有两可能.
1)write的返回值大于0,表示写了部分或者是全部的数据. 这样我们用一个while循环来不停的写入,但是循环过程中的buf参数和nbyte参数得由我们来更新。也就是说,网络写函数是不负责将全部数据写完之后在返回的。
2)返回的值小于0,此时出现了错误.我们要根据错误类型来处理.
如果错误为EINTR表示在写的时候出现了中断错误.
如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接).
为了处理以上的情况,我们自己编写一个写函数来处理这几种情况.

int my_write(int fd,void *buffer,int length)
{
int bytes_left;
int written_bytes;
char *ptr;

ptr=buffer;
bytes_left=length;
while(bytes_left>0)
{
         /* 开始写*/
         written_bytes=write(fd,ptr,bytes_left);
         if(written_bytes<=0) /* 出错了*/
         {       
                 if(errno==EINTR) /* 中断错误 我们继续写*/
                         written_bytes=0;
                 else             /* 其他错误 没有办法,只好撤退了*/
                         return(-1);
         }
         bytes_left-=written_bytes;
         ptr+=written_bytes;     /* 从剩下的地方继续写   */
}
return(0);
}

读函数read
ssize_t read(int fd,void *buf,size_t nbyte)
read函数是负责从fd中读取内容.当读成功 时,read返回实际所读的字节数,如果返回的值是0 表示已经读到文件的结束了,小于0表示出现了错误.如果错误为EINTR说明读是由中断引起 的, 如果是ECONNREST表示网络连接出了问题. 和上面一样,我们也写一个自己的读函数.

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_read);
    if(bytes_read<0)
    {
      if(errno==EINTR)
         bytes_read=0;
      else
         return(-1);
    }
    else if(bytes_read==0)
        break;
     bytes_left-=bytes_read;
     ptr+=bytes_read;
}
return(length-bytes_left);
}

数据的传递
有了上面的两个函数,我们就可以向客户端或者是服务端传递数据了.比如我们要传递一个结构.可以使用如下方式

/*   客户端向服务端写 */

struct my_struct my_struct_client;
write(fd,(void *)&my_struct_client,sizeof(struct my_struct);

/* 服务端的读*/
char buffer[sizeof(struct my_struct)];
struct *my_struct_server;
read(fd,(void *)buffer,sizeof(struct my_struct));
my_struct_server=(struct my_struct *)buffer;    

在网络上传递数据时我们一般都是把数据转化为char类型的数据传递.接收的时候也是一样的 注意的是我们没有必要在网络上传递指针(因为传递指针是没有任何意义的,我们必须传递指针所指向的内容)


6.1 recv和send
recv和send函数提供了和read和write差不多的功能.不过它们提供 了第四个参数来控制读写操作.

int recv(int sockfd,void *buf,int len,int flags)
int send(int sockfd,void *buf,int len,int flags)

前面的三个参数和read,write一样,第四个参数可以是0或者是以下的组合
_______________________________________________________________
| MSG_DONTROUTE | 不查找表 |
| MSG_OOB | 接受或者发送带外数据 |
| MSG_PEEK | 查看数据,并不从系统缓冲区移走数据 |
| MSG_WAITALL | 等待所有数据 |
|--------------------------------------------------------------|

MSG_DONTROUTE:是send函数使用的标志.这个标志告诉IP.目的主机在本地网络上面,没有必要查找表.这个标志一般用网络诊断和路由程序里面.
MSG_OOB:表示可以接收和发送带外的数据.关于带外数据我们以后会解释的.

MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容.这样下次读的时候,仍然是一样的内容.一般在有多个进程读写数据时可以使用这个标志.

MSG_WAITALL是recv函数的使用标志,表示等到所有的信息到达时才返回.使用这个标志的时候recv回一直阻塞,直到指定的条件满足,或者 是发生了错误. 1)当读到了指定的字节时,函数正常返回.返回值等于len 2)当读到了文件的结尾时,函数正常返回.返回值小于len 3)当操作发生错误时,返回-1,且设置错误为相应的错误号(errno) 

作者“YEYUANGEN的专栏”

补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,