So, I am new to socket programming in C and am using the select function to communicate with multiple clients on a server. The server essentially just echos a buffer back to a client based on a requirest. I have used Beej's guide to network programming as the model for my server. What is not clear to me is whether I am exiting the server properly when a command is sent to exit. The code for handling the select function looks like:
for (;;)
{
read_fds = master; // Copy the master fds to the basic read...
// Check to see if any flags have been set for reading
if (select(fdmax + 1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select");
exit(4);
}
for (i = 0; i <= fdmax; i++)
{
if (FD_ISSET(i, &read_fds))
{
if (i == listener)
{ // need to add new connnection here
addrlen = sizeof remote_addr;
newfd = accept(listener, (struct sockaddr *)&remote_addr, &addrlen);
if (newfd == -1)
{
perror("accept");
}
else
{
FD_SET(newfd, &master);
if (newfd > fdmax)
{
fdmax = newfd;
}
}
} // end add new listener
else
{
/*if (i == 0)
{
printf("Input received from stdin\n");
continue;
} */
// handle data from existing client
if ((nbytes = recv(i, input_buffer, sizeof input_buffer, 0)) <= 0)
{ // Remove connection if there is a hangup...
if (nbytes == 0)
{
printf("selectserver: socket%d hung up\n", i);
}
else
{
perror("recv");
}
close(i);
FD_CLR(i, &master);
} // no bytes error or port closed - remove from fdset
else
{
if (strchr(input_buffer,'\r') == NULL){
printf("we have a problem\n");
}
if (strcmp(input_buffer, "exit")){
printf("Exit requested...\n");
close(listener);
exit(0);
}
for (j = 0; j <= fdmax; j++)
{
if (FD_ISSET(j, &master))
{
if (j != listener && j != 0)
{
if (send(j, input_buffer, nbytes, 0) == -1)
{
error_msg = strerror(errno);
printf("%s\n", error_msg);
//perror("send");
}
}
}
}
}
}
}
}
}
and the code I am specifically concerned about is
if (strcmp(input_buffer, "exit")){
printf("Exit requested...\n");
close(listener);
exit(0);
}
where listener is the file descriptor for the listening socket. Is this the correct way of exiting this loop or is there a better way to handle this?
What you are doing is correct.
The proper way to close a socket, whether it is a connected socket or a listening socket, is with close.
Related
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;
}
}
}
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;
}
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;
}
I am running into some problem with my server. I am trying to reading inputs from multiple clients but I can only do it one at a time. I think my server's code does not have an issue. So I assume that my local clients are the ones that are actually at fault here. Are each clients exactly the same as the other if I want to handle multiple clients with different sockets. or are they different in some ways?
What do you guys think?
Server.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
//char *socket_path = "./socket";
char *socket_path = "\0hidden";
int main(int argc, char *argv[]) {
struct sockaddr_un addr;
//max client sockets
int max_sd, sd, i, client_socket[3], activity;
char buf[100];
int fd,cl,rc;
fd_set readfds;
if (argc > 3) socket_path=argv[3];
//establish socket
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
//establish socket name
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
unlink(socket_path);
//bind socket
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("bind error");
exit(-1);
}
//listen to the socket
if (listen(fd, 5) == -1) {
perror("listen error");
exit(-1);
}
while (1) {
//clear the scoket set
FD_ZERO(&readfds);
//add fd socket to set
FD_SET(fd, &readfds);
max_sd = fd;
//add child sockets to the set
for(i = 0; i < 3; i++) {
sd = client_socket[i];
if(sd > 0) {
FD_SET( sd, &readfds);
}
if(sd > max_sd) {
max_sd = sd;
}
}
//wait for an activity from a socket
activity = select(max_sd + 1, &readfds,NULL, NULL,NULL);
if((activity < 0) && (errno!=EINTR)) {
printf("select");
}
if(FD_ISSET(fd, &readfds)) {
if ( (cl = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
while ( (rc=read(cl,buf,sizeof(buf))) > 0) {
printf("read %u bytes: %.*s\n", rc, rc, buf);
}
if (rc == -1) {
perror("read");
exit(-1);
}
else if (rc == 0) {
printf("EOF\n");
close(cl);
}
}
}
return 0;
}
What?
//wait for an activity from a socket
activity = select(max_sd + 1, &readfds,NULL, NULL,NULL);
if((activity < 0) && (errno!=EINTR)) {
printf("select");
}
if(FD_ISSET(fd, &readfds)) {
if ( (cl = accept(fd, NULL, NULL)) == -1) {
Accepting a connection...
perror("accept error");
continue;
}
while ( (rc=read(cl,buf,sizeof(buf))) > 0) {
printf("read %u bytes: %.*s\n", rc, rc, buf);
}
Blocking read until there is no error... (with incorrect printf)
if (rc == -1) {
perror("read");
exit(-1);
}
else if (rc == 0) {
printf("EOF\n");
close(cl);
}
Incorrect, but assuming no real errors means reading till the other party closes their side.
With this in mind, how was this supposed to support several clients at once?
Read this: http://www.kegel.com/c10k.html
I don't know how sockets work in C, but I've been working on some Java programs, and the solution to your problem, in java, is to provide a separate thread on the server for each client. Each time a client connects to the server, you start a thread that will serve that client. A fork will help maybe.
I'm having a really hard time figuring out how to exit the loop on the receiver side. I have a tcp stream socket connection and I'm sending what I read from a file and putting it in the buffer. The receiver end just keeps looping writing the same info over and over. I'm sure it's because i'm not decrementing nRecv so it never hits the if(nRecv == 0) statement but i can't figure out how to decrement it. I'm posting the while loop for both sender and receiver hopefully someone can point me in the right direction.
sender
/* prepare file to send */
pf = fopen("input.txt", "rb");
if(pf == NULL)
{
printf("The file you want to send was not found");
return(1);
}
else
{
while (!feof(pf))
{
nRead = fread(bufferin, sizeof(char), 256, pf);
if (nRead <= 0)
printf("ERROR reading file");
while (nRead > 0)
{
nSent = send(filesender_socket, bufferin, nRead, 0);
if (nSent < 0)
{
printf("ERROR sending from socket = %d\n", WSAGetLastError());
break;
}
if (nSent == 0)
printf("DISCONNECTED writing to socket");
//pBuf += nSent;
//nRead -= nSent;
}
}
} //end of if statement
// Close all open sockets
#ifdef WIN
//retcode = closesocket(jsender_socket);
retcode = closesocket(filesender_socket);
if (retcode < 0)
{
printf("*** ERROR - closesocket() failed \n");
exit(-1);
}
#endif
RECEIVER
//create a new socket for file transfer
filesocket = socket(AF_INET, SOCK_STREAM, 0);
if (filesocket < 0)
{
printf("*** ERROR - socket() failed \n");
exit(-1);
}
// >>> Step #2 <<<
// Fill-in my socket's address information
receiver_addr.sin_family = AF_INET; // Address family to use
receiver_addr.sin_port = htons(PORT_FILE); // Port number to use
receiver_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on any IP address
filebindcode = bind(filesocket, (struct sockaddr *)&receiver_addr,sizeof(receiver_addr));
if (filebindcode < 0)
{
printf("*** ERROR - file socket bind() failed \n");
exit(-1);
}
printf("receiver accepting connections\n");
//2 DEBUG LINES
printf("received a connection from: %s port %d\n",
inet_ntoa(sender_addr.sin_addr), ntohs(sender_addr.sin_port));
if (newsockfd = listen(filesocket, 2) < 0) {
printf("newsock in listen %d\n", newsockfd);
perror("listen failed");
exit(1);
}
//listen(filesocket, 2);
//while(1) //while loop for to accept files
//{
printf("\nwaiting for accept() to complete \n");
newsockfd = accept(filesocket, (struct sockaddr *) &sender_addr, &addr_len);
printf("newsock return %d", newsockfd);
if (newsockfd < 0)
{
printf("*** ERROR - accepting() failed \n");
exit(-1);
}
// start receiving file
fp = fopen("output.txt", "wb");
if (fp == NULL)
{
printf("File not found!\n");
return NULL;
}
else
{
printf("created file output.txt\n");
}
//receive file
while(1)
{
nRecv = recv(newsockfd, bufferin, 256, 0);
if (nRecv < 0)
{
printf("ERROR reading from socket = %d\n", WSAGetLastError());
break;
}
if (nRecv == 0)
break;
while (nRecv > 0)
{
printf("%s", bufferin); // debug
nWritten = fwrite(bufferin, sizeof(char), nRecv, fp);
if (nWritten <= 0)
printf("ERROR writing to file");
//nRecv -= nWritten;
}
}
printf("File Transfer complete\n\n"); //} //end of while
The return value of recv() is as follows:
>0 -- the number of bytes received
0 -- no data (async sockets only) or other socket was closed cleanly
<0 -- an error occurred.
In your sender code, you never close the socket, so your receiver waits for more data.
not sure why but now the two lines i had commented out because they weren't working now they work..
uncommented the following lines
pBuf += nSent;
nRead -= nSent;
and these on the reciver side
nRecv -= nWritten;
and it worked like a charm.
Thank you for the help.