Client/server chatroom: Handle unexpected disconnect - c

I wrote in C a server - client chatroom.
The server creates a new pthread for every new connection to a client, this pthread waits for a message to receive, and sends this message to all the other clients (using a array of all the file descriptors). If a client wants to quit he informs the server and he will terminate the pthread and delete the file descriptor from the array
This works fine !, but:
if a client disconnects unexpected, by closing the terminal for example, the server won't delete the file descriptor from the array and when an other client wants to send a message i have an error because the pthread tries to send the message to a fd which isn't a connection any more
Now my question:
How can in test if the file descriptor of a client's socket is still active before i send a message ?
the part of my code (from the pthread):
for(i=0; i<*p_Nbr_Clients; i++){ // send the message to all the other clients
if (fd_array[i] != fd){ // <- i want to test the fd here
if ( send(fd_array[i], msg, strlen(msg), 0) == -1 ){
perror("Serveur: send");
}
}
}

Check the return value of the recv().
If the user terminated abnormally then return value should be zero 0.
Based on that you can close fd easily.
if(recv(fd,buffer,length,flag) == 0)
close(fd);

There is no standalone api to check whether socket is closed. Just try to send data to that socket.
send will return -1 if you write to a closed socket. and errno will be set to appropriately. You may got EBADF or ECONNRESET i guess. Check (Check connection open or closed ?(in C in Linux)) and (How to find out if a socket is closed)
for(i=0; i<*p_Nbr_Clients; i++){ // send the message to all the other clients
if (fd_array[i] != fd){ // <- i want to test the fd here
if ( send(fd_array[i], msg, strlen(msg), 0) == -1 ){
//perror("Serveur: send");
// something wrong, check errno to see more detail
// you need to include <errno.h> to use errno
close(fd_array[i]);
fd_array[i] = -1;// or something you define as not open
}
}
}

Related

SSL_connect succeeding but returning -1

I'm trying to do a https connection over TLSv1.2. Context creation and everything is fine which I've not included in the below code.
if(https) //block 1
{
int ssl_err = 0;
int ssl_rc = 0;
ssl_fd = SSL_new(ctx); /* create new SSL connection state */
if(ssl_fd) //block 2
{
printf("Failure in SSL state creation.");
ssl_err = -1;
}
if(ssl_err == 0) //block 3
{
SSL_set_fd(ssl_fd, fd); /* attach the socket descriptor */
ERR_clear_error();
ssl_rc = SSL_connect(ssl_fd); /* perform the connection*/
printf("Failure in SSL connection %d returned.\n", ssl_rc);
if ( ssl_rc == -1 ) //block 4
ssl_err = -1;
}
if ( ssl_err == -1 ) //block 5
{
printf("Failure in SSL connection.\n");
SSL_free(ssl_fd);
shutdown(fd, 2);
abort();
}
}
In the code above, it's showing output as
Failure in SSL connection -1 returned.
Failure in SSL connection.
I've checked the packets file. Immediately (in 200 microseconds) after sending the client hello, it is going to if block 5 and sending a FIN request which kept me worried to find the error which I couldn't as without server's response, the SSL_connect is returning with error.
I commented if block 5 and tested. To my surprise, since the shutdown is not called, the SSL handshake is happening and data transfer over TLSv1.2 is going on till the end. That means SSL_connect is actually succeeding but somehow it's happening async. But in this way, I can't report if actually there is some errors in the SSL handshaking.
Can anyone help me with this behaviour?
Whether it's actually doing the handshake asynchronously? If yes, why it's returning -1 immediately. Shouldn't it wait for the handshake to complete before putting -1?
As you mentioned you have non-blocking sockets. In that case if SSL_connect() returns -1you need to call SSL_get_error(), check what it returns SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, then call SSL_connect() again after checking the underlying socket is ready for read/write with select()

Multiclient server socket doesn't print the client messages

I'm trying to implement a client-server application with multiclients using threads. Just to try, I would like to print the messages from each client, but when I send messages from a client, the server does not print anything.
Server (thread code)
void comunicationHandler(void *socket)
{
int sock = *(int*) socket;
char msg[2000];
while ((strcmp(msg, "!quit")) != 0) {
if (recv(sock, msg, 2000, 0) < 0)
puts("Error recv");
printf("%s", msg);
}
puts("Client Disconnected\n");
}
when I send "!quit", the Server goes in a infinite loop printing the messages
Client
for(;;) {
printf("\nInserisci il msg: ");
scanf("%s", msg);
if (strcmp(msg, "!quit") == 0)
break;
write(sd, msg, 2000);
}
There are multiple problems with your code:
TCP is stream based, there is no guarantee that all the bytes you send will be received in one shot on the other side. You need to modify code to check what is the number of bytes received and is it atleast equal to the size of "!quit" before you go in for the "strcmp" comparison.
Better to null terminate the buffer once you receive the buffer equal to the size of "!quit"
It is not clear as to why you are sending a 2000 bytes buffer from the client when you intend to send only "!quit". Modify and send only appropriate size as needed
Check recv return value against 0 also
Break out of the loop in both server and client once the Job is done.
Server goes in a infinite loop
You want to test recv()'s result against 0 and quit in this case. 0 indicates that the client orderly closed the connection.

