Is it possible to use EPOLL to send tasks to multiple client - c

I am building a server/client application. The server will need to deal with less than 1000+ clients. I currently have a simple EPOLL implementation to be able to receive information from the clients to the server. However, I will need to be able to send tasks to specific clients as well. My question is there a way to use EPOLL to identify which clients I need to send a task too (potentially using the EPOLLOUT flag) and send the message out. I've attached the snippet of what I currently have. If possible, how would I go about implementing this. It would be great to be able to have one epoll for all the sending an the receiving if it could work like that.
Thanks for any help/recommendations!
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);
bzero(buffer, sizeof(buffer));
}
else if(result == ERROR) //Message is completely sent
{
if(errno == EAGAIN)
{
send_message(curr_fd, "Success", strlen("Success"));
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;
}

Related

Multiple ports server in C

I have a server that has 2 ports with a socket for each port, each of them doing different thigs. I made this work with epoll but I have a question, can 2 different clients connect to each port and use the functionality offered by them at the same time? For example: client1 connects to port X and wants to read data and client2 connects at the same time and wants to write data at the port Y. Is this possible? Thanks!
// create the 2 sockets for each port
// bind(), listen() for each
// tried to do this 'parallelism' using epoll but after someone connects
// to a port that port takes over, the other can't be used
epollfd = epoll_create1(0);
if (epollfd == -1) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = sd;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sd, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
ev2.events = EPOLLIN;
ev2.data.fd = sd2;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sd2, &ev2) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
for (int n = 0; n < nfds; ++n) {
if (events[n].data.fd == sd2) {
// handle the clients at PORT2 where I just show data from
// database, I'm using processes to handle clients
}
else if (events[n].data.fd == sd) {
// handle the clients at PORT1 where they can update/add/delete
// from data base, I'm using processes to handle clients
}

How to make server not wait on accept after first client?

I have a tcp server and I want to make it so after it accepts the first client it doesn't wait on the accept function and just goes on if no-one else has connected
while (1)
{
SOCKET ClientSocket = accept(Socket, (sockaddr *)&Client, &ClientSize);
if(ClientSocket == INVALID_SOCKET)
{
Error("accept failed.\n");
}
ClientSockets[ClientSocketsIndex++] = ClientSocket;
Result = connect(ClientSocket, (sockaddr*)&Client, ClientSize);
sockaddr_in From;
int FromSize = sizeof(From);
Result = recv(ClientSocket, Message, sizeof(Message), 0);
if (Result == SOCKET_ERROR)
{
Error("recv failed.\n");
}
Result2 = getpeername(ClientSocket, (sockaddr*)&From, &FromSize);
if (Result2 == SOCKET_ERROR)
{
Error("getpeername failed.\n");
}
Here I take the ClientSocket with accept() and add it to an array where I store all ClientSockets and then receive a buffer. The problem is that after each loop it waits for a new ClientSocket.
The Error() function is just a one I made for printing to the console and quitting if I encounter an Error btw.
First off, calling connect() on the SOCKET returned by accept() is wrong. Get rid of that.
Second, if you want your loop to service multiple clients properly, then use select() (or equivalent). Don't call accept() until select() tells you that a new client is waiting to be accepted. Likewise, don't call recv() until select() tells you that a client in your array is waiting to be read from.
Try something more like this:
const size_t MAX_SOCKETS = ...;
SOCKET ClientSockets[MAX_SOCKETS];
size_t NumClientSockets = 0;
...
while (true)
{
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(Socket, &readfds);
for(int i = 0; i < NumClientSockets; ++i)
{
FD_SET(ClientSockets[i], &readfds);
}
if (select(0, &readfds, NULL, NULL, NULL) > 0)
{
if (FD_ISSET(Socket, &readfds))
{
ClientSize = sizeof(Client);
SOCKET ClientSocket = accept(Socket, (sockaddr *)&Client, &ClientSize);
if (ClientSocket == INVALID_SOCKET)
{
Error("accept failed.\n");
}
else if (NumClientSockets == MAX_SOCKETS)
{
closesocket(ClientSocket);
}
else
{
ClientSockets[NumClientSockets] = ClientSocket;
++NumClientSockets;
}
}
size_t i = 0;
while (i < NumClientSockets)
{
if (!FD_ISSET(ClientSockets[i], &readfds))
{
++i;
continue;
}
Result = recv(ClientSockets[i], Message, sizeof(Message), 0);
if (Result <= 0)
{
if (Result == SOCKET_ERROR)
Error("recv failed.\n");
for(size_t j = i + 1; j < NumClientSockets; ++j)
{
ClientSockets[j - 1] = ClientSockets[j];
}
--NumClientSockets;
continue;
}
// use Message up to Result bytes as needed...
++i;
}
}
}

Can't figure out epoll example in manual

Recently, I am study system call epoll. I already have basic concept about this topic, but I stuck in example given by manual.
/* Code to set up listening socket, 'listen_sock',
(socket(), bind(), listen()) omitted */
epollfd = epoll_create1(0);
if (epollfd == -1) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &addr, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events[n].data.fd);
}
}
}
Following is my assumption:
Above is server code.
listen_sock is file descriptor return from socket() in server.
My question is:
What did file descriptor events[n].data.fd mean? Is server's fd or client's?
Why if events[n].data.fd == listen_sock , then we need to create a new connection?
You can look at the man page for epoll_wait for details on struct epoll_event and its data member and to help make sense of the code.
Any given events[n].data.fd refers to an fd that had some kind of event on it. It could either be the listening socket, or it could be a client socket if there are active client connections.
If the fd with activity is the listen_sock listening socket, this means that a new client is attempting a connection. At this point we accept the connection and add the new conn_sock fd to the set of epoll's fds.

