How do you keep a socket connection open indefinitely in C? - c

I'm trying to implement a C socket server in Linux using the code from Beej's sockets guide, which is here:
http://beej.us/guide/bgnet/examples/server.c
This works, and I've written a Windows client in C# to communicate with it. Once the client connects, I have it send a byte array to the server, the server reads it, then sends back a byte array. This works.
However, after this, if I have the client try to send another byte array, I get a Windows popup saying "An established connection was aborted by the software in your host machine." Then I have to re-connect with the client again. I want to keep the connection open indefinitely, until the client sends a disconnect command, but despite reading through Beej's guide, I just don't seem to get it. I'm not even trying to implement the disconnect command at present, I'm just trying to keep the connection open until I close the server.
I've tried removing the close() calls in Beej's code:
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
ProcessRequest(new_fd); // this is not Beej's code, I've replaced his code here (which was a simple string send()) with a function call that does a read() call, processes some data, then sends back a byte array to the client using send().
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
But that just gets me an infinite loop of "socket accept: bad file descriptor" (I tried removing both the close(new_fd) lines, together and apart, and the close(sockfd) as well.
Can anyone more versed with C socket programming give me a hint where I should be looking? Thank you.

The reason for the accept() problem is that sockfd isn't valid. You must have closed it somewhere. NB if you get such an error you shouldn't just keep retrying as though it hadn't happened.
The reason for the client problem is that you're only processing one request in ProcessRequest(), as its name suggests, and as you describe in your comment. Use a loop, reading requests until recv() returns zero or an error occurs.

Cause
The reason client faces error is because of close(new_fd) either by the server-parent or server-child.
Solution
At any point of time, a server may get two kind of events:
Connection request from a new client
Data from an existing client
The server have to honor both of them. There are two (major) ways to handle this.
Solution Approach 1
Design the server as a concurrent server. In Beej's guide it is
7.2. select()—Synchronous I/O Multiplexing
http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#select
Since OP's approach is not this one, we do not explore it further.
Solution Approach 2
At server, fork() a process per client. This is the approach OP has taken and we explore here. Essentially, it is fine tuning the ProcessRequest() function in OP's code. Here is a sketch.
void ProcessRequest( int new_fd ) {
char buffer[ N ];
for( ; ; ) { // infinite loop until client disconnects or some error
int const recvLen = recv( new_fd, buffer, sizeof buffer, 0 );
if( recvLen == 0 ) { break; } // client disconnected
else if( recvLen == -1 ) { perror( "recv" ); break; }
int const sendLen = send( new_fd, buffer, recvLen, 0 );
if( sendLen == -1 ) { perror( "send" ); break; }
// TODO if( sendLen < recvLen ) then send() in loop
}
}
Note
I am sorry for having the half-baked solution four few hours. While I was editing the answer, I lost connectivity to stackoverflow.com which lasted for couple of hours.

Related

Sockets "hanging up" in C/buffer smaller than usual

When I send() and recv() data from my program locally it works fine.
However, on my remote server, the same program, which usually receives data in chunks of 4096, will receive in buffers capped at 1428, which rarely jump above this number.
Worse of all, after a minute or so of transferring data the socket just freezes and stops execution, and the program perpetually stays in this frozen state, like so:
Received: 4096
Received: 4096
Received: 3416
The server is simple, it accepts a connection from a client and receives data in chunks of 4096, which works absolutely fine locally, but on my remote server it is failing consistently, unless I only send a small chunk of data (sending 1000 byte files worked fine).
int main()
{
while(1){
int servSock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
if(servSock < 0){
fprintf(stderr, "Socket error.\n");
continue;
}
struct sockaddr_in servAddr;
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(atoi(STANDARD_PORT));
if(bind(servSock, (struct sockaddr*) &servAddr, sizeof(servAddr)) < 0){
fprintf(stderr, "Bind error.\n");
close(servSock);
continue;
}
if(listen(servSock, BACKLOG) < 0){
fprintf(stderr, "Listen error.\n");
close(servSock);
continue;
}
printf("%s", "Listening on socket for incoming connections.\n");
struct sockaddr_in clntAddr;
socklen_t clntAddrLen = sizeof(clntAddr);
while(1) {
int newsock = accept(servSock, (struct sockaddr*) &clntAddr, &clntAddrLen);
if(newsock < 0){
fprintf(stderr, "Accept connection error");
return 1;
continue;
}
char clntName[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName, sizeof(clntName)) != NULL)
printf("Handling client %s:%d\n", clntName, ntohs(clntAddr.sin_port));
char file[17];
memset(file, 0, 17);
int recvd = recv(newsock, file, 16, 0);
file[17] = '\0';
char local_file_path[200];
memset(local_file_path, 0, 200);
strcat(local_file_path, "/home/");
strcat(local_file_path, file);
printf(local_file_path);
FILE* fp = fopen(local_file_path, "wb");
char buffer[4096];
while(1)
{
memset(buffer, 0, 4096);
recvd = recv(newsock, buffer, 4096, 0);
printf("Received: %d\n", recvd);
fwrite(buffer, sizeof(char), recvd, fp);
if(recvd == -1 || recvd == 0) {
fclose(fp);
break;
} else
}
close(newsock);
}
close(servSock);
}
return 1;
}
EDIT: For more context, this is a Windows server I am adapting to linux. Perhaps the recv() call is blocking when it shouldn't be, I'm going to test with flags.
However, on my remote server, the same program, which usually receives data in chunks of 4096, will receive in buffers capped at 1428, which rarely jump above this number.
Insufficient context has been presented for confidence, but that looks like a plausible difference between a socket whose peer is on the same machine (one connected to localhost, for example) and one whose peer is physically separated from it by an ethernet network. The 1428 is pretty close to the typical MTU for such a network, and you have to allow space for protocol headers.
Additionally, you might be seeing that one system coallesces the payloads from multiple transport-layer packets more or differently than the other does, for any of a variety of reasons.
In any case, at the userspace level, the difference in transfer sizes for a stream socket is not semantically meaningful. In particular, you cannot rely upon one end of the connection to read data in the same size chunks that the other sends it. Nor can you necessarily rely on receiving data in full-buffer units, regardless of the total amount being transferred or the progress of the transfer.
Worse of all, after a minute or so of transferring data the socket just freezes and stops execution, and the program perpetually stays in this frozen state, like so:
"Worst" suggests other "bad", which you have not described. But yes, your code is susceptible to freezing. You will not see EOF on the socket until the remote peer closes their side, cleanly. The closure part is what EOF means for a network socket. The cleanness part is required, at the protocol level, for the local side to recognize the closure. If the other end holds the connection open but doesn't write anything else to it then just such a freeze will occur. If the other side is abruptly terminated, or physically or logically cut off from the network without a chance to close their socket, then just such a freeze will occur.
And indeed, you remarked in comments that ...
Both the client and the server are hanging. The client program just stops sending data, and the server freezes as well.
If the client hangs mid-transfer, then, following from the above, there is every reason to expect that the server will freeze, too. Thus, it sounds like you may be troubleshooting the wrong component.
Perhaps the recv() call is blocking when it shouldn't be, I'm going to test with flags.
There is every reason to think the recv() call is indeed blocking when you don't expect it to do. It's highly unlikely that it is blocking when it shouldn't.
It is possible to set timeouts for socket operations, so that they eventually will fail instead of hanging indefinitely when the remote side fails. Doing so would allow your server to recover, but it would not resolve the client-side issue. You'll need to look into that more deeply.*
*You might see the client unfreeze after the server times out and closes the connection on its end. Don't take that as a resolution.

