对于网络编程这一块,面试必考,但……真正工作不见得接触到几次,于是我又忘了,今天整理一下发送和接受时候的几个函数
一、recv
函数原型:
int recv( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags);
参数:
In SOCKET s:就是Socket本身,这没什么好说的,需要注意的是server_socket与client_socket之间的联系和区分
Out char *buf:接受数据的缓存区,传入的是地址或者指针。在成功调用recv后,接收到的数据就存放在这里
In int len:缓存区的长度,整型。
In int flags:某个标志,默认取0。也有其他的,初学者写0就可以。flags参数表如下:
flags | 说明 | recv | send |
---|---|---|---|
MSG_DONTROUTE | 绕过路由表查找 | • | |
MSG_DONTWAIT | 仅本操作非阻塞 | • | • |
MSG_OOB | 发送或接收带外数据 | • | • |
MSG_PEEK | 窥看外来消息 | • | |
MSG_WAITALL | 等待所有数据 | • |
函数说明:
在调用recv
函数之后,socket执行了下列操作
- 检查socket的
发送缓存区
,并等待之前的数据发送完,期间出现网络错误则返回SOCKET_ERROR; - 检查套接字s的
接收缓冲区
,并等待数据接收完毕; - 当数据接收完毕,recv函数就把s的接收缓冲区中的数据copy到recv_buf中。
注意事项:
- 协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。
recv函数仅仅是copy数据,真正的接收数据是协议来完成的。
- recv函数返回其实际copy的字节数,如果recv在copy时出错,那么它返回SOCKET_ERROR。如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
- 在unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用 recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
返回值:
返回值 | 说明 |
---|---|
SOCKET_ERROR | Socket错误(见上文) |
>0 | 调用正常,返回接收到的字节数,非传入的recv_buf_len |
0 | socket关闭 |
<1 | 各种错误 |
二、send
函数原型:
int send( SOCKET s,char *buf,int len,int flags );
参数:
In SOCKET s:就是Socket本身,这没什么好说的,需要注意的是server_socket与client_socket之间的联系和区分
Out char *buf:发送的缓存区,传入的是地址或者指针。在这里存放等待发送的数据。
In int len:缓存区的长度,整型。
In int flags:某个标志,默认取0。也有其他的,初学者写0就可以。flags参数表如下:
flags | 说明 | recv | send |
---|---|---|---|
MSG_DONTROUTE | 绕过路由表查找 | • | |
MSG_DONTWAIT | 仅本操作非阻塞 | • | • |
MSG_OOB | 发送或接收带外数据 | • | • |
MSG_PEEK | 窥看外来消息 | • | |
MSG_WAITALL | 等待所有数据 | • |
需要注意的是,send的参数与recv参数大致相同,recv的buf在调用后会被装入接收到的消息,而send的buf调用后没有变化
函数说明:
在调用send
函数之后,socket执行了下列操作
- 若
参数len(即待发送数据的长度)
大于socket发送缓存区的长度n
,则返回SOCKET_ERROR。(数据太多了,无法copy过去) - 检查
套接字s是否正在发送数据
,若是则等待发送完毕。(注意不是检查发送缓存区是否有数据) - 对比
参数len(即待发送数据的长度)
和socket发送缓存区剩余长度n-x
,判断:
(注意此时发送缓存区可能有数据,只是并未开始发送)- 如果剩余长度不能塞下buf,则socket等待
发送缓存区
发送完毕; - 如果能塞下,则把
待发送数据
copy到发送缓存区的剩余空间
,并返回实际copy的字节数。(该返回值不一定等于len)
- 如果剩余长度不能塞下buf,则socket等待
注意事项:
- 将待发送数据copy到发送缓存区时,若copy出错,则返回SOCKET_ERROR;若socket在等待发送缓存区发送的时候网络错误,也返回SOCKET_ERROR。
- 待发送数据copy完之后,send函数就返回了,数据还没有send出去,若此时遇到网络错误,则会在下一次socket调用时候返回SOCKET_ERROR。
- 在unix系统下,如果send在等待协议传送数据时网络断开,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的处理是进程终止。
返回值:
返回值 | 说明 |
---|---|
SOCKET_ERROR | 错误(见上文) |
>0 | 调用正常,返回实际发送的字节数,非传入的len |
0 | socket关闭 |
<1 | 各种错误 |
三、read与write
read/write在socket实现部分与send/recv几乎一致,故简要介绍
函数原型:
ssize_t read(int fd, void *buf, size_t nbytes);
ssize_t write(int fd, const void *buf, size_t nbytes);
参数:
int fd:就是Socket本身,这没什么好说的,需要注意的是server_socket与client_socket之间的联系和区分
void *buf:数据的缓存区,传入的是地址或者指针。
size_t nbytes:缓存区的长度。
函数说明:
- Linux在建立好Tcp之后,socket可以当作文件描述符来使用,因此read/write可以读取文件,也可以读取套接字。
- size_t 是通过 typedef 声明的 unsigned int 类型;
ssize_t 在 “size_t” 前面加了一个"s",代表 signed,即 ssize_t 是通过 typedef 声明的 signed int 类型。 write函数返回值不一定是全部数据的字节数,有可能只有一部分,即write函数不保证能全部传输完数据,需要while不断传输,while时需要同步更改len
返回值:
返回值 | 说明 |
---|---|
>0 | 调用正常,返回写入或者读取的字节数,非传入的recv_buf_len。write函数返回值不一定是全部数据的字节数 |
0 | read时候遇到文件结束符 |
<1 | 各种错误 |
EINTR | 中断错误 |
EPIPE | 网络错误 |