Can not re-connect to server via socket after it is crashed?

I have a system where two machines are connected, and send/receive information to each other. One of the servers sends RTP voice packets using RTSP protocol to the other one. I should implement an edge case, and it requires the following;
When the server goes down/crashes, socket connection is lost. I should create and connect a new socket when the server is available. In this regard, I remove the existing socket, create a new one ( with a new file descriptor ). But it does not connect to the server even if it is available..
static int socket_new_try_connect( struct rtsp_session *s )
{
int flags;
int res = -1;
socklen_t slen;
struct hostent *he;
struct ast_hostent h;
while( res < 0 )
{
sleep(2);
if ((he=ast_gethostbyname("10.1.1.6", &h)) == NULL) {
log("socket_new_try_connect::ast_gethostbyname ERROR !\n");
continue;
}
memset(&s->sin, 0, sizeof(s->sin));
s->sin.sin_family = AF_INET;
s->sin.sin_port = htons(8554);
memmove(&s->sin.sin_addr, he->h_addr, sizeof(s->sin.sin_addr));
/* socket */
s->sock = socket(AF_INET, SOCK_STREAM, 0);
if (s->sock < 0) {
log("socket_new_try_connect::Unable to create RTSP session socket\n");
continue;
}
log("socket_new_try_connect::new RTSP socket is created. \n");
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
/*if(setsockopt(s->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&timeout, sizeof(timeout)) < 0){
perror("setsockopt() SO_REUSEADDR");
close(s->sock);
continue;
}*/
if(setsockopt(s->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0){
log(LOG_ERROR, "setsockopt, EXIT FAILURE!\n");
perror("setsockopt()");
close(s);
continue;
}
if(setsockopt(s->sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0){
perror("setsockopt()");
close(s);
continue;
}
flags = fcntl(s->sock, F_GETFL);
fcntl(s->sock, F_SETFL, flags | O_NONBLOCK);
while(1)
{
res = connect(s->sock, (struct sockaddr *)&s->sin, sizeof(s->sin));
log(LOG_NOTICE, "socket_new_try_connect::connect RESULT : %d \n",res);
if(res < 0 && errno != EINPROGRESS) {
sleep(1);
continue;
}
else if( res < 0 )
{
log("socket_new_try_connect::Cannot connect to RTSP recording server. Error :%s\n", strerror(errno));
close(s->sock);
break;
}
else
{
log("socket_new:: SOCKET CONNECT IS SUCCESSFULL ! RESULT : %d \n",res);
return 1;
}
}
}
return 1;
}
With the code above, I am getting "Operation in progress" error. I also saved the messages when the server is available again after getting crashed;
tcp messages
Could you please help me to elaborate the problem ?

Transport end already connected error : 106 with connect()

I have started implementing a server-client model with UNIX domain sockets of SOCK_STREAM type.
Thanks in advance
You need to avoid connecting again if your socket is already connected, you can keep some flag in your client code that indicates your client socket is already connected or not, based on that skip connecting again.
I have edited your server code below to show what I am trying to say in comments(You need to put accepted fd in select read set too, so that you can check any of clients posted any data, also you have multiple clients connected to you so you will have to keep accepted sockets in an array):
int cltfdlen = 0;
int cltfds[FD_SETSIZE];
int maxFD = sockfd;
int sun_path_size = sizeof(client_address.sun_path);
client_address.sun_family = AF_UNIX;
strncpy(client_address.sun_path, SOCKET_NAME, sun_path_size);
client_len = sizeof(client_address);
cli_interval.tv_sec = 60;
cli_interval.tv_usec = 0;
while (1) {
FD_ZERO(&read_fds);
FD_SET(sockfd, &read_fds);
for(i=0;i<cltfdlen;cltfdlen++)
FD_SET(cltfds[i], &read_fds);
int activity = select(maxFD + 1, &read_fds, NULL, NULL, &cli_interval);
if ((activity < 0) && (errno !=EINTR)) {
printf("socket select failed errno %d\n", errno);
return 1;
}
if (FD_ISSET(sockfd, &read_fds)) {
cli_sockfd = accept(sockfd,
(struct sockaddr *)&client_address,
&client_len);
if(cli_sockfd < 0) {
printf("accept from IPC socket failed");
return 1;
}
else
{
cltfds[cltfdlen++] = cli_sockfd;
if(maxFD < cli_sockfd)
maxFD = cli_sockfd
continue;
}
}
msg = (msg_t *) malloc(sizeof(msg_t));
if (msg == NULL) {
printf("Memory allocation failed for msg");
close(ipc_sockfd);
ipc_sockfd = -1;
return 1;
}
memset(msg, 0, sizeof(msg));
for(i=0;i<cltfdlen;i++)
{
if(FD_ISSET(cltfds[i], &read_fds))
{
if (read(cltfds[i], (void *)msg, sizeof(msg_t)) == -1) {
printf("Read from IPC socket failed");
return 1;
}
}
}
close(cli_sockfd);
cli_sockfd = -1;
}

Resources