C UDP Client-Server - recvfrom blocking server in case of client crash

I have this UDP Client-Server chat, a small program that I made for a university course, everything runs just fine. But the professor asked me what would happen if a user crashed while sending a message, the server will keep waiting to receive the message from the client that does not exist anymore, so will wait forever. I tried to think of a solution but couldn't find one and really hope someone can help me.
I will leave just the snippet of code that causes the issue. If needed I can provide more or even the full repo.
void Forward_message(int sockfd, ListHead* head, struct sockaddr_in cliaddr){
Message msg = {0};
int len = sizeof(cliaddr);
if(head->size >1){
int read_bytes;
do{
read_bytes = 0;
read_bytes = recvfrom(sockfd,(Message *)&msg, sizeof(Message),MSG_WAITALL,(struct sockaddr* )&cliaddr, &len );
if(read_bytes == -1)perror("An error occurred while receiving Message from the Client"),exit(1);
}while( (read_bytes != sizeof(Message)) );
Send_msg(&msg,sockfd,head);
printf("\nmessage forwarded\n");
}
}
The issue is caused by the recvfrom inside the do while.

Socket programming for multi-clients with 'select()' in C

This is a question about socket programming for multi-client.
While I was thinking how to make my single client and server program
to multi clients,I encountered how to implement this.
But even if I was searching for everything, kind of confusion exists.
I was thinking to implement with select(), because it is less heavy than fork.
but I have much global variables not to be shared, so I hadn`t considered thread to use.
and so to use select(), I could have the general knowledge about FD_functions to utilize, but here I have my question, because generally in the examples on websites, it only shows multi-client server program...
Since I use sequential recv() and send() in client and also in server program
that work really well when it`s single client and server, but
I have no idea about how it must be changed for multi cilent.
Does the client also must be unblocking?
What are all requirements for select()?
The things I did on my server program to be multi-client
1) I set my socket option for reuse address, with SO_REUSEADDR
2) and set my server as non-blocking mode with O_NONBLOCK using fctl().
3) and put the timeout argument as zero.
and proper use of FD_functions after above.
But when I run my client program one and many more, from the second client,
client program blocks, not getting accepted by server.
I guess the reason is because I put my server program`s main function part
into the 'recv was >0 ' case.
for example with my server code,
(I`m using temp and read as fd_set, and read as master in this case)
int main(void)
{
int conn_sock, listen_sock;
struct sockaddr_in s_addr, c_addr;
int rq, ack;
char path[100];
int pre, change, c;
int conn, page_num, x;
int c_len = sizeof(c_addr);
int fd;
int flags;
int opt = 1;
int nbytes;
fd_set read, temp;
if ((listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
perror("socket error!");
return 1;
}
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
s_addr.sin_port = htons(3500);
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1)
{
perror("Server-setsockopt() error ");
exit(1);
}
flags = fcntl(listen_sock, F_GETFL, 0);
fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK);
//fcntl(listen_sock, F_SETOWN, getpid());
bind(listen_sock, (struct sockaddr*) &s_addr, sizeof(s_addr));
listen(listen_sock, 8);
FD_ZERO(&read);
FD_ZERO(&temp);
FD_SET(listen_sock, &read);
while (1)
{
temp = read;
if (select(FD_SETSIZE, &temp, (fd_set *) 0, (fd_set *) 0,
(struct timeval *) 0) < 1)
{
perror("select error:");
exit(1);
}
for (fd = 0; fd < FD_SETSIZE; fd++)
{
//CHECK all file descriptors
if (FD_ISSET(fd, &temp))
{
if (fd == listen_sock)
{
conn_sock = accept(listen_sock, (struct sockaddr *) &c_addr, &c_len);
FD_SET(conn_sock, &read);
printf("new client got session: %d\n", conn_sock);
}
else
{
nbytes = recv(fd, &conn, 4, 0);
if (nbytes <= 0)
{
close(fd);
FD_CLR(fd, &read);
}
else
{
if (conn == Session_Rq)
{
ack = Session_Ack;
send(fd, &ack, sizeof(ack), 0);
root_setting();
c = 0;
while (1)
{
c++;
printf("in while loop\n");
recv(fd, &page_num, 4, 0);
if (c > 1)
{
change = compare_with_pre_page(pre, page_num);
if (change == 1)
{
page_stack[stack_count] = page_num;
stack_count++;
}
else
{
printf("same as before page\n");
}
} //end of if
else if (c == 1)
{
page_stack[stack_count] = page_num;
stack_count++;
}
printf("stack count:%d\n", stack_count);
printf("in page stack: <");
for (x = 0; x < stack_count; x++)
{
printf(" %d ", page_stack[x]);
}
printf(">\n");
rq_handler(fd);
if (logged_in == 1)
{
printf("You are logged in state now, user: %s\n",
curr_user.ID);
}
else
{
printf("not logged in.\n");
c = 0;
}
pre = page_num;
} //end of while
} //end of if
}
} //end of else
} //end of fd_isset
} //end of for loop
} //end of outermost while
}
if needed for code explanation : What I was about to work of this code was,
to make kind of web pages to implement 'browser' for server.
I wanted to make every client get session for server to get login-page or so.
But the execution result is, as I told above.
Why is that?
the socket in the client program must be non-blocking mode too
to be used with non-blocking Server program to use select()?
Or should I use fork or thread to make multi client and manage with select?
The reason I say this is, after I considered a lot about this problem,
'select()' seems only proper for multi client chatting program... that many
'forked' or 'threaded' clients can pend to, in such as chat room.
how do you think?...
Is select also possible or proper thing to use for normal multi-client program?
If there something I missed to let my multi client program work fine,
please give me some knowledge of yours or some requirements for the proper use of select.
I didn`t know multi-client communication was not this much easy before :)
I also considered to use epoll but I think I need to understand first about select well.
Thanks for reading.
Besides the fact you want to go from single-client to multi-client, it's not very clear what's blocking you here.
Are you sure you fully understood how does select is supposed to work ? The manual (man 2 select on Linux) may be helpful, as it provides a simple example. You can also check Wikipedia.
To answer your questions :
First of all, are you sure you need non-blocking mode for your sockets ? Unless you have a good reason to do so, blocking sockets are also fine for multi-client networking.
Usually, there are basically two ways to deal with multi-clients in C: fork, or select. The two aren't really used altogether (or I don't know how :-) ). Models using lightweight threads are essentially asynchronous programming (did I mention it also depends on what you mean by 'asynchronous' ?) and may be a bit overkill for what you seem to do (a good example in C++ is Boost.Asio).
As you probably already know, the main problem when dealing with more than one client is that I/O operations, like a read, are blocking, not letting us know when there's a new client, or when a client has said something.
The fork way is pretty straighforward : the server socket (the one which accepts the connections) is in the main process, and each time it accepts a new client, it forks a whole new process just to monitor this new client : this new process will be dedicated to it. Since there's one process per client, we don't care if i/o operations are blocking or not.
The select way allows us to monitor multiple clients in one same process : it is a multiplexer telling us when something happens on the sockets we give it. The base idea, on the server side, is first to put the server socket on the read_fds FD_SET of the select. Each time select returns, you need to do a special check for it : if the server socket is set in the read_fds set (using FD_ISSET(...)), it means you have a new client connecting : you can then call accept on your server socket to create the connection.
Then you have to put all your clients sockets in the fd_sets you give to select in order to monitor any change on it (e.g., incoming messages).
I'm not really sure of what you don't understand about select, so that's for the big explaination. But long story short, select is a clean and neat way to do single-threaded, synchronous networking, and it can absolutely manage multiple clients at the same time without using any fork or threads. Be aware though that if you absolutely want to deal with non-blocking sockets with select, you have to deal extra error conditions that wouldn't be in a blocking way (the Wikipedia example shows it well as they have to check if errno isn't EWOULDBLOCK). But that's another story.
EDIT : Okay, with a little more code it's easier to know what's wrong.
select's first parameter should be nfds+1, i.e. "the highest-numbered file descriptor in any of the three sets, plus 1" (cf. manual), not FD_SETSIZE, which is the maximum size of an FD_SET. Usually it is the last accept-ed client socket (or the server socket at beginning) who has it.
You shouldn't do the "CHECK all file descriptors" for loop like that. FD_SETSIZE, e.g. on my machine, equal to 1024. That means once select returns, even if you have just one client you would be passing in the loop 1024 times ! You can set fd to 0 (like in the Wikipedia example), but since 0 is stdin, 1 stdout and 2 stderr, unless you're monitoring one of those, you can directly set it to your server socket's fd (since it is probably the first of the monitored sockets, given socket numbers always increase), and iterate until it is equal to "nfds" (the currently highest fd).
Not sure that it is mandatory, but before each call to select, you should clear (with FD_ZERO for example) and re-populate your read fd_set with all the sockets you want to monitor (i.e. your server socket and all your clients sockets). Once again, inspire yourself of the Wikipedia example.

Trouble when trying to define a write_all() socket function in C

I am currently using this function in a C client program. Everything seems to work fine but when the server to which this client is connected is shut down, write_all() returns 4 (that's len) instead of the expected -1.
int write_all(int sock, const void *buf, size_t len)
{
int buf_size = len;
while(len > 0)
{
int result = write(sock, buf, len);
if(result < 0)
{
if(errno == EINTR)
continue;
return result;
}
buf += result;
len -= result;
}
return buf_size;
}
Is there anything I am missing in this function? Is there any other function I can call beforehand to make sure the server is still up?
Thanks
You say "shut down", do you mean that you switch the power off, without gracefull TCP closing?
In that case write call returns with success. Data is in TCP sending buffer, and TCP stack does not yet know that peer is down. Program will get EPIPE or other error during later calls.
TCP stack will try retransmission a while, before making decision of connection failure.
To me this looks like you won't get around implementing some sort of hand shake.
As if it's not enough for your sender to know the data it send had been fully received (what I assume is the case), but it also needs to know if any kind on processing had been done on it by the receiver, you expect more from the socket's mechanics than they can provide ...
The sockets are just the transmitter.
Note: I'm assuming TCP here.
From the return value, I gather that the client managed to write 4 bytes to the send buffer before learning that the server closed its end or otherwise disappeared. If it disappeared without proper closing, the only way to know would be a timed-out send. The next write, shutdown or close after that will get the error.
If you want to get prompt notification of disappearing endpoints without having to constantly send data, you can activate the socket keepalive option. In Linux, that would be a setsockopt(..., SOL_SOCKET, SO_KEEPALIVE, ...), and TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT at the SOL_TCP level.

How to find the socket connection state in C?

I have a TCP connection. Server just reads data from the client. Now, if the connection is lost, the client will get an error while writing the data to the pipe (broken pipe), but the server still listens on that pipe. Is there any way I can find if the connection is UP or NOT?
You could call getsockopt just like the following:
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (socket_fd, SOL_SOCKET, SO_ERROR, &error, &len);
To test if the socket is up:
if (retval != 0) {
/* there was a problem getting the error code */
fprintf(stderr, "error getting socket error code: %s\n", strerror(retval));
return;
}
if (error != 0) {
/* socket has a non zero error status */
fprintf(stderr, "socket error: %s\n", strerror(error));
}
The only way to reliably detect if a socket is still connected is to periodically try to send data. Its usually more convenient to define an application level 'ping' packet that the clients ignore, but if the protocol is already specced out without such a capability you should be able to configure tcp sockets to do this by setting the SO_KEEPALIVE socket option. I've linked to the winsock documentation, but the same functionality should be available on all BSD-like socket stacks.
TCP keepalive socket option (SO_KEEPALIVE) would help in this scenario and close server socket in case of connection loss.
There is an easy way to check socket connection state via poll call. First, you need to poll socket, whether it has POLLIN event.
If socket is not closed and there is data to read then read will return more than zero.
If there is no new data on socket, then POLLIN will be set to 0 in revents
If socket is closed then POLLIN flag will be set to one and read will return 0.
Here is small code snippet:
int client_socket_1, client_socket_2;
if ((client_socket_1 = accept(listen_socket, NULL, NULL)) < 0)
{
perror("Unable to accept s1");
abort();
}
if ((client_socket_2 = accept(listen_socket, NULL, NULL)) < 0)
{
perror("Unable to accept s2");
abort();
}
pollfd pfd[]={{client_socket_1,POLLIN,0},{client_socket_2,POLLIN,0}};
char sock_buf[1024];
while (true)
{
poll(pfd,2,5);
if (pfd[0].revents & POLLIN)
{
int sock_readden = read(client_socket_1, sock_buf, sizeof(sock_buf));
if (sock_readden == 0)
break;
if (sock_readden > 0)
write(client_socket_2, sock_buf, sock_readden);
}
if (pfd[1].revents & POLLIN)
{
int sock_readden = read(client_socket_2, sock_buf, sizeof(sock_buf));
if (sock_readden == 0)
break;
if (sock_readden > 0)
write(client_socket_1, sock_buf, sock_readden);
}
}
Very simple, as pictured in the recv.
To check that you will want to read 1 byte from the socket with MSG_PEEK and MSG_DONT_WAIT. This will not dequeue data (PEEK) and the operation is nonblocking (DONT_WAIT)
while (recv(client->socket,NULL,1, MSG_PEEK | MSG_DONTWAIT) != 0) {
sleep(rand() % 2); // Sleep for a bit to avoid spam
fflush(stdin);
printf("I am alive: %d\n", socket);
}
// When the client has disconnected, this line will execute
printf("Client %d went away :(\n", client->socket);
Found the example here.
I had a similar problem. I wanted to know whether the server is connected to client or the client is connected to server. In such circumstances the return value of the recv function can come in handy. If the socket is not connected it will return 0 bytes. Thus using this I broke the loop and did not have to use any extra threads of functions. You might also use this same if experts feel this is the correct method.
get sock opt may be somewhat useful, however, another way would to have a signal handler installed for SIGPIPE. Basically whenever you the socket connection breaks, the kernel will send a SIGPIPE signal to the process and then you can do the needful. But this still does not provide the solution for knowing the status of the connection. hope this helps.
You should try to use: getpeername function.
now when the connection is down you will get in errno:
ENOTCONN - The socket is not connected.
which means for you DOWN.
else (if no other failures) there the return code will 0 --> which means UP.
resources:
man page: http://man7.org/linux/man-pages/man2/getpeername.2.html
On Windows you can query the precise state of any port on any network-adapter using:
GetExtendedTcpTable
You can filter it to only those related to your process, etc and do as you wish periodically monitoring as needed. This is "an alternative" approach.
You could also duplicate the socket handle and set up an IOCP/Overlapped i/o wait on the socket and monitor it that way as well.
#include <sys/socket.h>
#include <poll.h>
...
int client = accept(sock_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
pollfd pfd = {client, POLLERR, 0}; // monitor errors occurring on client fd
...
while(true)
{
...
if(not check_connection(pfd, 5))
{
close(client);
close(sock[1]);
if(reconnect(HOST, PORT, reconnect_function))
printf("Reconnected.\n");
pfd = {client, POLLERR, 0};
}
...
}
...
bool check_connection(pollfd &pfd, int poll_timeout)
{
poll(&pfd, 1, poll_timeout);
return not (pfd.revents & POLLERR);
}
you can use SS_ISCONNECTED macro in getsockopt() function.
SS_ISCONNECTED is define in socketvar.h.
For BSD sockets I'd check out Beej's guide. When recv returns 0 you know the other side disconnected.
Now you might actually be asking, what is the easiest way to detect the other side disconnecting? One way of doing it is to have a thread always doing a recv. That thread will be able to instantly tell when the client disconnects.

Resources