网络编程send和recv

来源:互联网 发布:java实现群发短信 编辑:程序博客网 时间:2024/05/21 02:22

 转载 : http://blog.csdn.net/petershina/article/details/7975798

1.send 函数

  int send( SOCKET s, const char FAR *buf, int len, int flags );

  不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

  该函数的第一个参数指定发送端套接字描述符;

  第二个参数指明一个存放应用程序要发送数据的缓冲区;

  第三个参数指明实际要发送的数据的字节数;

  第四个参数一般置0。

  这里只描述同步Socket的send函数的执行流程。当调用该函数时,

  (1)send先比较待发送数据的长度len和套接字s的发送缓冲的长度, 如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;

  (2)如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len

  (3)如果len大于剩余空间大小,send就一直等待协议把s的发送缓冲中的数据发送完

  (4)如果len小于剩余 空间大小,send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。

  如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。

  要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如 果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在 执 行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返 回 SOCKET_ERROR)

  注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

  通过测试发现,异步socket的send函数在网络刚刚断开时还能发送返回相应的字节数,同时使用select检测也是可写的,但是过几秒钟之后,再send就会出错了,返回-1。select也不能检测出可写了。

 

2. recv函数

  int recv( SOCKET s, char FAR *buf, int len, int flags);

   不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。该函数的第一个参数指定接收端套接字描述符;

  第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;

  第三个参数指明buf的长度;

  第四个参数一般置0。

  这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,

  (1)recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,

  (2)如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数 据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以 在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),

  recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。

  注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。如果你通过printf想在某个点打印信息,但是怎么运行都打印不出,那可能就是服务器或客户端send/recv失败,直接终止进程了。可以设置忽略这个信号:

signal(SIGPIPE, SIG_IGN);


http://www.cnblogs.com/mhscn/p/3911284.html

1、read

-----------------------------------------------------------------------

#include <unistd.h>

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); }
复制代码

2、write

-----------------------------------------------------------------------

#include <unistd.h>

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); } 
复制代码

3、recv & send

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

-----------------------------------------------------------------------

#include <sys/types.h>

#include <sys/socket.h>

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

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

-----------------------------------------------------------------------

    前面的三个参数和一样,第四个参数可以是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)。

    MSG_NOSIGNAL:is a flag used by send() in some implementations of the Berkeley sockets API.

This flag requests that the implementation does not to send a SIGPIPE signal on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned as normal.

Though it is in some Berkely sockets APIs (notably Linux) it does not exist in what some refer to as the reference implementation, FreeBSD, which instead uses a socket option SO_NOSIGPIPE?.

对于服务器端,我们可以使用这个标志。目的是不让其发送SIG_PIPE信号,导致程序退出。

    如果flags为0,则和read、write一样的操作。还有其它的几个选项,不过我们实际上用的很少。可以查看 Linux Programmer's Manual得到详细解释。

 


原创粉丝点击