How to use Accept() for multiple connections - c

I'm trying to send http response to the browser at my code (I use virtual machine of linux), when I debug my code I can tell that after one request and response, the accpet() function doesn't wait and continue the procedure even if I did close the socket-fd.
Does anyone have any idea why is this happening?
Here is my code:
while(1){ // infinite loop for requests
if (( user_socket = accept(fsockfd, (struct sockaddr *)&serv_addrf, // wait for a request from customer and accept new socket
(socklen_t*)&addrlenf))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
while(flag_rec!=1){
if(recv(user_socket, customer_reply , BUFSIZE , 0) < 0) // get the request from customer
{
puts("recv failed my man"); // in case of failing
}
for(int i=0; i<BUFSIZE;i++){
if(customer_reply[i]=='\r'&&customer_reply[i+1]=='\n'&&customer_reply[i+2]=='\r'&&customer_reply[i+3]=='\n'){ //checking if there is \r\n\r\n
customer_reply[i+4]='\0';
customer_reply_size=i+4;
flag_rec=1; // it means we received the full http request
break;
}
}
}
flag_rec=0;
if(send(serv_socket,customer_reply,customer_reply_size,0)<0){ // sending the http request to the servers
puts("send failed"); // in case of failing
}
while(flag_rec!=1){
if(recv(serv_socket,server_reply,BUFSIZE,0)<0){ // get the answer message from the servers
puts("recv failed mann"); // in case of failing
}
for(int i=0; i<BUFSIZE;i++){
if(server_reply[i]=='\r'&&server_reply[i+1]=='\n'&&server_reply[i+2]=='\r'&&server_reply[i+3]=='\n'&&server_reply[i+4]=='\0'){ //checking if there is \r\n\r\n
server_reply[i+4]='\0';
server_reply_size=i+4;
flag_rec=1; // it means we received the full http request
break;
}
}
}
flag_rec=0;
if(send(user_socket,server_reply,server_reply_size,0)<0){
puts("send failed"); // in case of failing
}
close(user_socket); // closing the socket after the response
}

Related

Chat room using socket programming with select() - winsock - C

I try to create a server-client application where the server provides a chat service to all clients that connect to the server. The server and client use cryptographic algorithms and protocols to secure data transmitted over the network. I can't figure out why the chat code isn't working properly.
I use the select() function to operate multiple drawers at the same time. If I use only a piece of code when multiple clients connect to the server and send data to the server and it gets everything, that's fine, but as soon as I try to write a piece of code that would be a chat function, even if multiple clients connect, the server serves only the last connected client. I use a link dynamic list to store the necessary client information, and when I can list currently connected clients, if I don't use part of the chat room code, all clients I connect will be accepted, and as soon as I use the chat room code part, only the last connected client.
This is code for server:
while(1) {
fd_set reads;
reads = master;
//The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O
if (select(max_socket+1, &reads, 0, 0, 0) < 0) {
fprintf(stderr, "select() failed. (%d)\n", GETSOCKETERRNO());
return 1;
}
SOCKET i;
//Loop through each possible socket
for(i = 1; i <= max_socket; ++i) {
if (FD_ISSET(i, &reads)) {
//If socket_listen, create TCP connection of accept() function
if (i == socket_listen) {
//
client_info = create_client();
client_info->client_len = sizeof(client_info->client_address);
client_info->sock_fd = accept(socket_listen,
(struct sockaddr*) &client_info->client_address,
&client_info->client_len);
if (!ISVALIDSOCKET(client_info->sock_fd)) {
fprintf(stderr, "accept() failed. (%d)\n",
GETSOCKETERRNO());
return 1;
}
FD_SET(client_info->sock_fd, &master);
if (client_info->sock_fd > max_socket)
max_socket = client_info->sock_fd;
//Prints the client address using the getnameinfo() function
getnameinfo((struct sockaddr*)&client_info->client_address,
client_info->client_len,
client_info->address_buffer,
100, 0, 0,
NI_NUMERICHOST);
printf("New connection %s\n", client_info->address_buffer);
printf("\nWaiting for succeses Salt handshake...\n");
//Salt handshake
salt_hndshk(client_info);
//Insert client to the list of clients
insert(p_list, client_info);
//List of clients connected to the server with a successful Salt handshake
listing_clients(p_list);
} else {
memset(rx_buffer, 0, sizeof(hndsk_buffer));
//Search for clients by sockets and the is in the list
//the server decrypts the data from the client
CLIENT *client_decrypt = create_client();
client_decrypt = search_client(p_list, i);
ret_msg = salt_read_begin_pom(&client_decrypt->channel, rx_buffer,
sizeof(rx_buffer), &msg_in, pom_buffer, &decrypt_size);
//Check if SALT_ERROR from message
if(ret_msg == SALT_ERROR) {
printf("\tThe client disconnects from the server.\n");
printf("\tThe server has closed him socket\n");
realese_client(p_list, client_decrypt);
FD_CLR(i, &master);
CLOSESOCKET(i);
continue;
}
//Freeing client memory
free(client_decrypt);
}
//Chat room service
SOCKET j;
for(j = 1; j <= max_socket; ++j){
if(FD_ISSET(j, &master)){
if (j == socket_listen || j == i){
continue;
} else {
memset(rx_buffer, 0, sizeof(hndsk_buffer));
//Search for clients by sockets and the is in the list
CLIENT *client_encrypt = create_client();
client_encrypt = search_client(p_list, j);
//Prepare data before send
salt_write_begin(tx_buffer, sizeof(tx_buffer), &msg_out);
//Copy clear text message to be encrypted to next encrypted package
salt_write_next(&msg_out, (uint8_t * )pom_buffer, decrypt_size);
//Wrapping, creating encrpted messages
salt_write_execute(&client_encrypt->channel, &msg_out, false);
//Freeing client memory
free(client_encrypt);
}
} //if(FD_ISSET(j, &master)
} //for(j = 1; j <= max_socket; ++j)
//Finish chat room service
} //if FD_ISSET
} //for i to max_socket
}
There is a link to the application on this link:
tcp_salt
You have logic errors in both of your inner for loops.
When reading from/writing to a non-listening client socket, DO NOT call create_client() at all, you are creating memory leaks with it:
CLIENT *client_decrypt = create_client();
client_decrypt = search_client(...); // <-- LEAK!
CLIENT *client_encrypt = create_client();
client_encrypt = search_client(...); // <-- LEAK!
Call create_client() ONLY when you accept() a new client. And DO NOT call free() on any CLIENT you read from/write to. Call that ONLY when you are removing a CLIENT from p_list.
You are corrupting your p_list on each loop iteration, leaving it with a bunch of dangling pointers to invalid CLIENTs.
Also, your writing code is not checking for errors to disconnect and remove dead clients.
Try something more like this:
while(1) {
fd_set reads;
reads = master;
//The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O
if (select(max_socket+1, &reads, 0, 0, 0) < 0) {
fprintf(stderr, "select() failed. (%d)\n", GETSOCKETERRNO());
return 1;
}
//Loop through each possible socket
for(SOCKET i = 1; i <= max_socket; ++i) {
if (!FD_ISSET(i, &master)) {
continue;
}
if (FD_ISSET(i, &reads)) {
//If socket_listen, create TCP connection of accept() function
if (i == socket_listen) {
//
CLIENT *client_info = create_client();
client_info->client_len = sizeof(client_info->client_address);
client_info->sock_fd = accept(socket_listen,
(struct sockaddr*) &client_info->client_address,
&client_info->client_len);
if (!ISVALIDSOCKET(client_info->sock_fd)) {
fprintf(stderr, "accept() failed. (%d)\n",
GETSOCKETERRNO());
return 1;
}
FD_SET(client_info->sock_fd, &master);
if (client_info->sock_fd > max_socket)
max_socket = client_info->sock_fd;
//Prints the client address using the getnameinfo() function
getnameinfo((struct sockaddr*)&client_info->client_address,
client_info->client_len,
client_info->address_buffer,
100, 0, 0,
NI_NUMERICHOST);
printf("New connection %s\n", client_info->address_buffer);
printf("\nWaiting for succesful Salt handshake...\n");
//Salt handshake
salt_hndshk(client_info);
//Insert client to the list of clients
insert(p_list, client_info);
//List of clients connected to the server with a successful Salt handshake
listing_clients(p_list);
continue;
}
memset(rx_buffer, 0, sizeof(rx_buffer));
//Search for clients by sockets and the is in the list
//the server decrypts the data from the client
CLIENT *client_decrypt = search_client(p_list, i);
ret_msg = salt_read_begin_pom(&client_decrypt->channel, rx_buffer,
sizeof(rx_buffer), &msg_in, pom_buffer, &decrypt_size);
//Check if SALT_ERROR from message
if (ret_msg == SALT_ERROR) {
printf("\tThe client disconnects from the server.\n");
printf("\tThe server has closed his socket\n");
release_client(p_list, client_decrypt);
free(client_decrypt);
CLOSESOCKET(i);
FD_CLR(i, &master);
continue;
}
//Chat room service
for(SOCKET j = 1; j <= max_socket; ++j){
if (!FD_ISSET(j, &master) || j == socket_listen || j == i){
continue;
}
memset(rx_buffer, 0, sizeof(rx_buffer));
//Search for clients by sockets and the is in the list
CLIENT *client_encrypt = search_client(p_list, j);
//Prepare data before send
ret_msg = salt_write_begin(tx_buffer, sizeof(tx_buffer), &msg_out);
//Copy clear text message to be encrypted to next encrypted package
if (ret_msg != SALT_ERROR)
ret_msg = salt_write_next(&msg_out, (uint8_t * )pom_buffer, decrypt_size);
//Wrapping, creating encrpted messages
if (ret_msg != SALT_ERROR
ret_msg = salt_write_execute(&client_encrypt->channel, &msg_out, false);
//Check if SALT_ERROR from message
if (ret_msg == SALT_ERROR) {
printf("\tThe client disconnects from the server.\n");
printf("\tThe server has closed his socket\n");
release_client(p_list, client_decrypt);
free(client_decrypt);
CLOSESOCKET(i);
FD_CLR(i, &master);
continue;
}
}
}
}
}

C - SIGALRM not received

I'm new to signals, I'm trying to set SIGALRM on UDP echo service, as a socket programming practice.
So here I have a UDP socket, the client sends a string to server and waits for response (any response, here the string is echoed by server).
The goals is to set SIGALRM and let the client resend the string a few times if no responses were made by server or UDP packets get lost.
Here, I used a small sample and simplified long lines with ..., you can get more details on my github repo (line 51)
sigALRM-Client.c
unsigned int tries = 0;
void CatchAlarm()
{
tries += 1;
}
int main(int argc, char **argv)
{
// SKIPPED
// ...
struct sigaction handler;
handler.sa_handler = CatchAlarm;
handler.sa_flags = 0;
if(sigfillset(&handler.sa_mask) < 0)
return 1;
if(sigaction(SIGALRM, &handler, 0) < 0)
return 2;
ssize_t bytes;
bytes = sendto(servSock,...);
while((bytes = recvfrom(servSock,...)) < 0) {
// alarm went off
if(errno == EINTR) {
// try 5 times
if(tries < 5) {
bytes = sendto(servSock,...);
} else {
fprintf(stdout, "no response, waiting...\n");
}
} else {
fprintf(stdout, "failed to get data\n");
return 3;
}
}
// recvfrom() got something, cancel timeout
alarm(0);
fprintf(stdout, "received %d bytes of data\n", bytes);
close(servSock);
}
When I run the client, it won't receive SIGALRM signal and UDP packets get lost in first attempt?!
Client won't retry sending string then exit after 5 attempts, instead, it waits for server response forever!
What prevents client to get SIGALRM?
Did I miss something here?
Your code in the GitHub repo never calls alarm() with a non-zero number. You'll never get an alarm signal delivered automatically unless you actually request one. Relying on some other process to send your process an alarm signal isn't resilient.

Chrome closes TCP connection after response

I have this code that handles some very basic http requests. I want it to support persistent connections. If I request some pages through firefox the session/connection is reused as indended. Chrome, however, closes the connection after each request/response independent of whether the Connection: keep-alive header is included or not.
Is this intended or is my read/write loop wrong?
#include<sys/socket.h>
// some setup code
listen(sockfd, 5);
// accept new connections
while (true) {
int session_sock_fd = accept(sockfd, (struct sockaddr*)&cli_addr, &clilen);
if (newsockfd < 0) {
// error
} else {
// create session
}
// session main loop, each session runs in its own thread
while(true) {
int n = read(session_sock_fd, buffer, buffer_size);
if (n < 0) {
// connection time out or some error
break;
} else if (n == 0) {
break; // client has closed the conneciton
}
// parse the request
// send response
char* resp_data = "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\n"
"Content-Length: xxx\r\nDate: some date\r\n\r\n"
"response_body\r\n";
n = write(session_sock_fd, resp_data, size);
if (n < 0) {
// error, unable to write to socket
break;
}
}
It seems my problem was that I didn't include the final "\r\n" when I calculated content-length. It seems to be working now!

Unknown condition satisfaction in pthread cond timedwait

There's something going on in my code that I didn't intend.
I've got a program with several threads. One thread reads a rfid chip and saves the rfid code string into a global struct variable. There are also some other threads that are opened when a client, which is supposed to get the string, connects to the program on a certain socket. They are on a timed waiting (pthread_cond_timedwait) for the reading thread to broadcast a pthread condition variable (thread->done) that signals that a rfid chip was read and there is a new string to be sent to the clients. Now to the problem: the pthread_cond_timedwait in the sending thread shall only be satisfied when a new string is ready to be sent, but it also triggers when a new client thread is opened. The client threads start with the function below. I hope I explained my problem clearly and anyone can help me with the problem.
code of the sending function:
void* send_data(void* arg)
{
int client_id;
int status_timeout;
struct timespec timeout;
struct Thread_parameter *thread = (struct Thread_parameter*)arg;
pthread_condattr_t attr;
pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&thread->done, &attr);
// read the client id from struct
pthread_mutex_lock(&thread->mutex);
client_id = thread->t_client_id;
pthread_mutex_unlock(&thread->mutex);
while (1)
{
// wait for string
while (1)
{
pthread_mutex_lock(&thread->mutex);
status_timeout = ETIMEDOUT;
while (1)
{
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec += 1;
status_timeout = pthread_cond_timedwait(&thread->done, &thread->mutex, &timeout);
// Timeout
if (status_timeout == ETIMEDOUT)
{
if (is_connected(client_id))
continue;
else
{
log_output("Lost connection to client '%i'", client_id);
pthread_mutex_unlock(&thread->mutex);
pthread_exit(NULL);
}
}
else if (status_timeout == 0)
{
// condition satisfied
break;
}
}
pthread_mutex_unlock(&thread->mutex);
break;
}
// send the string to the client when the condition is satisfied
while (1)
{
pthread_mutex_lock(&thread->mutex);
if (send(client_id, thread->t_chip_id, CHIP_ID_LENGTH, MSG_NOSIGNAL) >= 0)
{
pthread_mutex_unlock(&thread->mutex);
log_output("Successfully sent data to client '%i'", client_id);
break;
}
else
{
pthread_mutex_unlock(&thread->mutex);
// sending not successful, check if client is still connected
if (is_connected(client_id))
continue;
else
{
close(client_id);
log_output("Lost connection to client '%i'", client_id);
pthread_exit(NULL);
}
}
}
}
}

C: Question about Beej's networking guide... Is there an assumption here?

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.

Resources