在TCP Socket网络编程中,接收和发送的阻塞和非阻塞是指调用相应的接收和发送函数时,该函数在什么条件下会立即返回。
- 阻塞:
当调用阻塞式接收函数时,如果当前没有数据可用,函数会一直等待,直到有数据可用为止。类似地,当调用阻塞式发送函数时,如果发送缓冲区已满,函数会一直等待,直到缓冲区中有足够的空间来存储要发送的数据。
- 非阻塞:
当调用非阻塞式接收函数时,如果当前没有数据可用,函数会立即返回,返回值为0或错误码,应用程序需要不断调用该函数来检查是否有新数据到达。类似地,当调用非阻塞式发送函数时,如果发送缓冲区已满,函数会立即返回,返回值为0或错误码,应用程序需要不断调用该函数来检查缓冲区是否有足够的空间来存储要发送的数据。
阻塞和非阻塞的主要区别在于函数的调用方式和返回值,阻塞函数会一直等待,直到满足条件才会返回,而非阻塞函数会立即返回,无论条件是否满足。阻塞函数通常会使应用程序挂起,等待数据可用或缓冲区可用,而非阻塞函数则允许应用程序在等待数据到达的同时继续执行其他任务。
下面是使用阻塞和非阻塞方式发送和接收数据的代码示例:
阻塞模式:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
int sockfd;
char buf[1024];
struct sockaddr_in server_addr;
// 创建socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 设置server地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(8080);
// 连接server
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("connect");
exit(EXIT_FAILURE);
}
// 阻塞接收数据
int ret = recv(sockfd, buf, sizeof(buf), 0);
if (ret == -1) {
perror("recv");
exit(EXIT_FAILURE);
}
printf("Received: %s\n", buf);
char *msg = "Hello, Server!";
int len = strlen(msg);
// 阻塞发送数据
ret = send(sockfd, msg, len, 0);
if (ret == -1) {
perror("send");
exit(EXIT_FAILURE);
}
close(sockfd);
return 0;
}
非阻塞模式:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
int sockfd;
char buf[1024];
struct sockaddr_in server_addr;
// 创建socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 设置为非阻塞模式
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
// 设置server地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(8080);
// 连接server
int ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret == -1 && errno != EINPROGRESS) {
perror("connect");
exit(EXIT_FAILURE);
}
// 等待连接完成
fd_set write_set;
FD_ZERO(&write_set);
FD_SET(sockfd, &write_set);
ret = select(sockfd + 1, NULL, &write_set, NULL, NULL);
if (ret == -1) {
perror("select");
exit(EXIT_FAILURE);
} else if (ret == 0) {
printf("Connection timed out.\n");
exit(EXIT_FAILURE);
}
// 非阻塞接收数据
while (1) {
ret = recv(sockfd, buf, sizeof(buf), 0);
if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 没有数据可读,等待一段时间再重试
usleep(1000);
continue;
} else {
perror("recv");
exit(EXIT_FAILURE);
}
} else if (ret == 0) {
printf("Server closed.\n");
break;
} else {
buf[ret] = '\0';
printf("Received: %s\n", buf);
break;
}
}
char *msg = "Hello, Server!";
int len = strlen(msg);
// 非阻塞发送数据
int sent = 0;
while (sent < len) {
ret = send(sockfd, msg + sent, len - sent, 0);
if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 发送缓冲区已满,等待一段时间再重试
usleep(1000);
continue;
} else {
perror("send");
exit(EXIT_FAILURE);
}
} else {
sent += ret;
}
}
close(sockfd);
return 0;
}
上面的代码中,首先通过`fcntl`函数将socket设置为非阻塞模式。在非阻塞模式下,当调用`connect`函数时,连接操作会立即返回,无论连接是否成功。此时需要使用`select`函数来等待连接完成,然后才能进行数据通信。
在非阻塞模式下,当调用`recv`函数时,如果没有数据可读,该函数会立即返回,返回值为-1,同时设置`errno`为`EAGAIN`或`EWOULDBLOCK`。此时需要等待一段时间再重试。同样,当调用`send`函数时,如果发送缓冲区已满,该函数会立即返回,返回值为-1,同时设置`errno`为`EAGAIN`或`EWOULDBLOCK`。此时需要等待一段时间再重试,直到所有数据发送完毕。
注意:根据自己的需求进行略作修改。