Starting new file descriptor using select and accept - c

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;
}
}
}
}

Related

Confusion about select()

I'm busy with this for 2 days now and still don't understand it. What does select() do in this code?
I know that if there is an incoming connection that can be accepted, the copy.fd_array[] will contain ListenSocket but when the while loop repeats it's still there. So how do we know if a client is disconnected? What does fd_set copy contain after the select() call?
fd_set master;
FD_ZERO(&master);
FD_SET(ListenSocket, &master);
while (1)
{
fd_set copy = master;
select(FD_SETSIZE, &copy, NULL, NULL, NULL);
for (int i = 0; i < FD_SETSIZE; i++)
{
// If new connection
if (FD_ISSET(ListenSocket, &copy))
{
printf("[+] New connection\n");
// Accept connection
SOCKET AcceptedClient = accept(ListenSocket, NULL, NULL);
FD_SET(AcceptedClient, &master);
// Send welcome message to client
char buff[128] = "Hello Client!";
send(AcceptedClient, buff, sizeof(buff), 0);
}
}
}
I'm busy with this for 2 days now and still don't understand it.
It's no wonder that you don't understand the code: The code in the example is nonsense.
Checking the ListenSocket should be done outside the for loop. And FD_ISSET must also be checked for the connections accepted using accept.
The correct code inside the while loop would look like this:
fd_set copy = master;
select(FD_SETSIZE, &copy, NULL, NULL, NULL);
// If new connection
if (FD_ISSET(ListenSocket, &copy))
{
...
}
for (int i = 0; i < FD_SETSIZE; i++)
{
// If an existing connection has data
// or the connection has been closed
if ((i != ListenSocket) && FD_ISSET(i, &copy))
{
nBytes = recv(i, buffer, maxBytes, 0);
// Connection dropped
if(nBytes < 1)
{
close(i); // other OSs (Linux, MacOS ...)
// closesocket(i); // Windows
FD_CLR(i, &master);
}
// Data received
else
{
...
}
}
}
I know that if there is an incoming connection that can be accepted, the copy.fd_array[] will contain ListenSocket but when the while loop repeats it's still there.
What does fd_set copy contain after the select() call?
First of all: Before calling select(), copy.fd_array[] must contain all socket handles that you are interested in. This means it must contain ListenSocket and all handles returned by accept().
master.fd_array[] contains all these handles, so fd_set copy = master; will ensure that copy.fd_array[] also contains all these handles.
select() (with NULL as last argument) will wait until at least one socket becomes "available". This means that it will wait until at least one of the following conditions is true:
A connection accepted using accept() is closed by the other side
a connection accepted using accept() has data that can be received
there is a new connection that can be accepted using accept(ListenSocket...)
As soon as one condition is fulfilled, select() removes all other handles from copy.fd_array[]:
ListenSocket is removed from copy.fd_array[] if there is no incoming connection
A handle returned by accept() is removed from that array if the connection has neither been closed nor new data is available
If two events happen the same time, copy.fd_array[] will contain more than one handle.
You use FD_ISSET() to check if some handle is still in the array.
So how do we know if a client is disconnected?
When you detect FD_ISSET(i, &copy) for a value i that has been returned by accept(), you must call recv() (under Linux read() would also work):
If recv() returns 0 (or negative in the case of errors), the other computer has dropped the connection. You must call close() (closesocket() on Windows) and remove the handle from copy.fd_array[] (this means: you must remove it from master.fd_array[] because of the line fd_set copy = master;).
If recv() returns a positive value, this is the number of bytes that have been received.

Select returns success even if connection is terminated by peer socket

I am having trouble understanding the use of select system call. As per select documentation , select will return after timeout or socket is ready for I/O operation. What does it mean by socket is ready for I/O operation ?
received_len = 0
do {
/* There is only one fd in readfds */
rv = select(fd+1, &readfds, NULL, NULL, timeout)
if (rv < 0){
/*socket select failed.*/
break
}else if (rv == 0){
/*socket timeout.*/
break
}else
/* Go for reading */
readbytes = read(fd, (buf + received_len), (expected_len - received_len))
if (readbytes < 0)
{
/*read fail*/
break
}
received_len += readbytes;
}while(received_len < expected_len)
I though select will timed out when peer socket connection closes. but after closing peer connection select returns 1 and read happens on socket. Then read call returns 0 (Which basically means peer terminated). I thought select will be timed out since the socket is not still ready for read operation but that is not happenning.
From the documentation you cited in the question
readfds
The file descriptors in this set are watched to see if
they are ready for reading. A file descriptor is ready
for reading if a read operation will not block; in
particular, a file descriptor is also ready on end-of-
file.

Proper way to send data when using select() and write_fds in C

So I'm building a chat server, now I'm trying to echo all the messages from the client. Currently as soon as I get the message, I send it back within readData(). However, as soon as I send it, select() notifies the write_fds and sendData() is called, even I already called send()
Most of my calls to send data would be inside the readData().
Is this the right way of using select() and write_fds?
How can I notify select() that I want to send data without two calls to send()?
It seems redundant to me having to deal with two calls to send().
int readData(int j){
// get message from the client
recv(j, client_buffer , 6000 , 0);
// echo message to the client
send(j, client_buffer, strlen(client_buffer));
}
int sendData(int j){
send(j, buf, nbytes, 0);
}
for(;;){
read_fds = master;
write_fds = master;
if(select(fdmax+1, &read_fds, &write_fds, NULL, NULL) == -1){
exit(4);
}
for(i = 0; i <= fdmax; i++){
if(FD_ISSET(i, &read_fds)){
if(i == listener){
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener, (struct sockaddr *)&addr, &addrlen);
FD_SET(newfd, &master);
if(newfd > fdmax) fdmax = newfd;
}else{
// we got some data from a client
readData(i);
}
}
if(FD_ISSET(i, &write_fds)){
if(i != listener){
// send data when notified
sendData(i);
}
}
}
}
I would not suggest calling sendData() directly inside of readData(). They should be kept separate. Have readData() return the received data to the caller, and let the caller decide what to do with it. If the caller wants to send data, it can then call sendData() as needed.
To address the select() issue, you need to create a per-socket buffer for outgoing data. And make sure the socket is running in non-blocking mode.
If sendData() is called when the buffer is empty, send() as much of the caller's data as possible. send() will return how many bytes it actually accepted. If send() reports a EWOULDBLOCK or EAGAIN error, stop sending and append any unsent data to the end of the buffer.
If sendData() is called when the buffer is not empty, just append the new data to the end of the buffer and exit without calling send() at all.
Whenever select() reports a socket is writable, send() whatever is currently cached in that socket's buffer, if anything. For each successful send(), remove the reported number of bytes from the front of the buffer. Stop sending if the buffer is exhausted or send() fails.

Client/server chatroom: Handle unexpected disconnect

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
}
}
}

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