Starting new file descriptor using select and accept

Scenario: when select detect activity in one socket then below criteria happens in my code.
pseudo code:
after select i am checking in
if stdin f descriptor
do something
else if listening file descriptor
newFDescriptor = accept sockFDescriptor, (struct sockaddr *) &clientAddress, &clientAddressSize
FD_SET (new file descriptor)
send connected response to peer
// data from connected peer
else {
receive data
}
But every time i send something from a peer to other it creates new connection with new filedescriptor. i.e. it doesn't recogonize data in already created filedescriptor for this peer.
peer 1 to peer 2 (new file descriptor created)
peer 1 to peer 2 (again new connection)
It is receiving all data on the listening file descriptor.
If the peer insists on creating a new connection there's nothing you can do about it at the server end.
"It is receiving all data on the listening file descriptor" doesn't begin to make sense. It's impossible. The listening file descriptor can't do anything except accept connections.
I agree with jedwards (+1) -- you should read the Beej's Guide to get you started.
In the mean time, here is some quick input that might help in avoiding the error you are running into. My guess is that you are mixing up the file descriptors.
You would need to add the new file descriptors (the ones from the accept() call) into a list and then use them (also) to populate the fd set for the next select call. The listener fd is only for establishing new connections and subsequent accept() -- you should not be calling receive or send on that fd (let us call it server_fd).
Here is a quick example code that stores all connections in an array, then you can set the fd as follows. For indices of the array that do not have a valid fd, it uses -1.
FD_ZERO(&read_fd_set);
/* Set the fd_set before passing it to the select call */
for (i=0;i < MAX_CONNECTIONS;i++) {
if (all_connections[i] >= 0) {
FD_SET(all_connections[i], &read_fd_set);
}
}
ret_val = select(FD_SETSIZE, &read_fd_set, NULL, NULL, NULL);
Once the select returns, you can check if the fd with the read-event is the server fd and if so, you can call accept() to get the new fd -- you need to add it to the array. Something like this:
if (FD_ISSET(server_fd, &read_fd_set)) {
new_fd = accept(server_fd, (struct sockaddr*)&new_addr, &addrlen);
if (new_fd >= 0) {
printf("Accepted a new connection with fd: %d\n", new_fd);
for (i=0;i < MAX_CONNECTIONS;i++) {
if (all_connections[i] < 0) {
all_connections[i] = new_fd;
break;
}
}
}
}

C: Data forwarding server using epoll ET fills the send buffer

I have the following situation. My server receives data from remote server (fd_server) and forwards it to the client (fd_client). I'm using edge triggered epoll so I can handle multiple clients and multiple server conncetions.
Procedure:
client connects to the server.
my server connects to the remote server and requests data.
remote server responds and my server forwards data to the client.
Details:
After my server connects to the remote server the fd_server is added to epoll control with EPOLLIN flag. Server waits for events.
When epoll_wait return the fd_server as readable I go in the following loop displayed bellow.
After some read/writes my sctp_sendmsg return EAGAIN, which means sctp send buffer is full. How should I handle this situation without loosing the data I have already read from the fd_server socket?
IS there a way of knowing before hand, how much data can I send, so I only read the right amount?
while(1){
N = recv(fd_server,buf, sizeof buf,0);
if (N == -1){
/* If errno == EAGAIN, that means we have read all
data. So go back to the main loop. */
if (errno != EAGAIN){
perror ("tcp_recv error");
}
break;
}
if(N == 0){
/* End of file. The remote has closed the
connection. */
close(fd_server);
break;
}
pos = 0;
while(pos < N){
got = sctp_sendmsg(fd_client, &buf[pos], N-pos, to, tolen, 0, 0, stream, 0, 0);
if(got<=0){
if (errno == EAGAIN){
//what to do?
}else{
perror("tcp to sctp send error");
}
}
else{
pos += got;}
}
}
After some read/writes my sctp_sendmsg return EAGAIN, which means sctp send buffer is full. How should I handle this situation without losing the data I have already read from the fd_server socket?
You need to keep some sort of "context" (data structure) for each fd_client socket. For each new client socket that gets connected to your server, create an instance of a "connection state" struct and store it in a hash table. This will be something like the following:
struct ConnectionState
{
int fd_client; // socket
uint8_t buffer[MAX_CHUNK_SIZE]; // protocol buffer for this connection
int buffer_length; // how many bytes received into this buffer
int pos; // how many bytes transmitted back out on fd_client from "buffer"
int has_data; // boolean to indicate protocol state (1 if there's still data in buffer to send)
};
If you can't send everything at once, toggle the fd_client socket from EPOLLIN to EPOLLOUT in your epoll mode. Change "has_data" to true in the ConnectionState structure. Then go back to waiting for socket events. When you are able to send again, you look at your ConnectionState struct for that socket to decide if you still need to keep sending or receive a new buffer.
Be careful with edge triggered sockets. When you do transition from EPOLLOUT back to EPOLLIN, you need to go ahead and recv() again just to make sure you don't lose any data. (Similarly for entering the send state, try an initial send).

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