send()
、recv()
、sendto()
和recvfrom()
函数解析
一般情况下,send()
、recv()
在TCP协议下使用,sendto()
、recvfrom()
在UDP协议下使用,也可以在TCP协议下使用,不过用的很少。
1、send()
这里只描述同步socket的send()
函数的执行流程。
s:套接字
| buf:存储发送数据
| len:发送数据长度
当调用该函数时,send()
先比较待发送数据的长度len
和套接字s的发送缓冲的长度
,
- 如果
len
大于套接字s
的发送缓冲区的长度,该函数返回SOCKET_ERROR
; - 如果
len
小于或者等于套接字s
的发送缓冲区的长度,那么send()
先检查协议是否正在发送套接字s
的发送缓冲中的数据,如果是就等待协议把数据发送完; - 如果协议还没有开始发送
套接字s
的发送缓冲中的数据或者套接字s
的发送缓冲中没有数据,那么send
就比较套接字s
的发送缓冲区的剩余空间和len
; - 如果
len
大于剩余空间大小,send()
就一直等待协议把套接字s
的发送缓冲中的数据发送完; - 如果
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
信号,进程对该信号的默认处理是进程终止。
2、recv()
这里只描述同步Socket的recv函数的执行流程。
s:套接字 | buf:存储发送数据 | len:发送数据长度
当应用程序调用recv()
函数时,recv()
先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送套接字s
的发送缓冲中的数据时出现网络错误,那么recv()
函数返回SOCKET_ERROR
;
如果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
信号,进程对该信号的默认处理是进程终止。
3、sendto()
& recvfrom()
在无连接的数据报socket方式下,由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址,sendto()
函数原型为:
int sendto(
int sockfd,
const void *msg,
int len,
unsigned int flags,
const struct sockaddr *to,
int tolen
);
该函数比send()
函数多了两个参数,to
表示目地机的IP地址
和端口号
信息,而tolen
常常被赋值为sizeof(struct sockaddr)
。sendto()
函数也返回实际发送的数据字节长度或在出现发送错误时返回 -1
。
recvfrom()
的情况与sendto()
类似,也需要目的地址,函数原型为:
int recvfrom(
int sockfd,
void *buf,
int len,
unsigned int lags,
struct sockaddr *from,
int *fromlen
);
from
是一个struct sockaddr
类型的变量,该变量保存源机的IP地址
及端口号
。fromlen
常置为sizeof(struct sockaddr)
。当recvfrom()
返回时,fromlen
包含实际存入from
中的数据字节数。recvfrom()
函数返回接收到的字节数或当出现错误时返回 -1
,并置相应的errno
。