socket编程中的read、write与recv、send的区别

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)
假如flags为0,则和read,write相同的操作.更有其他的几个选项,但是我们实际上用的很少,能够查看Linux Programmer’s Manual得到周详解释.
2 recvfrom和sendto
这两个函数一般用在非套接字的网络程式当中(UDP),我们已在前面学会了.
3 recvmsg和sendmsg
recvmsg和sendmsg能够实现前面任何的读写函数的功能.
int recvmsg(int sockfd,struct msghdr *msg,int flags)
int sendmsg(int sockfd,struct msghdr *msg,int flags)
struct msghdr
{
void *msg_name;
int msg_namelen;
struct iovec *msg_iov;
int msg_iovlen;
void *msg_control;
int msg_controllen;
int msg_flags;
}
struct iovec
{
void *iov_base;
size_t iov_len;
}
msg_name和msg_namelen当套接字是非面向连接时(UDP),他们存储接收和发送方的地址信息.msg_name实际上是个指向struct sockaddr的指针,msg_namelen是结构的长度.当套接字是面向连接时,这两个值应设为NULL. msg_iov和msg_iovlen指出接受和发送的缓冲区内容.msg_iov是个结构指针,msg_iovlen指出这个结构数组的大小.msg_control和msg_controllen这两个变量是用来接收和发送控制数据时的,msg_flags指定接受和发送的操作选项.和recv,send的选项相同
4 套接字的关闭
关闭套接字有两个函数close和shutdown.用close时和我们关闭文档相同.
5 shutdown
int shutdown(int sockfd,int howto)
TCP连接是双向的(是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个方向,这个时候我们能够使用shutdown.针对不同的howto,系统回采取不同的关闭方式.
howto=0这个时候系统会关闭读通道.但是能够继续往接字描述符写.
howto=1关闭写通道,和上面相反,着时候就只能够读了.
howto=2关闭读写通道,和close相同 在多进程程式里面,假如有几个子进程共享一个套接字时,假如我们使用shutdown,那么任何的子进程都不能够操作了,这个时候我们只能够使用close来关闭子进程的套接字描述符.
<think>我们首先讲解writeread函数,然后讲解sendrecv函数,最后比较它们的区别。 注意:在Linux socket编程中,这些函数都用于数据的发送接收,但使用场景功能有差异。 1. write函数 原型:ssize_t write(int fd, const void *buf, size_t count); 作用:将buf中的count字节数据写入文件描述符fd所指向的对象(可以是文件、套接字等)。 返回值:成功时返回写入的字节数,出错返回-1。 示例:用于向已连接的套接字发送数据。 2. read函数 原型:ssize_t read(int fd, void *buf, size_t count); 作用:从文件描述符fd中读取最多count字节的数据到buf中。 返回值:成功时返回读取的字节数(0表示对端关闭),出错返回-1。 示例:用于从已连接的套接字接收数据。 3. send函数 原型:ssize_t send(int sockfd, const void *buf, size_t len, int flags); 作用:将buf中的len字节数据发送到套接字sockfd(必须为已连接的套接字)。 参数flags:用于指定发送方式,常见的有MSG_OOB(发送带外数据)、MSG_DONTROUTE(绕过路由表)等。 返回值:成功时返回发送的字节数,出错返回-1。 4. recv函数 原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags); 作用:从套接字sockfd接收最多len字节的数据到buf中(套接字必须已连接)。 参数flags:用于指定接收方式,常见的有MSG_OOB(接收带外数据)、MSG_PEEK(查看数据但不移除)等。 返回值:成功时返回接收的字节数(0表示对端关闭),出错返回-1。 区别: (1) 功能范围: write/read:是通用的文件描述符操作函数,可用于文件、管道、套接字等。 send/recv:是专门为套接字设计的函数,只能用于套接字描述符。 (2) 参数差异: sendrecvwriteread多了一个flags参数,用于指定特殊的发送或接收选项。 (3) 带外数据(紧急数据)处理: sendrecv可以通过flags参数(MSG_OOB)来发送接收带外数据,而write/read无法处理带外数据。 (4) 使用场景: write/read:在不需要特殊控制选项的情况下,可以用于套接字,代码通用性更强。 send/recv:当需要利用flags参数进行特殊操作(如带外数据、窥探数据等)时使用。 注意:在套接字编程中,对于已连接的TCP套接字,writesendreadrecv在基本数据传输上功能相似,但send/recv提供了额外的控制选项。 示例代码片段(TCP回显客户端): // 已连接套接字sockfd char buf[1024]; // 发送数据 if (send(sockfd, buf, strlen(buf), 0) == -1) { perror("send"); } // 接收数据 int n = recv(sockfd, buf, sizeof(buf), 0); if (n == 0) { // 对端关闭 } else if (n < 0) { perror("recv"); } 重要:在非阻塞套接字中,这些函数的返回值处理更为复杂,需要检查errno(如EAGAIN或EWOULDBLOCK)。 §§相关问题§§ 1. 在非阻塞套接字编程中,writesend函数的行为有何不同?如何处理EAGAIN错误? 2. 什么是带外数据(OOB)?在TCP中如何正确使用send/recv的MSG_OOB标志传输紧急数据? 3. 使用recv函数时,MSG_PEEKMSG_WAITALL标志分别有什么作用?请举例说明。 4. 为什么在套接字编程中,有时需要多次调用read/recv函数才能接收到完整的数据? 5. 在UDP套接字编程中,能否使用writeread函数?使用sendto/recvfrom相比有什么限制?</think>### Linux Socket 编程中的 I/O 函数详解 #### 1. **基本函数用法** | 函数 | 原型 | 关键参数 | 主要用途 | |-------|------|---------|---------| | **write()** | `ssize_t write(int fd, const void *buf, size_t count);` | `fd`: 文件描述符<br>`buf`: 数据缓冲区<br>`count`: 字节数 | 向文件描述符写入数据(通用文件/套接字) | | **read()** | `ssize_t read(int fd, void *buf, size_t count);` | `fd`: 文件描述符<br>`buf`: 接收缓冲区<br>`count`: 最大读取字节 | 从文件描述符读取数据(通用文件/套接字) | | **send()** | `ssize_t send(int sockfd, const void *buf, size_t len, int flags);` | `sockfd`: 套接字描述符<br>`flags`: 控制标志(如MSG_OOB) | 向已连接的套接字发送数据(专用于套接字) | | **recv()** | `ssize_t recv(int sockfd, void *buf, size_t len, int flags);` | `sockfd`: 套接字描述符<br>`flags`: 控制标志(如MSG_PEEK) | 从已连接的套接字接收数据(专用于套接字) | #### 2. **核心区别对比** | 特性 | write()/read() | send()/recv() | |--------------|---------------------------------------------|--------------------------------------------| | **适用范围** | 通用文件描述符(文件/管道/套接字) | 仅用于套接字描述符 | | **控制标志** | 无flags参数 | 支持flags参数(MSG_OOB, MSG_PEEK等) | | **带外数据** | 无法处理 | 通过MSG_OOB发送/接收紧急数据 | | **连接要求** | 可用于无连接套接字(需结合sendto/recvfrom) | 仅适用于已连接套接字(TCP) | | **错误处理** | 返回-1设置errno | 额外返回错误类型(如ENOTCONN, ECONNRESET) | #### 3. **关键特性详解 - **flags 参数作用**: - `MSG_OOB`:发送/接收带外数据(最高优先级,TCP紧急指针) - `MSG_PEEK`:查看数据但不从缓冲区移除(recv专用) - `MSG_WAITALL`:阻塞直到读取完整请求数据(recv专用) - `MSG_DONTWAIT`:非阻塞操作(等价于O_NONBLOCK) - **带外数据(OOB)示例**: ```c // 发送端 send(sockfd, "U", 1, MSG_OOB); // 发送紧急字节 // 接收端 recv(sockfd, &oob_byte, 1, MSG_OOB); // 优先处理紧急数据 ``` - **缓冲区行为差异**: ```c // MSG_PEEK 应用场景 char preview[1024]; ssize_t n = recv(sockfd, preview, sizeof(preview), MSG_PEEK); // 数据仍保留在缓冲区,可再次读取 ``` #### 4. **使用场景建议** - **优先使用 send()/recv() 当**: - 需要处理带外数据 - 要求非阻塞操作 - 需要检查数据而不消耗缓冲区 - 需要精确控制TCP行为 - **优先使用 write()/read() 当**: - 编写通用文件描述符代码(文件/套接字混合) - 简单数据传输无需特殊控制 - UDP套接字需配合`sendto()`/`recvfrom()`(但不可单独使用) > **重要注意事项**:所有函数都可能返回部分传输的数据(特别是非阻塞模式),实际开发中需循环调用直到完成数据传输。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值