我写了一个epoll服务器和一个简单的客户端。当客户端发送消息后,立即退出,服务器不会注意到断开连接。
events[i].events & EPOLLERR || events[i].events & EPOLLHUP 这个判断语句根本没有任何用处。 我的 epoll 服务器有哪些地方需要改进? 我真的很想知道,拜托了!
这是我的服务器.cc
#include "server.h"
#include "../threadpool/threadpool.h"
static std::string getIpAddress()
{
struct ifaddrs *ifAddrStruct = nullptr;
struct ifaddrs *ifa = nullptr;
void *tmpAddrPtr = nullptr;
std::string ipAddress;
getifaddrs(&ifAddrStruct);
for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr)
continue;
if (ifa->ifa_addr->sa_family == AF_INET)
{
tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
if (strcmp(addressBuffer, "127.0.0.1") != 0)
{
ipAddress = std::string(addressBuffer);
break;
}
}
}
if (ifAddrStruct != nullptr)
{
freeifaddrs(ifAddrStruct);
}
return ipAddress;
}
static void setFdNoblock(int fd)
{
fcntl(fd, F_SETFL, fcntl(fd, F_SETFL) | O_NONBLOCK);
}
server::server(int port)
{
epoll_fd = epoll_create1(0);
if(epoll_fd == -1)
throw std::runtime_error("Error in epoll_create1");
struct sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if(server_socket == -1)
throw std::runtime_error("Error in socket");
int reuse = 1;
if(setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
throw std::runtime_error("Error in setsocketopt");
if(bind(server_socket, reinterpret_cast<struct sockaddr*>(&server_addr), sizeof(server_addr)) == -1)
throw std::runtime_error("Error in bind");
if(listen(server_socket, SOMAXCONN) == -1)
throw std::runtime_error("Error in listen");
setFdNoblock(server_socket);
std::cout << "server start on " << getIpAddress() << ':' << port << '\n';
}
server::~server()
{
close(server_socket);
close(epoll_fd);
}
void handleClient(int client_socket)
{
printf("success!!!\n");
}
static int readn(int fd, char* buf, int size)
{
char* pt = buf;
int count = size;
while(count > 0)
{
int len = recv(fd, pt, count, 0);
if(len == -1)
return -1;
else if(len == 0)
return size-count;
pt += len;
count -= len;
}
return size;
}
static int recvMsg(int cfd, char** msg)
{
int len = 0;
readn(cfd, (char*)&len, 4);
len = ntohl(len);
char *buf = (char*)malloc(len+1);
int ret = readn(cfd, buf, len);
if(ret != len)
{
close(cfd);
free(buf);
return -1;
}
buf[len] = '\0';
*msg = buf;
return ret;
}
void server::run()
{
struct epoll_event event{};
event.events = EPOLLIN;
event.data.fd = server_socket;
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &event) == -1)
throw std::runtime_error("Error in epoll_ctl");
threadpool threadPool(THREAD_NUM);
epoll_event events[MAX_EVENTS];
while(true)
{
int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if(num_events == -1)
throw std::runtime_error("Error in epoll_wait");
for(int i = 0; i < num_events; i++)
{
if(events[i].data.fd == server_socket)
{
std::cout << "accepting new client...\n";
int client_socket = accept(server_socket, nullptr, nullptr);
if(client_socket == -1)
{
std::cerr << "Failed to accept client connection\n";
continue;
}
event.events = EPOLLIN | EPOLLET;
event.data.fd = client_socket;
setFdNoblock(client_socket);
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_socket, &event);
std::cout << "client on line client_socket: " << client_socket << '\n';
threadPool.enqueue(handleClient, client_socket);//---------------to do
}
else
{
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
{
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, nullptr);
std::cout << "Client disconnected by EPOLLERR | EPOLLHUP: " << events[i].data.fd << '\n';
close(events[i].data.fd);
}
else if(events[i].events & EPOLLIN)
{
char* msg = nullptr;
int ret = recvMsg(events[i].data.fd, &msg);
if(ret == -1)
{
std::cerr << "Failed to receive message from client_socket: " << events[i].data.fd << '\n';
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, nullptr);
close(events[i].data.fd);
}
else if (ret == 0)
{
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, nullptr);
std::cout << "Client disconnected: " << events[i].data.fd << '\n';
close(events[i].data.fd);
}
else
{
std::cout << "Received message from client_socket: " << events[i].data.fd << '\n';
std::cout << msg << '\n';
free(msg);
}
}
}
}
}
}
这是我的客户.cc
#include <sys/socket.h>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cerrno> // For errno
#include <sys/types.h>
#define PORT_NUM 8000
#define IP "10.30.0.245"
#define BUF_SIZE 2048
void errExit(const char* error)
{
perror(error);
exit(EXIT_FAILURE);
}
int writen(int fd, const char* msg, int size)
{
const char* buf = msg;
int count = size;
while(count > 0)
{
int len = send(fd, buf, count, 0);
if(len == -1)
{
if(errno == EPIPE || errno == ECONNRESET)
{
printf("Network connection reset or broken.\n");
return -1; // Return -1 to indicate network error
}
else
{
perror("send");
close(fd);
return -1;
}
}
else if(len == 0)
{
continue;
}
buf += len;
count -= len;
}
return size;
}
int sendMsg(int cfd, const char* msg, int len)
{
if(msg == nullptr || len <= 0 || cfd <= 0)
{
return -1;
}
char* data = (char*)malloc(len+4);
if(data == nullptr)
{
perror("malloc");
return -1;
}
int bigLen = htonl(len);
memcpy(data, &bigLen, 4);
memcpy(data+4, msg, len);
int ret = writen(cfd, data, len+4);
free(data);
return ret;
}
int main(void) {
int sfd;
struct sockaddr_in svaddr;
char buf[BUF_SIZE];
sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd == -1)
errExit("socket");
memset(&svaddr, 0, sizeof(struct sockaddr_in));
svaddr.sin_family = AF_INET;
svaddr.sin_port = htons(PORT_NUM);
inet_pton(AF_INET, IP, &svaddr.sin_addr);
if(connect(sfd, (const struct sockaddr*) &svaddr, sizeof(struct sockaddr_in)) == -1)
errExit("connect");
printf("\nPlease input: ");
memset(buf, 0, BUF_SIZE);
if(fgets(buf, BUF_SIZE, stdin) == nullptr)
{
printf("Failed to read input.\n");
close(sfd);
return EXIT_FAILURE;
}
int len = strlen(buf);
// Remove trailing newline if present
if (len > 0 && buf[len-1] == '\n') {
buf[len-1] = '\0';
len--;
}
if (len == 0) {
printf("Input is empty.\n");
close(sfd);
return EXIT_FAILURE;
}
if (strcmp(buf, "exit") == 0) {
printf("Exiting...\n");
close(sfd);
return EXIT_SUCCESS;
}
if(sendMsg(sfd, buf, len) == -1) {
errExit("sendMsg");
}
close(sfd);
return EXIT_SUCCESS;
}
我希望服务器注意到客户端已断开连接,我该怎么办?
如果我正在调试这段代码,我会立即做两件事:
std::cout << "Server event 0x" << std::hex << static_cast<unsigned>(events[i].events) << std::endl;
由于您的客户端发送数据,然后立即关闭其套接字,因此您可能会收到一个已合并为 (
EPOLLIN | EPOLLHUP
) 的事件掩码。您的服务器代码无法处理该问题...