阻塞I/O(Blocking I/O):
- 在阻塞I/O模式下,当程序执行一个I/O操作(如读取文件、网络通信等)时,它会等待直到该操作完成才会继续执行下一步操作。
- 在等待期间,程序的执行会被暂停,无法执行其他任务,直到I/O操作完成或者发生超时。
- 阻塞I/O通常较容易使用和理解,但可能会导致应用程序在高负载或大量I/O操作的情况下性能下降,因为大部分时间都在等待I/O操作完
代码
服务端代码 server.cpp
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main() {
// 创建套接字
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == -1) {
std::cerr << "无法创建套接字" << std::endl;
return 1;
}
// 绑定地址和端口
sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = INADDR_ANY;
serverAddress.sin_port = htons(8080); // 使用8080端口
if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
std::cerr << "绑定失败" << std::endl;
return 1;
}
// 开始监听连接
if (listen(serverSocket, 5) == -1) {
std::cerr << "监听失败" << std::endl;
return 1;
}
std::cout << "服务器正在监听端口 8080..." << std::endl;
while (true) {
// 接受客户端连接(阻塞)
sockaddr_in clientAddress;
socklen_t clientAddressSize = sizeof(clientAddress);
int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressSize);
if (clientSocket == -1) {
std::cerr << "无法接受客户端连接" << std::endl;
return 1;
}
// 与客户端通信(阻塞)
char buffer[1024];
std::memset(buffer, 0, sizeof(buffer));
int bytesRead = read(clientSocket, buffer, sizeof(buffer));
if (bytesRead == -1) {
std::cerr << "读取数据失败" << std::endl;
return 1;
}
std::cout << "收到客户端消息: " << buffer << std::endl;
// 关闭连接
close(clientSocket);
}
// 关闭服务器套接字
close(serverSocket);
return 0;
}
客户端代码 client.cpp
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
int main() {
// 创建套接字
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == -1) {
std::cerr << "无法创建套接字" << std::endl;
return 1;
}
// 连接到服务器
sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(8080); // 与服务器相同的端口
serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器的IP地址
if (connect(clientSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
std::cerr << "无法连接到服务器" << std::endl;
return 1;
}
// 发送消息给服务器
const char* message = "Hello, Server!";
int bytesSent = send(clientSocket, message, strlen(message), 0);
if (bytesSent == -1) {
std::cerr << "发送消息失败" << std::endl;
return 1;
}
// 接收服务器的响应(阻塞)
char buffer[1024];
std::memset(buffer, 0, sizeof(buffer));
int bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0);
if (bytesRead == -1) {
std::cerr << "接收服务器响应失败" << std::endl;
return 1;
}
std::cout << "服务器响应: " << buffer << std::endl;
// 关闭连接
close(clientSocket);
return 0;
}
非阻塞I/O(Non-blocking I/O):
- 在非阻塞I/O模式下,程序会立即返回从I/O操作中获取的结果,而不会等待操作完成。
- 如果I/O操作尚未完成,程序不会被阻塞,而是可以继续执行其他任务,然后定期轮询或使用回调等方式来检查I/O操作是否已完
- 非阻塞I/O通常用于构建高并发的应用程序,因为它允许程序同时处理多个I/O操作,提高了应用程序的性能。
阻塞I/O适用于简单的、低并发的应用程序,而非阻塞I/O更适合需要高并发处理或对I/O操作的响应时间要求很高的应用程序。在实际应用中,通常会使用非阻塞I/O的方式来构建多线程、多进程或异步编程模型,以充分利用计算资源并提高应用程序的性能
代码
服务端代码 server.cpp
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <arpa/inet.h>
int main() {
// 创建套接字
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == -1) {
std::cerr << "无法创建套接字" << std::endl;
return 1;
}
// 设置套接字为非阻塞模式
int flags = fcntl(serverSocket, F_GETFL, 0);
fcntl(serverSocket, F_SETFL, flags | O_NONBLOCK);
// 绑定地址和端口
sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = INADDR_ANY;
serverAddress.sin_port = htons(8080); // 使用8080端口
if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
std::cerr << "绑定失败" << std::endl;
return 1;
}
// 开始监听连接
if (listen(serverSocket, 5) == -1) {
std::cerr << "监听失败" << std::endl;
return 1;
}
std::cout << "服务器正在监听端口 8080..." << std::endl;
while (true) {
// 接受客户端连接(非阻塞)
sockaddr_in clientAddress;
socklen_t clientAddressSize = sizeof(clientAddress);
int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressSize);
if (clientSocket == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 没有新连接,继续循环
continue;
} else {
std::cerr << "无法接受客户端连接" << std::endl;
return 1;
}
}
// 与客户端通信(非阻塞)
char buffer[1024];
std::memset(buffer, 0, sizeof(buffer));
int bytesRead = read(clientSocket, buffer, sizeof(buffer));
if (bytesRead == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 没有数据可读,继续循环
close(clientSocket);
continue;
} else {
std::cerr << "读取数据失败" << std::endl;
return 1;
}
} else if (bytesRead == 0) {
// 客户端关闭连接
close(clientSocket);
continue;
}
std::cout << "收到客户端消息: " << buffer << std::endl;
// 关闭连接
close(clientSocket);
}
// 关闭服务器套接字
close(serverSocket);
return 0;
}
客户端代码 client.cpp
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
int main() {
// 创建套接字
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == -1) {
std::cerr << "无法创建套接字" << std::endl;
return 1;
}
// 设置套接字为非阻塞模式
int flags = fcntl(clientSocket, F_GETFL, 0);
fcntl(clientSocket, F_SETFL, flags | O_NONBLOCK);
// 连接到服务器
sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(8080); // 与服务器相同的端口
serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器的IP地址
if (connect(clientSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
if (errno != EINPROGRESS) {
std::cerr << "无法连接到服务器" << std::endl;
return 1;
}
}
// 使用select来等待连接完成
fd_set writeSet;
FD_ZERO(&writeSet);
FD_SET(clientSocket, &writeSet);
struct timeval timeout;
timeout.tv_sec = 5; // 设置超时时间为5秒
timeout.tv_usec = 0;
int selectResult = select(clientSocket + 1, nullptr, &writeSet, nullptr, &timeout);
if (selectResult == -1) {
std::cerr << "select调用失败" << std::endl;
return 1;
} else if (selectResult == 0) {
std::cerr << "连接超时" << std::endl;
return 1;
}
// 发送消息给服务器
const char* message = "Hello, Server!";
int bytesSent = send(clientSocket, message, strlen(message), 0);
if (bytesSent == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 缓冲区不可用,继续循环
close(clientSocket);
return 1;
} else {
std::cerr << "发送消息失败" << std::endl;
return 1;
}
}
// 关闭连接
close(clientSocket);
return 0;
}