我有一台使用epoll的服务器。我的问题是处理所有缓冲区的最佳方法是什么,这样我就可以处理一半的消息或缓冲区中的多条消息。
例如:
如果消息是“您好,来自客户编号#”。
通过epoll收到缓冲区:
从客户端1:“ Hello From”
从客户端2:“从客户端编号2向您好:从客户端2向您好”
从客户端1:“客户端编号1HelloFromClient编号1”
在这种情况下,我需要能够识别“ Hello From”只是消息的一半,并将其存储在某处。然后,我需要能够处理来自客户端2的两条完整消息,然后返回并选择我从客户端1离开的地方。我知道可以使用定界符来区分消息,也可以发送消息长度,但我不确定该如何处理接收一半的消息。
有人对我有任何想法或示例代码吗?
注意 我知道现在将所有内容都处理到一个缓冲区中是不好的。我将改变它。每个客户都需要一个单独的缓冲区吗?
感谢您的帮助!
void epoll(int listening_port)
{
char buffer[500]; //buffer for message
int listen_sock = 0; //file descriptor (fd) for listening socket
int conn_sock = 0; //fd for connecting socket
int epollfd = 0; // fd for epoll
int nfds = 0; //number of fd's ready for i/o
int i = 0; //index to which file descriptor we are lookng at
int curr_fd = 0; //fd for socket we are currently looking at
bool loop = 1; //boolean value to help identify whether to keep in loop or not
socklen_t address_len;
struct sockaddr_in serv_addr;
struct epoll_event ev, events[EPOLL_MAX_EVENTS];
ssize_t result = 0;
bzero(buffer, sizeof(buffer));
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = listening_port;
serv_addr.sin_addr.s_addr = INADDR_ANY;
listen_sock = create_socket();
if(bind(listen_sock, SA &serv_addr, sizeof(serv_addr)) != 0)
{
perror("Bind failed");
}
else
{
printf("Bind successful\n");
}
set_socket_nonblocking(listen_sock);
listen_on_socket(listen_sock, SOMAXCONN); //specifying max connections in backlog
epollfd = initialize_epoll();
ev.events = EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP;
ev.data.fd = listen_sock;
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == ERROR)
{
perror("Epoll_ctl: listen sock");
}
else
{
printf("Successfully added listen socket to epoll\n");
}
while (RUN_EPOLL)
{
nfds = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, 0); //waiting for incoming connection;
if(nfds == ERROR)
{
perror("EPOLL_Wait");
}
//printf("Finished waiting\i");
for(i = 0; i < nfds; ++i)
{
curr_fd = events[i].data.fd;
loop = true; //reset looping flag
//Notification from Listening Socket - Process Incoming Connections
if (curr_fd == listen_sock) {
while(loop)
{
conn_sock = accept(listen_sock, SA &serv_addr, &address_len); //accept incoming connection
printf("Accepted new incoming connection - socket fd: %d\n", conn_sock);
if (conn_sock > 0) //if successful set socket nonblocking and add it to epoll
{
set_socket_nonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLOUT| EPOLLET | EPOLLRDHUP; //setting flags
ev.data.fd = conn_sock; //specify fd of new connection in event to follow
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == ERROR) //add fd to monitored fd's
{
perror("epoll_ctl: conn_sck");
}
else
{
printf("Added %d to monitor list\n", conn_sock);
}
}
else if (conn_sock == ERROR)
{
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
{
printf("All incoming connections processed\n");
loop = false;
}
else
{
perror("Accept remote socket");
loop = false;
}
}
}
}
else if(events[i].events & EPOLLRDHUP) //detecting if peer shutdown
{
printf("Detected socket peer shutdown. Closing now. \n");
if (epoll_ctl(epollfd, EPOLL_CTL_DEL, curr_fd, NULL) == ERROR) {
perror("epoll_ctl: conn_sck");
}
close_socket(curr_fd);
}
else if(events[i].events & EPOLLIN)
{
while(loop)
{
result = recv(curr_fd, buffer, sizeof(buffer), 0);
//printf("Length of incoming message is %d\i", result);
if(result > 0) //
{
printf("File Descriptor: %d. Message: %s\n", curr_fd, buffer); //I know this will need to be changd
bzero(buffer, sizeof(buffer));
}
else if(result == ERROR) //Message is completely sent
{
if(errno == EAGAIN)
{
loop = false;
}
}
else if(result == 0)
{
//Removing the fd from the monitored descriptors in epoll
if (epoll_ctl(epollfd, EPOLL_CTL_DEL, curr_fd, NULL) == ERROR) {
perror("epoll_ctl: conn_sck");
}
close_socket(curr_fd); //Closing the fd
loop = false;
}
}
}
}
}
close_socket(listen_sock);
//need to develop way to gracefully close out of epoll
return;
}
答案 0 :(得分:1)
每个客户我需要一个单独的缓冲区吗?
是的。您将需要为每个客户端存储一个单独的客户端状态。作为该客户端状态的一部分,部分消息缓冲区也应为每个客户端分别存储。
有人对我有任何想法或示例代码吗?
您可以查看the facil.io library的代码,它是raw-HTTP example code。
在示例代码中,您会注意到each HTTP client (protocol / state object)将拥有自己的目标buffer for reading。
facil.io库在幕后使用epoll
(或者在BSD / macOS上使用kqueue
,如果您真的想移植,则使用poll
)-因此,框架的逻辑适用于您的情况。
有时可以使用堆栈分配(或每个线程)缓冲区,但这仅在以后复制需要保留的数据时才适用。