一般情况下:send(), recv()用于TCP,sendto()及recvfrom()用于UDP,但是send(), recv()也可以用于UDP,sendto()及recvfrom()也可以用于TCP。

一、recv()和recvfrom()有什么区别?

1. recvfrom()函数原型.

  函数说明: recvfrom()用来接收远程主机经指定的socket传来的数据, 并把数据传到由参数buf指向的内存空间, 参数len为可接收数据的最大长度. 参数flags一般设0, 其他数值定义参考recv(). 参数from用来指定欲传送的网络地址,结构sockaddr请参考bind()函数. 参数fromlen为sockaddr的结构长度. 如果正确接收返回接收到的字节数,失败返回-1. 

 1 //sockfd:标识一个已连接套接口的描述字。
 2 //buf:接收数据缓冲区。
 3 //len:缓冲区长度。
 4 //flags:调用操作方式。是以下一个或者多个标志的组合体,可通过“ | ”操作符连在一起:
 5 //    MSG_DONTWAIT: 操作不会被阻塞。
 6 //    MSG_ERRQUEUE:1.指示应该从套接字的错误队列上接收错误值,依据不同的协议,错误值以某种辅佐性消息的方式传递进来,使用者应该提供足够大的缓冲区。导致错误的原封包通过msg_iovec作为一般的数据来传递。导致错误的数据报原目标地址作为msg_name被提供。错误以sock_extended_err结构形态被使用,定义如下    
 7 //    MSG_ERRQUEUE:2.指示除了来自套接字错误队列的错误外,没有接收到其它数据。    #define SO_EE_ORIGIN_NONE 0
 8 //        #define SO_EE_ORIGIN_LOCAL 1
 9 //        #define SO_EE_ORIGIN_ICMP 2
10 //        #define SO_EE_ORIGIN_ICMP6 3
11 //        struct sock_extended_err
12 //        {
13 //            u_int32_t ee_errno;
14 //            u_int8_t ee_origin;
15 //            u_int8_t ee_type;
16 //            u_int8_t ee_code;
17 //            u_int8_t ee_pad;
18 //            u_int32_t ee_info;
19 //            u_int32_t ee_data;
20 //        };
21 //    MSG_PEEK:指示数据接收后,在接收队列中保留原数据,不将其删除,随后的读操作还可以接收相同的数据。
22 //    MSG_TRUNC:1.返回封包的实际长度,即使它比所提供的缓冲区更长, 只对packet套接字有效。
23 //    MSG_TRUNC:2.指明数据报尾部数据已被丢弃,因为它比所提供的缓冲区需要更多的空间
24 //    MSG_WAITALL:要求阻塞操作,直到请求得到完整的满足。然而,如果捕捉到信号,错误或者连接断开发生,或者下次被接收的数据类型不同,仍会返回少于请求量的数据。
25 //    MSG_EOR:指示记录的结束,返回的数据完成一个记录。。
26 //    MSG_CTRUNC:指明由于缓冲区空间不足,一些控制数据已被丢弃。
27 //    MSG_OOB:指示接收到out-of-band数据(即需要优先处理的数据)。
28 //from:(可选)指针,指向装有源地址的缓冲区。
29 //fromlen:(可选)指针,指向from缓冲区长度值。
30 ssize_t recvfrom(int sockfd, void *buf, size_t len, unsigned int flags, struct sockaddr *from, socket_t *fromlen);

  recv的recvfrom是可以替换使用的,只是recvfrom多了两个参数,可以用来接收对端地址信息,这个对于udp这种无连接协议,可以很方便地进行回复。而换过来如果你在udp当中也使用recv,那么就不知道该回复给谁了,如果你不需要回复的回话,也是可以使用的。另外就是对于tcp协议是已经知道对端的,就没必要每次接收还多收一个地址,要取地址信息,在accept当中取得就可以加以记录了。recvfrom 可同时应用于面向连接的源和无连接的套接字,recv一般只用在面向连接的套接字,几乎等同于recvfrom,只要将recvfrom的第五个参数设度置NULL。说白了就是 recvfrom比recv多了一个导航功能。

1 // //函数返回值+错误代码
2 // EBADF 参数s非合法的socket处理代码
3 // EFAULT 参数中有一指针指向无法存取的内存空间。
4 // ENOTSOCK 参数s为一文件描述词,非socket。
5 // EINTR 被信号所中断。
6 // EAGAIN 此动作会令进程阻断,但参数s的socket为不可阻断。
7 // ENOBUFS 系统的缓冲内存不足
8 // ENOMEM 核心内存不足
9 // EINVAL 传给系统调用的参数不正确。

2.recv()函数原型

1 int recv( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags);
  这里只描述同步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信号,进程对该信号的默认处理是进程终止。
二、send()和sendto()有什么区别?

  sendto可以在参数中指定发送的目标地址 , send需要socket已建立连接, sendto 可用于无连接的 socket 对于send的有连接socket,两者一样,sendto最后两个参数没用.

1. send()函数原型

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

  Linux接口ssize_t send (int s,const void *msg,size_t len,int flags)说明:send() 用来将数据由指定的 socket 传给对方主机。使用 send 时套接字必须已经连接。send 不包含传送失败的提示信息,如果检测到本地错误将返回-1。因此,如果send 成功返回,并不必然表示连接另一端的进程接收数据。所保证的仅是当send 成功返回时,数据已经无错误地发送到网络上。

  Windows接口int PASCAL FAR send( SOCKET s, const char FAR* buf, int len, int flags)说明:send()适用于已连接的数据包或流式套接口发送数据。对于数据报类套接口,必需注意发送数据长度不应超过通讯子网的IP包最大长度。IP包最大长度在WSAStartup()调用返回的WSAData的iMaxUdpDg元素中。如果数据太长无法自动通过下层协议,则返回WSAEMSGSIZE错误,数据不会被发送。

  请注意成功地完成send()调用并不意味着数据传送到达。如果传送系统的缓冲区空间不够保存需传送的数据,除非套接口处于非阻塞I/O方式,否则send()将阻塞。对于非阻塞SOCK_STREAM类型的套接口,实际写的数据数目可能在1到所需大小之间,其值取决于本地和远端主机的缓冲区大小。可用select()调用来确定何时能够进一步发送数据。
1 //参数1: 指定发送端套接字描述符;
2 //参数2: 指明一个存放应用程序要发送数据的缓冲区;
3 //参数3: 指明实际要发送的数据的字节数;
4 //参数4: 一般设置为0。
5 int send( SOCKET s , const char FAR *buf , int len , int flags );  

  同步socket的send函数的执行流程: 当调用该函数时,

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

  2)如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议 是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么 send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。

  3)如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;

  4)如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。

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

2. sendto()函数原型

  sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。

1 //该函数比 send() 函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。 
2 int sendto(int sockfd, const void *msg,int len , unsigned int flags, const struct sockaddr *to, int tolen); 

 -----END----

没有坚守就没有事业,没有执着就没有未来!