I'm coding an IRC client and I would like implement a "/server" command to switch the connection of my client to an other server.
Before initialize the new connection I want to close the sockect's fd but the close() call fail. Anybody could say me why ?
Here is my code :
/* Main execution loop */
FD_ZERO(&irc->rdfs);
FD_SET(STDIN_FILENO, &irc->rdfs);
FD_SET(irc->socket_fd, &irc->rdfs);
if ((select(irc->socket_fd + 1, &irc->rdfs, NULL, NULL, NULL)) == -1)
{
if ((close(irc->socket_fd)) == -1)
exit(usage(CLOSE_ERROR));
exit(usage(SELECT_ERROR));
}
if (FD_ISSET(STDIN_FILENO, &irc->rdfs))
{
fgets(irc->buffer, SIZE - 1, stdin);
{
p = strstr(irc->buffer, RET);
if (p != NULL)
*p = 0;
else
irc->buffer[SIZE - 1] = 0;
}
write_on_server(irc, irc->buffer); /* The function where I call switch_server() in */
}
else if (FD_ISSET(irc->socket_fd, &irc->rdfs))
{
if ((read_on_server(irc)) == 0)
exit(usage(SERVER_DISCONNECT));
puts(irc->buffer);
}
And here is where I'm trying to close my socket's fd :
void switch_server(t_irc *irc)
{
if ((close(irc->socket_fd)) == -1) /* This is the close which fail */
exit(EXIT_FAILURE);
}
void write_on_server(t_irc *irc, const char * buffer)
{
if (!(strncmp("/server", buffer, strlen("/server"))))
switch_server(irc);
else
if ((send(irc->socket_fd, buffer, strlen(buffer), 0)) < 0)
{
if ((close(irc->socket_fd)) == -1)
exit(usage(CLOSE_ERROR));
exit(usage(CLIENT_SEND_ERROR));
}
}
Thanks a lot.
If you want to know why a syscall like close() failed, use perror() to print an error message to stderr, or strerror(errno) to convert the error code to a string and output it some other way.
Almost certainly the socket FD is invalid. You need to call perror() on that, and on the select() failure.
Related
As a school project, I'm implementing a IRC server and I've been stuck on this one problem for the day.
My server use select to choose which client is sending data, it then read one command from this user (commands are \r\n separated), parse it and execute it before passing to the next user.
A user can send multiple command at once like so :
"command1\r\ncommand2\r\n"
If this happen, i want the first command to be executed and the second to stay in the stream so that it can be read at the next select() call. (this way, each user only execute one comamnd per "turn").
To do this, I have a FILE *stream per client that stay open as long as it is connected.
This work perfectly (if I send the double comamnd specified above, the two commands are executed one after the other).
The problem is after that after that, select() continue to tell me that there is something to read in the socket (FD_ISSET of the fd return 1), so my receive function is called for that fd and the getline() in it fail (without setting errno) and it loop like this forever.
I don't understand why the select still consider that there is something to read in the socket and why the getline is failing instead of blocking.
Any idea ?
edit: I'm not allowed to use non blocking socket or fork() for this project
The "main" loop:
while (g_run_server)
{
fd_max = g_socket_fd;
FD_ZERO(fds);
FD_SET(g_socket_fd, fds);
tmp = users;
while (tmp)
{
FD_SET(tmp->fd, fds);
fd_max = (tmp->fd > fd_max ? tmp->fd : fd_max);
tmp = tmp->next;
}
if (select(fd_max + 1, &fds, NULL, NULL, NULL) < 0)
break;
if (FD_ISSET(g_socket_fd, &fds))
accept_new_user(&hdl, &users);
handle_clients(&hdl, &fds);
}
The functions to handle and read client input :
static bool recv_and_execute(t_handle *hdl)
{
char *raw;
size_t len;
len = 0;
raw = NULL;
if (!hdl->sender->stream &&
(hdl->sender->stream = fdopen(dup(hdl->sender->fd), "r")) == NULL)
return (false);
if (getline(&raw, &len, hdl->sender->stream) != -1)
{
printf("Received \"%s\"\n", raw);
parse_cmd(hdl, raw);
exec_cmd(hdl);
}
else
printf("Getline failed %s\n", strerror(errno));
free(raw);
return (true);
}
int handle_clients(t_handle *hdl, fd_set *fds)
{
t_user *tmp;
tmp = *hdl->users;
while (tmp)
{
if (FD_ISSET(tmp->fd, fds))
{
printf("fd %d is ready to be read\n", tmp->fd);
hdl->sender = tmp;
recv_and_execute(hdl);
FD_CLR(tmp->fd, fds);
tmp = tmp->next;
if (hdl->sender->status == DEAD)
del_user(hdl->users, hdl->sender);
}
else
tmp = tmp->next;
}
return (0);
}
And this is the output when I connect one client and send "USER foo\r\nUSER no bo dy :wa\r\n" :
fd 4 is ready to be read
Received "NICK foo
"
[DEBUG] Executing "NICK" with params "foo" "(null)" "(null)" "(null)"
[INFO] Nickname successfully changed.
fd 4 is ready to be read
Received "USER no bo dy :wa
"
[DEBUG] Executing "USER" with params "no" "bo" "dy" ":wa"
[INFO] User registered.
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
continue like this....
Edit : I edited my receive function based on the comment of alk:
static bool recv_and_execute(t_handle *hdl)
{
char *raw;
size_t len;
ssize_t nread;
len = 0;
raw = NULL;
if (!hdl->sender->stream &&
(hdl->sender->stream = fdopen(dup(hdl->sender->fd), "r")) == NULL)
return (false);
errno = 0;
if ((nread = getline(&raw, &len, hdl->sender->stream)) > 0)
{
printf("Received \"%s\"\n", raw);
parse_cmd(hdl, raw);
exec_cmd(hdl);
}
else if (errno != 0)
printf("getline failed %s\n", strerror(errno));
else {
printf("EOF reached\n");
fclose(hdl->sender->stream);
hdl->sender->stream = NULL;
}
printf("nread = %zd\n", nread);
free(raw);
return (true);
}
So this time, when EOF is reach (getline return -1), I close the stream and set it to NULL to be reopened the next time select find data on the socket fd. But even when I close the stream, select still detect that there is data available and the inifinite loop continue :/
fd 4 is ready to be read
Received "NICK foo^M
"
nread = 10
fd 4 is ready to be read
Received "USER no bo dy :wa^M
"
nread = 19
fd 4 is ready to be read
EOF reached
nread = -1
fd 4 is ready to be read
EOF reached
nread = -1
fd 4 is ready to be read
EOF reached
nread = -1
and so on...
I'm pretty sure you're using the select wrong. I show you a simple code example on how to use it (I don't handle many errors) and you can edit it as you need.
/*
* If you read at man select bugs you can see
* that select could return that someone is
* ready but it isn't true
*/
int fd_c;
fd_set rdset;
fd_set set; /*That's the mask you'll use when new requests arrive*/
FD_ZERO(&set); /*Clears the mask*/
FD_SET(g_socket_fd,&set); /*Set the listening socket as ready*/
while(g_run_server){
/*
* YOU MUST INITIALIZATE IT EVERY LOOP
* read # man select
*/
rdset = set;
if(select((fd_num_max+1),(&rdset),NULL,NULL,NULL) < 0){
perror("Failed on select\n");
exit(EXIT_FAILURE);
}
/*You go through the ready clients in the rdset*/
for(fd=0;fd<=fd_num_max;fd++) {
if(FD_ISSET(fd,&rdset)) { /*If the bit is set*/
if(fd == fd_skt) { /*If it's a new client*/
/*You can handle the new client here or delegete it to someone*/
fd_c=accept(fd_skt,NULL,0); /*File descriptor of new client*/
FD_SET(fd_c,&set);
if(fd_c > fd_num_max) fd_num_max = fd_c;
}else { /*If it's a request from an existing client*/
/*You can handle the new request here or delegete it to someone*/
FD_SET(fd,&set);
}
}
}
}
You should also modify static bool recv_and_execute(t_handle *hdl) that way:
errno = 0;
if ((nread = getline(&raw, &len, hdl->sender->stream)) != -1){
printf("Received \"%s\"\n", raw);
parse_cmd(hdl, raw);
exec_cmd(hdl);
}else{
if( errno == 0){
printf("EOF reached\n");
fclose(hdl->sender->stream);
hdl->sender->stream = NULL;
}else{
printf("getline failed %s\n", strerror(errno));
exit(EXIT_FAILURE); /*You must handle it in some way, exiting or doing something*/
}
}
I wrote a C application for a socialization network and also a simple room-based chat. I used ncurses, sockets and basic networking stuff.
The problem is that my function uses select() to read from server socket AND stdin so when I start to write a message, the output window freezes and only shows messages from other clients after I hit enter.
I tried everything possible .. Is there a way to fix this ?
I also tried to force nocbreak().It works okay but if I do that, when I write the message, the echoing is disabled and nothing shows up in the input window as I type, even though the message is there but like "invisible".
Here is the code :
ssize_t safePrefRead(int sock, void *buffer)
{
size_t length = strlen(buffer);
ssize_t nbytesR = read(sock, &length, sizeof(size_t));
if (nbytesR == -1)
{
perror("read() error for length ! Exiting !\n");
exit(EXIT_FAILURE);
}
nbytesR = read(sock, buffer, length);
if (nbytesR == -1)
{
perror("read() error for data ! Exiting !\n");
exit(EXIT_FAILURE);
}
return nbytesR;
}
ssize_t safePrefWrite(int sock, const void *buffer)
{
size_t length = strlen(buffer);
ssize_t nbytesW = write(sock, &length, sizeof(size_t));
if (nbytesW == -1)
{
perror("write() error for length ! Exiting !\n");
exit(EXIT_FAILURE);
}
nbytesW = write(sock, buffer, length);
if (nbytesW == -1)
{
perror("write() error for data ! Exiting !\n");
exit(EXIT_FAILURE);
}
return nbytesW;
}
void activeChat(int sC, const char *currentUser, const char *room)
{
char inMesg[513], outMesg[513];
char user[33];
int winrows, wincols;
WINDOW *winput, *woutput;
initscr();
nocbreak();
getmaxyx(stdscr, winrows, wincols);
winput = newwin(1, wincols, winrows - 1, 0);
woutput = newwin(winrows - 1, wincols, 0, 0);
keypad(winput, true);
scrollok(woutput, true);
wrefresh(woutput);
wrefresh(winput);
fd_set all;
fd_set read_fds;
FD_ZERO(&all);
FD_ZERO(&read_fds);
FD_SET(0, &all);
FD_SET(sC, &all);
wprintw(woutput, "Welcome to room '%s' \n Use /quitChat to exit !\n!", room);
wrefresh(woutput);
while (true)
{
memcpy( &read_fds, &all, sizeof read_fds );
if (select(sC + 1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select() error or forced exit !\n");
break;
}
if (FD_ISSET(sC, &read_fds))
{
memset(inMesg, 0, 513);
safePrefRead(sC, user);
safePrefRead(sC, inMesg);
wprintw(woutput, "%s : %s\n", user, inMesg);
wrefresh(woutput);
wrefresh(winput);
}
if (FD_ISSET(0, &read_fds))
{
//wgetnstr(winput, "%s", outMesg);
int a, i = 0;
while ( i < MAX_BUF_LEN && (a = wgetch(winput)) != '\n')
{
outMesg[i] = (char)a;
i++;
}
outMesg[i] = 0;
if (outMesg[0] == 0)
continue;
if (strcmp(outMesg, "/quitChat") == 0)
{
safePrefWrite(sC, outMesg);
break;
}
safePrefWrite(sC, outMesg);
delwin(winput);
winput = newwin(1, wincols, winrows - 1, 0);
keypad(winput, true);
wrefresh(winput);
}
}
delwin(winput);
delwin(woutput);
endwin();
}
-safePrefWrite and safePrefRead are wrappers for prexied read / write and error treating
-sC is the server socket.
LE: I tried using fork and threads. Using fork was behaving the same and threads were a disaster, the terminal was messed up.
Thank you.
modify the while(true) loop to only handle one char at a time for the stdin.
Which mostly means for stdin, read a single char:
if char is '\n' then handle as currently,
otherwise, just append char to the buffer to write.
Always, before appending a char to buffer to write, check that buffer is not full.
add code to handle the case where the buffer to write is full
end the function with this sequence:
delwin(winput);
delwin(woutput);
endwin();
endwin();
to end both windows.
Do not call endwin() during processing of the socket input.
Do not call endwin() when select() returns an error condition
the fd_set is not an intrinsic size in C, so use memcpy() to set
read_fds from all. suggest:
memcpy( &read_fds, &all, sizeof read_fds );
the parameter: currentUser is not used, suggest inserting the line:
(void)currentUser;
to eliminate a compiler warning message.
for readability, and ease of understandability, suggest #define the magic numbers 513 and 33 with meaningful names, then use those meaningful names throughout the code.
#define MAX_BUF_LEN (513)
#define MAX_USER_LEN (33)
this line: outMesg[i] = a; raises a compiler warning, suggest:
outMesg[i] = (char)a;
This line: while ( (a = wgetch(winput)) != '\n') can allow the buffer outMesg[] to be overrun, resulting in undefined behaviour and can lead to a seg fault event. suggest:
while ( i < MAX_BUF_LEN && (a = wgetch(winput)) != '\n')
Suggest posting the prototypes for the safePrefWrite() and safePrefRead() functions, similar to:
void safePrefRead( int, char * );
void safePrefWrite( int, char * );
As noted by #user3629249, there are several criticisms which can be applied to the sample code. However, OP's question is not addressed by those improvements.
OP seems to have overlooked these functions:
cbreak or raw, to make wgetch read unbuffered data, i.e., not waiting for '\n'.
nodelay or timeout, to control the amount of time wgetch spends waiting for input.
By the way, making select work with a curses program will make assumptions about the curses library internal behavior: getting that to work reliably can be troublesome.
Fixed it finally by using only the big loop.
Here is the code if anyone has the same problem in the future :
if (FD_ISSET(0, &read_fds))
{
inChar = wgetch(winput);
if (inChar == 27)
{
safePrefWrite(sC, "/quit");
break;
}
if (inChar == KEY_UP || inChar == KEY_DOWN || inChar == KEY_LEFT || inChar == KEY_RIGHT)
continue;
if (inChar == KEY_BACKSPACE || inChar == KEY_DC || inChar == 127)
{
wdelch(winput);
wrefresh(winput);
if (i != 0)
{
outMesg[i - 1] = 0;
i--;
}
}
else
{
outMesg[i] = (char)inChar;
i++;
}
if (outMesg[i - 1] == '\n')
{
outMesg[i - 1] = 0;
i = 0;
if (outMesg[0] == 0)
continue;
if (strcmp(outMesg, "/quit") == 0)
{
safePrefWrite(sC, outMesg);
break;
}
safePrefWrite(sC, outMesg);
delwin(winput);
winput = newwin(1, wincols, winrows - 1, 0);
keypad(winput, true);
wrefresh(winput);
memset(outMesg, 0, 513);
}
}
I also use raw() to disable signals and to treat the codes how I want.
Anything else above and below this "if" is just like in the 1st post.
I'm working on a Server in C using a Multi-Process / Multi-Threaded architecture. At the start I've a main process that forks 10 different processes. Each process creates a pool of threads. Each process is, also, the controller of its pool (creates new thread / extends the pool when needed).
The processes also listen on the socket (whose access is controlled by the main process with an unnamed semaphore in a shared memory area) and pass the socket file descriptor to one of the thread in their pool when a connection is acquired. The chosen thread (which is awakened by a pthread_cond_signal) reads from the socket with a recv using the option MSG_DONTWAIT.
ssize_t readn, writen;
size_t nleft;
char *buff;
buff = malloc(sizeof(char) * BUFF_SIZE);
if (buff == NULL) {
perror("malloc");
return EXIT_FAILURE;
}
char *ptr = buff;
/*if (fcntl(connsd, F_SETFL, O_NONBLOCK) == -1) { // set to non-blocking
perror("fcntl");
exit(EXIT_FAILURE);
}*/
errno = 0;
nleft = BUFF_SIZE;
while(nleft > 0) {
if ((readn = recv(connsd, ptr, nleft, MSG_DONTWAIT)) < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
*ptr = '\0';
break;
}
else if (errno == EINTR)
readn = 0;
else {
perror("read");
return EXIT_FAILURE;
}
}
else if (readn == 0)
break;
if (buff[strlen(buff)-1] == '\0') {
break;
}
nleft -= readn;
ptr += readn;
}
The problem is that sometimes, when I try to connect to the server using Chrome (or Firefox) 3 threads seems to receive the HTTP Request but each one of them simply closes the connection because of this portion of code.
if (buff[strlen(buff)-1] != '\0') {
printf("%s\n",buff);
fflush(stdout);
buff[strlen(buff)-1] = '\0';
}
errno = 0;
if (strlen(buff) < 1) {
perror("No string");
if (shutdown(connsd,SHUT_RDWR) < 0) {
perror("shutdown");
return EXIT_FAILURE;
}
if (close(connsd) < 0) {
perror("close");
return EXIT_FAILURE;
}
return EXIT_FAILURE;
}
Other times we have 3 threads with different behaviours: one of them receives and reads from the socket the first HTTP Request (GET / HTTP/1.1). The second one is empty (request received (because it is awakened (?)) no string read). The third one receives and reads another HTTP Request (GET /favicon.ico HTTP/1.1).
Where is the problem that cause these behaviours? I can add other portions of code if needed.
Thank you very much for your time.
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);
}
}
I have a simple server program written in C, and the program is running on an Ubuntu Linux distribution. The program is intended to listen for messages sent from the client, write those messages to a file (each message goes into a separate file), and send an acknowledgement back to the client once the message has been received and stored.
I have noticed that, as the server continues to receive and store messages, the amount of available system memory quickly decreases and continues to decrease until messages have stopped. Memory remains constant when no messages are being sent. However, I have also noticed that I can free up the memory again by deleting the written files from disk (I can do this even while the server is still running). I am therefore led to believe that the memory issue has something to do with my file operations, though I can't see any issue with the code that writes the files.
Can someone help?
NOTE: I am observing the memory usage with "top".
I have included an excerpt from the program. The below function handles input from the client and writes that information to file. This is where I currently believe the problem to be:
void handleinput (int sock)
{
char filename[strlen(tempfolder) + 27];
generatefilename(filename);
int rv;
int n = 1;
int received = 0;
char buffer[BUFFER_SIZE];
FILE *p = NULL;
fd_set set;
char response[768];
struct timeval timeout;
timeout.tv_sec = 360;
timeout.tv_usec = 0;
FD_ZERO(&set);
FD_SET(sock, &set);
bzero(buffer, BUFFER_SIZE);
bzero(response, sizeof response);
rv = select(sock + 1, &set, NULL, NULL, &timeout);
if (rv == -1)
{
error("error on select in handleinput");
close(sock);
exit(1);
}
else if (rv == 0)
{
close(sock);
exit(0);
}
else
{
n = read(sock, buffer, BUFFER_SIZE-1);
if (n <= 0)
{
close(sock);
exit(0);
}
}
// open file
if (n != 0)
{
p = fopen(filename, "a");
if (p == NULL)
{
error("ERROR writing message to file");
close(sock);
exit(1);
}
}
// loop until full message is received
while (n != 0)
{
if (n < 0)
{
error("ERROR reading from socket");
close(sock);
exit(1);
}
received = 1;
// write content to file
fwrite(buffer, strlen(buffer), 1, p);
if (buffer[strlen(buffer)-1] == 0x1c)
{
break;
}
bzero(buffer, BUFFER_SIZE);
rv = select(sock + 1, &set, NULL, NULL, &timeout);
if (rv == -1)
{
error("ERROR select in loop in handleinput");
close(sock);
exit(1);
}
else if (rv == 0)
{
close(sock);
exit(0);
}
else
{
n = read(sock, buffer, BUFFER_SIZE-1);
}
}
// close file if we opened it earlier
if (p != NULL)
{
fclose(p);
}
// send acknowledgement back to client
if (received == 1)
{
generateResponse(response, filename);
n = write(sock, response, strlen(response));
if (n < 0)
{
error("ERROR writing to socket");
close(sock);
exit(1);
}
}
}
Its because of the caching mechanism of writing. IF you have a lot of clients trying to write files, the IO buffer fills in kernel memory, but doesn't actually write to that file until you close the socket or the buffer fills. You can fix this just by flushing the buffer. What some other people have suggested is to get rid of the write and read wrappers in stdio, and just use the kernel call write, since this will help performance and will probably flush the buffer automatically.
You can flush with fsync() btw.