I'm trying to write a simple server in c that plays a two player game. It checks for incoming connections, and if there is no player1, it saves player1's file descriptor (to be used later for sending and receiving) and if there is no player2, it does the same. I have this loop set up that I modified from Here. My problem is that I want to receive from one, and send to the other, but it seems that my assignments are invalid. When I try to send to player2, it fails or it sends garbage. Sometimes, sending to player1 sends back to the server(?). Am I using select correctly and looping through the file descriptor set correctly? Any feedback would be appreciated.
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
while (1) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
error("select");
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
//This indicates that someone is trying to do something
if (FD_ISSET(i, &read_fds)) {
if (i == listener) {
addrlen = sizeof remoteaddr;
newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen);
if (newfd == -1) {
error("accept");
} else {
FD_SET(newfd, &master);
if (newfd > fdmax) {
fdmax = newfd;
}
/* If we have the maximum number of players, we tell if that it's busy */
if (players >= 2) {
toobusy(fdmax); close(fdmax); FD_CLR(fdmax, &master);
} else {
//Problem here?
if (player1_fd == -1) {
player1_fd = newfd;
}
if ((player1_fd != -1) && (player2_fd == -1)) {
player2_fd = newfd;
}
players++;
if (players == 2) {
sendhandles(); //says two players exist
}
}
}
} else {
//Possible problems here
if (i == player1_fd || i == player2_fd) {
receive(i); //Processes the messages
}
}
}
}
}
The toobusy part should use newfd, not fdmax. Otherwise there's no easy spotted error in this code.
Your comment "Sometimes, sending to player1 sends back to the server(?)" makes me think that player1_fd and player2_fd might be uninitialized or perhaps initialized to 0 instead of -1. You should double check that you set them to -1 before the loop.
A few additionally notes:
Are you sure master is 0 initialized? Have you called FD_ZERO on it?
You should use FD_COPY to copy master to read_fds.
Finally, I'd recommend to use a library for event handling, such as libevent or libev.
Related
I am maintaining/developing an existing code-base. We have a Raspberry Pi controlling a bunch of hardware, some of which is modular. The code, written in C (might be C++), communicates using IPv4 standards over a socket (using socket.h) with a gui on Windows. I'm pretty sure we don't have multithreading implemented in the raspberry code, or this would be much easier.
Some of the modular hardware is not interacting with the code. Without the extra hardware, the raspberry sits there waiting until the GUI sends something. That's how we want it behaving.
Problem is, when we have this extra hardware connected, the raspberry needs to also run some code in response to one of its IO pins tied to a button on the hardware.
I tried adding (various) conditions to a loop in main(), which is where I thought the code was idling. I always got either the hardware OR GUI control working, but not both.
I eventually figured out that read(), which is called near the end of that loop, is blocking.
Now I'm trying to figure out how to execute one chunk of code (for the hardware control) if read() is blocking, and another when it stops.
Something like:
(Pseudocode)
read socket
while(blocking)
{
check for hardware signal, if found, runTest();
}
{use result of read()}
The loop in main:
while(true)
{
initPort();
while(listening)
{
openPort();
if (netOpen)
{
//puts("// Tester is connected /////////////////////////////////");
loadCalFactors();
loadCalResistors();
loadExtConf(true);
inOn(false);
outOn(false);
// Loop until client terminates connection.
initPins();
while(netOpen)
{
/*
* Moving the block to work().
*/
// oldI = i;
// i = digitalRead(iTSW);
//
// if((i ^ oldI) == 0 && oldI == 1) // TSW has been held on
// testing = true;
//
// else if((i ^ oldI) == 1 && oldI == 0)
// testing = false;
//
// if(testLoaded && usingTermCtrl && i && !testing)
// {
// RunTp(false);
// }
// else
work();
}
}
}
}
Here's the code where it's blocking:
void work()
{
char bs[1];
int n;
//socket Sockfd is blocking here, so we only briefly return to the
//loop in main right after an action
n = read(Sockfd, bs, 1);
if(n > 0)
doAct(bs[0]);
else
checkForSOT();
}
I tried putting all my hardware checks in checkForSOT() (which was previously empty) already, but it didn't do any better than the loop in main.
And where the socket is being setup:
void initPort()
{
struct sockaddr_in serv_addr;
printf("Initializing Port %d\n", HOST_PORT);
listening = false;
sockListen = socket(AF_INET, SOCK_STREAM, 0);
printf("sockListen=%d\n",sockListen);
if(sockListen < 0)
{
puts("Error opening socket");
delay(1000);
}
else
{
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(HOST_PORT);
if(bind(sockListen, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
{
puts("Error on binding");
delay(1000);
}
else
{
listen(sockListen, 1);
listening = true;
}
}
}
void openPort()
{
int clilen;
int newsockfd;
printf("\nTester waiting for connection on socket %d\n", sockListen);
clilen = sizeof(cli_addr);
// infinite wait on a connection
if((newsockfd = accept(sockListen,
(struct sockaddr *) &cli_addr, (socklen_t*) &clilen) ) < 0 )
puts("Error on accept");
else
{
if (DEBUG) debugMode = true;
netOpen = true;
Sockfd = newsockfd;
sendVersion();
netWrite("Connected\n");
sendExtConfMessage();
sendExtConf();
}
}
Most of the posts I found on sockets blocking mention poll() and select() (along with their p- and e- upgrades(?)), but don't go into clear-enough detail on how they work for me to figure out if they are what I need.
I'm also not sure what it would take to change the socket to non-blocking while maintaining the same behavior.
Note: I am still trying to read through and wrap my head around Beej's guide to network programming, so if there's a specific section of that that'll help me, please be specific.
Also, if anyone knows of (or could write) a good guide to setting up remote debugging the raspberry through NetBeans 12 (on Windows 10), that would be a HUGE help!
Poll isn't very hard. Make a loop that runs at least every 5 msec.
while (running)
Initialize, then wait for something to happen
struct pollfd fds = {fd, POLLIN, 0};
int rc = poll(&fds, 1, 5); // wait for fds or 5 msec
if (rc < 0) {
perror("poll");
exit(1);
} else if (rc > 0) {
if (fds.revents & POLLIN)
recv(fd); // fds is ready
}
// check button here
}
Obviously there should be more checking and cleanup on error conditions.
I'm initializing a daemon in C in a Debian:
/**
* Initializes the daemon so that mcu.serial would listen in the background
*/
void init_daemon()
{
pid_t process_id = 0;
pid_t sid = 0;
// Create child process
process_id = fork();
// Indication of fork() failure
if (process_id < 0) {
printf("Fork failed!\n");
logger("Fork failed", LOG_LEVEL_ERROR);
exit(1);
}
// PARENT PROCESS. Need to kill it.
if (process_id > 0) {
printf("process_id of child process %i\n", process_id);
exit(0);
}
//unmask the file mode
umask(0);
//set new session
sid = setsid();
if(sid < 0) {
printf("could not set new session");
logger("could not set new session", LOG_LEVEL_ERROR);
exit(1);
}
// Close stdin. stdout and stderr
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
The main daemon runs in the background and monitors a serial port to communicate with a microcontroller - it reads peripherals (such as button presses) and passes information to it. The main functional loop is
int main(int argc, char *argv[])
{
// We need the port to listen to commands writing
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
logger("ERROR, no port provided", LOG_LEVEL_ERROR);
exit(1);
}
int portno = atoi(argv[1]);
// Initialize serial port
init_serial();
// Initialize server for listening to socket
init_server(portno);
// Initialize daemon and run the process in the background
init_daemon();
// Timeout for reading socket
fd_set setSerial, setSocket;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
char bufferWrite[BUFFER_WRITE_SIZE];
char bufferRead[BUFFER_READ_SIZE];
int n;
int sleep;
int newsockfd;
while (1)
{
// Reset parameters
bzero(bufferWrite, BUFFER_WRITE_SIZE);
bzero(bufferRead, BUFFER_WRITE_SIZE);
FD_ZERO(&setSerial);
FD_SET(fserial, &setSerial);
FD_ZERO(&setSocket);
FD_SET(sockfd, &setSocket);
// Start listening to socket for commands
listen(sockfd,5);
clilen = sizeof(cli_addr);
// Wait for command but timeout
n = select(sockfd + 1, &setSocket, NULL, NULL, &timeout);
if (n == -1) {
// Error. Handled below
}
// This is for READING button
else if (n == 0) {
// This timeout is okay
// This allows us to read the button press as well
// Now read the response, but timeout if nothing returned
n = select(fserial + 1, &setSerial, NULL, NULL, &timeout);
if (n == -1) {
// Error. Handled below
} else if (n == 0) {
// timeout
// This is an okay tiemout; i.e. nothing has happened
} else {
n = read(fserial, bufferRead, sizeof bufferRead);
if (n > 0) {
logger(bufferRead, LOG_LEVEL_INFO);
if (strcmp(stripNewLine(bufferRead), "ev b2") == 0) {
//logger("Shutting down now", LOG_LEVEL_INFO);
system("shutdown -h now");
}
} else {
logger("Could not read button press", LOG_LEVEL_WARN);
}
}
}
// This is for WRITING COMMANDS
else {
// Now read the command
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0 || n < 0) logger("Could not accept socket port", LOG_LEVEL_ERROR);
// Now read the command
n = read(newsockfd, bufferWrite, BUFFER_WRITE_SIZE);
if (n < 0) {
logger("Could not read command from socket port", LOG_LEVEL_ERROR);
} else {
//logger(bufferWrite, LOG_LEVEL_INFO);
}
// Write the command to the serial
write(fserial, bufferWrite, strlen(bufferWrite));
sleep = 200 * strlen(bufferWrite) - timeout.tv_usec; // Sleep 200uS/byte
if (sleep > 0) usleep(sleep);
// Now read the response, but timeout if nothing returned
n = select(fserial + 1, &setSerial, NULL, NULL, &timeout);
if (n == -1) {
// Error. Handled below
} else if (n == 0) {
// timeout
sprintf(bufferRead, "err\r\n");
logger("Did not receive response from MCU", LOG_LEVEL_WARN);
} else {
n = read(fserial, bufferRead, sizeof bufferRead);
}
// Error reading from the socket
if (n < 0) {
logger("Could not read response from serial port", LOG_LEVEL_ERROR);
} else {
//logger(bufferRead, LOG_LEVEL_INFO);
}
// Send MCU response to client
n = write(newsockfd, bufferRead, strlen(bufferRead));
if (n < 0) logger("Could not write confirmation to socket port", LOG_LEVEL_ERROR);
}
close(newsockfd);
}
close(sockfd);
return 0;
}
But the CPU usages is always at 100%. Why is that? What can I do?
EDIT
I commented out the entire while loop and made the main function as simple as:
int main(int argc, char *argv[])
{
init_daemon();
while(1) {
// All commented out
}
return 0;
}
And I'm still getting 100% cpu usage
You need to set timeout to the wanted value on every iteration, the struct gets modified on Linux so I think your loop is not pausing except for the first time, i.e. select() is only blocking the very first time.
Try to print tv_sec and tv_usec after select() and see, it's modified to reflect how much time was left before select() returned.
Move this part
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
inside the loop before the select() call and it should work as you expect it to, you can move many delcarations inside the loop too, that would make your code easier to maintan, you could for example move the loop content to a function in the future and that might help.
This is from the linux manual page select(2)
On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1-2001 permits either behavior.) This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple select()s in a loop without reinitializing it. Consider timeout to be undefined after select() returns.
I think the bold part in the qoute is the important one.
I am having a printing issue with my server. I want there to be simultaneous printing when I have 2 or more clients active on terminals. However, I am only printing from one client at a time. Once I close a client, the other clients are free to write to the server. What can I do to fix my problem?
I have tried to fork the printing section, which I think didn't really do anything. (Just realized if I do this, then the select system call is a waste, i'd rather use the select system call) *edit
while(TRUE) {
FD_ZERO(&readfds);
FD_SET(socket1, &readfds);
FD_SET(socket2, &readfds);
FD_SET(socket3, &readfds);
select(socket3+1, &readfds, NULL, NULL, NULL);
//add socket1
if(FD_ISSET(socket1, &readfds)) {
if((client_socket1 = accept(socket1, NULL, NULL)) < 0) {
perror("accept1");
exit(EXIT_FAILURE);
}
printf("New Connection\n");
puts("Welcome message1 sent successfully\n");
}
//add socket2
if(FD_ISSET(socket2, &readfds)) {
if((client_socket2 = accept(socket2, (struct sockaddr *)&addr2, (socklen_t*)&addr2)) < 0) {
perror("accept2");
exit(EXIT_FAILURE);
}
printf("New Connection\n");
puts("Welcome message2 sent successfully\n");
}
//add socket 3
if(FD_ISSET(socket3, &readfds)) {
if((client_socket3 = accept(socket3, (struct sockaddr *)&addr3, (socklen_t*)&addr3)) < 0) {
perror("accept3");
exit(EXIT_FAILURE);
}
printf("New Connection\n");
puts("Welcome message3 sent successfully\n");
}
//print from socket 3
while( (ready = read(client_socket3, buffer, sizeof(buffer))) > 0) {
printf("%s\n", buffer);
}
//print from socket 2
while( (ready = read(client_socket2, buffer, sizeof(buffer))) > 0) {
printf("%s\n", buffer);
}
//print from socket 1
while( (ready = read(client_socket1, buffer, sizeof(buffer))) > 0) {
printf("%s\n", buffer);
}
}
You need to add your client sockets to the fd_set and select statement before attempting to read from them. Also, you should make all your sockets non-blocking. Otherwise, the read call will block until you get data.
Here's a quick fix that uses recv instead of read to read the sockets, but with the async flag of MSG_DONTWAIT.
I didn't see anywhere where you were closing your client sockets or handling errors properly. So I inserted some code as a hint. Also, it's never a good idea to "printf" a buffer of data from a socket directly. Because you never know if the data you received is null terminated. Always null terminate your buffer after you read the data off the socket.
Change this block of code:
//print from socket 3
while( (ready = read(client_socket3, buffer, sizeof(buffer))) > 0) {
printf("%s\n", buffer);
}
To this:
while (1)
{
int result;
result = recv(client_socket3, buffer, sizeof(buffer)-1, MSG_DONTWAIT);
if ((result == -1) &&
((errno == EAGAIN) || (errno==EWOULDBLOCK)) )
{
// no more data available, but could be available later
// use the socket with "select" above to wait for more data
}
else if ((result == -1) || (result == 0))
{
// remote close or unrecoverable error
close(client_socket3);
client_socket3=-1;
}
else
{
// null terminate the buffer before printing
buffer[result] = '\0';
printf("%s\n", buffer);
}
}
The objective of my program is to use select to manage multiple sockets. However, I thought of trying it with one socket first. Now, the problem that I am facing is that initially client sends data to server, and server receives it and displays it, but then when client again sends some data, the server code remains still at select command.
here are some snippets that will give you an idea of how I am initializing the socket.
if((master_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
exit(1);
}
if((bind(master_socket, (struct sockaddr *)&req, sizeof(req))) < 0)
{
exit(1);
}
listen(master_socket, 5);
FD_SET(master_socket,&listening);
/* wait for connection, then receive and print text */
len = sizeof(struct sockaddr);
while(1)
{
FD_ZERO(&listening); //Flush out everything in socket
FD_SET(master_socket,&listening); // Add master
if(f_client>0) // Add client if any
{
FD_SET(f_client,&listening);
}
printf("Checking for new connection \n");
//Timeout is null, so waiting indefinitely
rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
if (FD_ISSET(master_socket, &listening))
{
printf("Master side invoked\n");
if((f_client = accept(master_socket, (struct sockaddr *)&req, &len)) < 0)
{
exit(1);
}
}
else if (FD_ISSET(f_client,&listening))
{
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
}
}
Basically in above program, it connects to the server, maintains a file descriptor for client f_client and add it. And in every round, it clears the socket, add master socket, and client socket if any, and then checks. Problem here is, first time it works, but second time when client sends some data. it gets hang to rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
I am not to understand what is wrong here. Can anyone help?
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
This code is broken. The fputs function can only be used with a C-style string. You just have arbitrary data with no particular structure. Since you ignore valread, you also have no idea how many bytes you read. (Think about it, how could fputs possibly know how many bytes to output? That information is only in valread, and you don't pass it that information.)
You've already received the data, this broken code just threw it away. If you log valread, you'll see that you actually already read it in your last call to read before the call to select that hung.
instead of fputs, you could use something like this:
for (int i = 0; i < valread; ++i)
putchar(buf[i]);
I was just going through the Networking Guide by Beej and am curious about this part of the code (specifically marked with "From here" and "To here"):
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1) {
perror("accept");
} else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
printf("selectserver: new connection from %s on "
"socket %d\n",
inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),
newfd);
}
} else {
// handle data from a client
//----------------- FROM HERE --------------------------
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
} else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
//----------------- TO HERE ----------------------------
} else {
// we got some data from a client
for(j = 0; j <= fdmax; j++) {
// send to everyone!
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)--and you thought it would never end!
return 0;
Now I know that read doesn't always read "everything" that is to be read on a socket and that it sometimes can return only part of it. In that case, wouldn't this code be incorrect? I mean, after one read, the connection is being closed... Instead, aren't we supposed to have some other mechanism in place? If so, what is the right approach here?
The socket is only going to get closed there if there was an error from recv(), otherwise it'll deal with the data that was read even if it isnt all read. It will then read more out when it loops through again. Pretty sure this is what you're asking?
Yes you would keep reading until you got all the data you expected, obviosuly you need someway of knowing how much to expect - which is why http puts the document size first
Your only calling close when recv() has returned a negative value which means that recv had some sort of error. Notice that the block where you do the close has a comment stating // got error or connection closed by client).
When you actually get some data (the else branch starting with // we got some data from a client), the connection is not being closed.
You are right that you can't assume the data arrives all at one time. Your mistake is in following how the code is working.