将套接字设置为非阻塞模式,可以使用 fcntl
函数。以下是如何在 C++ 中实现这一点的示例,包括对 TCP 和 UDP 的非阻塞套接字设置。
设置非阻塞套接字
#include <iostream>
#include <cstring>
#include <fcntl.h> // for fcntl()
#include <arpa/inet.h>
#include <unistd.h>
int setNonBlocking(int sockfd) {
int flags = fcntl(sockfd, F_GETFL, 0);
if (flags == -1) {
std::cerr << "Failed to get socket flags!" << std::endl;
return -1;
}
if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
std::cerr << "Failed to set non-blocking!" << std::endl;
return -1;
}
return 0; // Success
}
void connectToPeer(const char* ip, int port) {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
std::cerr << "Socket creation failed!" << std::endl;
return;
}
// Set the socket to non-blocking mode
if (setNonBlocking(sockfd) == -1) {
close(sockfd);
return;
}
struct sockaddr_in peerAddr;
memset(&peerAddr, 0, sizeof(peerAddr));
peerAddr.sin_family = AF_INET;
peerAddr.sin_port = htons(port);
// Convert IP address from text to binary form
inet_pton(AF_INET, ip, &peerAddr.sin_addr);
// Initiate connection (this will return immediately in non-blocking mode)
if (connect(sockfd, (struct sockaddr*)&peerAddr, sizeof(peerAddr)) == -1) {
if(errno != EINPROGRESS){
std::cerr << "Connection failed!" << std::endl;
close(sockfd);
return;
}
}
char buffer[1024];
while (true) {
std::string message;
std::cout << "Enter message: ";
getline(std::cin, message);
send(sockfd, message.c_str(), message.size(), 0); // Send the message
ssize_t bytesReceived = recv(sockfd, buffer, sizeof(buffer)-1, 0); // Receive a response
if (bytesReceived > 0) {
buffer[bytesReceived] = '\0'; // Null-terminate the received data
std::cout << "Received from peer: " << buffer << std::endl;
} else if(bytesReceived == -1 && errno != EAGAIN && errno != EWOULDBLOCK){
perror("recv error");
}
if (message == "exit") break; // Exit condition
}
close(sockfd);
}
int main() {
const char* remoteIp = "192.168.x.x"; // Replace with the other client's IP address
int remotePort = 5000; // The port you wish to connect to
connectToPeer(remoteIp, remotePort);
return 0;
}
UDP 非阻塞示例
对于 UDP 套接字,你可以使用类似的方法来设置它为非阻塞:
void udpClient() {
int sockfd = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP);
setNonBlocking(sockfd); // Set as non-blocking
struct sockaddr_in broadcastAddr;
memset(&broadcastAddr, 0, sizeof(broadcastAddr));
broadcastAddr.sin_family = AF_INET;
// Enable broadcasting on this socket
int optval = 1;
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&optval,sizeof(optval));
broadcastAddr.sin_port = htons(5000);
broadcastAddr.sin_addr.s_addr = INADDR_BROADCAST;
while(true){
std::string message("Hello from client");
sendto(sockfd,message.c_str(),message.length(),0,
(struct sockaddr*)&broadcastAddr,sizeof(broadcastAddr));
sleep(1); // Sleep for a second before sending next broadcast.
}
close(sockfd);
}
void udpServer() {
int sockfd = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP);
setNonBlocking(sockfd); // Set as non-blocking
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr=INADDR_ANY;
serverAddr.sin_port=htons(5000);
bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr));
while(true) {
char buffer[1024];
struct sockaddr_in clientAddress;
socklen_t clientLength=sizeof(clientAddress);
ssize_t bytesReceived = recvfrom(sockfd , buffer , sizeof(buffer)-1 , MSG_WAITALL,
(struct sockaddr*)&clientAddress , &clientLength);
if(bytesReceived > 0 ) {
buffer[bytesReceived] = '\0'; // Null terminate received string
std::cout<< "Received: "<<buffer<<" from "<<inet_ntoa(clientAddress.sin_addr)<<std::endl;
} else if(bytesReceived == -1 && errno != EAGAIN && errno != EWOULDBLOCK){
perror("recvfrom error");
}
}
close(sockfd);
}
注意事项:
- 使用非阻塞套接字时,可能会因为没有数据可读而导致
recv
或recvfrom
返回-1
,并设置errno
为EAGAIN
或EWOULDBLOCK
。这只是意味着当前没有数据可用,而不是错误。 - 在生产代码中,请确保适当地处理所有返回值和错误情况,以便确保程序稳定性。
如果你有其他问题或需要进一步帮助,请随时询问!
内容由零声教学AI助手提供,问题来源于学员提问