1. 基本介绍
send
函数
send
函数用于向一个已连接的套接字发送数据。它的典型使用场景是在TCP通信中,客户端和服务器之间交换数据。
函数声明:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数解释:
sockfd
:套接字描述符,表示已连接的套接字。buf
:指向要发送的数据缓冲区。len
:要发送的数据字节数。flags
:控制数据发送行为的标志,可以为 0 或者使用一些选项(例如MSG_DONTWAIT
,非阻塞发送)。
返回值:
- 成功时,返回发送的字节数。
- 失败时,返回 -1,并设置相应的
errno
错误码。
recv
函数
recv
函数用于从已连接的套接字接收数据。通常和send
函数配合使用。
函数声明:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数解释:
sockfd
:套接字描述符,表示已连接的套接字。buf
:指向接收数据的缓冲区。len
:缓冲区的大小,最大接收字节数。flags
:控制接收行为的标志,如MSG_WAITALL
(等待接收完整数据),MSG_PEEK
(查看缓冲区中的数据,但不将其从缓冲区移除)。
返回值:
- 成功时,返回接收到的字节数。
- 如果对端正常关闭连接,返回 0。
- 失败时,返回 -1,并设置
errno
。
2. 发送和接收的典型使用场景
TCP 通信中的 send
和 recv
- 在客户端-服务器模型中,通常会先由客户端发起连接,然后通过
send
函数发送请求数据,服务器通过recv
接收请求并处理。 - 服务器响应数据后,再使用
send
函数将结果返回给客户端,客户端通过recv
获取返回结果。
UDP 通信中的 send
和 recv
UDP是无连接的,因此可以使用sendto
和recvfrom
来指定目标地址和端口,进行数据的发送和接收。
3. flags 参数的常见选项
send
和 recv
的 flags
参数可以控制不同的发送和接收行为,常用的选项包括:
MSG_DONTWAIT
:非阻塞模式,若无法立即发送或接收数据,send
或recv
会立刻返回而不是阻塞。MSG_PEEK
:查看缓冲区数据但不移除数据,常用于探测网络数据的到来。MSG_OOB
:发送或接收带外数据(TCP紧急数据)。MSG_WAITALL
:等待接收到指定大小的数据后才返回。
4. 常见错误及处理
EAGAIN
或EWOULDBLOCK
:在非阻塞模式下,数据无法立即发送或接收,需要等待套接字可用。EINTR
:操作被信号中断,通常可以重试。ECONNRESET
:连接被对端重置,通常意味着对端异常关闭了连接。
5. 示例代码
客户端使用示例
cpp
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main() {
int sockfd;
struct sockaddr_in server;
char message[1000], server_reply[2000];
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
printf("创建套接字失败\n");
}
server.sin_addr.s_addr = inet_addr("192.168.1.1");
server.sin_family = AF_INET;
server.sin_port = htons(8888);
// 连接到远程服务器
if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("连接失败");
return 1;
}
// 发送数据
strcpy(message, "Hello Server");
if (send(sockfd, message, strlen(message), 0) < 0) {
printf("发送失败\n");
return 1;
}
// 接收服务器响应
if (recv(sockfd, server_reply, 2000, 0) < 0) {
printf("接收失败\n");
}
printf("服务器响应:%s\n", server_reply);
close(sockfd);
return 0;
}
服务器使用示例
cpp
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main() {
int socket_desc, client_sock, c;
struct sockaddr_in server, client;
char client_message[2000];
// 创建套接字
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
printf("无法创建套接字\n");
}
// 配置服务器地址结构
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(8888);
// 绑定
if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("绑定失败");
return 1;
}
// 监听
listen(socket_desc, 3);
// 接受客户端连接
c = sizeof(struct sockaddr_in);
client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_sock < 0) {
perror("接受连接失败");
return 1;
}
// 接收客户端消息
if (recv(client_sock, client_message, 2000, 0) > 0) {
printf("客户端消息:%s\n", client_message);
// 发送响应
send(client_sock, "Hello Client", strlen("Hello Client"), 0);
}
close(client_sock);
close(socket_desc);
return 0;
}
6. 总结
send
和 recv
是套接字通信中的基础操作,理解它们的使用方式和参数对于编写可靠的网络程序至关重要。