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!
Related
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;
}
}
}
}
}
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
}
I create a server that receive http request and print that request. I can successfully print out http request that I receive without closing that client socket but when I close the client socket, the server print out multiple http request that I already printed out before.
This is my code:
int handleConnection(int sockfd)
{
// sockfd is new_sockfd
char fullReq[MAX_HEADER_SIZE];
receiveReq(sockfd, fullReq);
printf("fullReq in handleConnection\n%s", fullReq);
close(sockfd); // bug occur when I close it
return 0;
}
This is the code of recvReq function:
int receiveReq(int sockfd, char *fullReq)
{
char buffer[1];
memset(buffer, 0, sizeof(buffer));
char *bufferPtr = buffer;
char terminators[] = {'\r', '\n'};
int number_of_requests = 0;
int matchedTerminators = 0;
while(number_of_requests <= MAX_HEADER_SIZE)
{
if(matchedTerminators >= 4)
break;
recv(sockfd, bufferPtr, 1, 0);
strncat(fullReq, bufferPtr, 1);
number_of_requests++;
if(buffer[0] == terminators[0] || buffer[0] == terminators[1])
matchedTerminators++;
else
matchedTerminators = 0;
}
return 0;
}
Output if I didn't close the socket:
fullReq in handleConnection
GET / HTTP/1.1
Output if I close the socket:
fullReq in handleConnection
GET / HTTP/1.1
fullReq in handleConnection
GET / HTTP/1.1
fullReq in handleConnection
GET / HTTP/1.1
more...
You should check for the return code of recv. When the client socket closes, recv is likely to fail (depends on how you open your server's socket), and bufferPtr will remain with the same contents, the last request. You can detect if recv failed, for example because the client socket has closed, by checking if it has returned -1. You can check the man pages for recv on various reasons recv might have failed.
So I'm trying to do the following:
I have two participants (let's call them A and B) communicating via TCP socket (send() and recv()). A is sending a counter and a random Nonce, B is just responding with that same message it gets. A then checks if the response matches the sent packet and if yes, it increments the counter and repeats.
This is a code snippet illustrating what A does at the moment:
send(sock, payload, strlen(payload), 0);
struct timeval t_out;
t_out.tv_sec = 0;
t_out.tv_usec = 5000;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,&t_out,sizeof(t_out)) <0)
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
if (len < 0)
{
print("Timeout reached, recv failed: errno %d", errno);
}
else
{
rx_buffer[len] = 0;
if(strncmp(rx_buffer, payload, payload_len) == 0)
{
pack_nr++;
}
}
Now I'm encountering one problem.
Let's say B, for some reason, has a delay in responding. This causes something like that:
A sends something like "1xyz"
B has a delay ......
A times out and resends something like "1abc"
B's first response ("1xyz") reaches A, A decides that this is the wrong payload
B's second response ("1abc") reaches A too, but A is only executing one recv() and it's unseen for now
A resends something like "1uvw"
A reads "1abc" from recv() and again decides that this is the wrong payload
B's third response ("1uvw") reaches A, and so on and on
So what I'd like to do is to put a recv() in a loop, so that in step 5, A would first look for another response from B until the timeout is reached.
So is there clever way to do this? I was thinking about something like
send(sock, payload, strlen(payload), 0);
int flag = 0;
gettimeofday(&start_time, NULL);
while((tx_time < start_time + timeout) && flag = 0)
{
gettimeofday(&tx_time, NULL);
recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
if(rx_buffer is okay)
{
flag = 1;
}
wait_a_bit();
}
if(flag == 1) pack_nr++;
"... B is just responding with that same message it gets. A then checks if the response matches the sent packet ..."
You have a code problem and a terminology problem.
First, the terminology problem: Don't say "matches the sent packet". The data can be sent in one packet or ten packets, TCP doesn't care. You don't receive packets, you receive data that may be split or combined across packets as TCP wishes. It really helps (trust me) to be very precise in your use of words. If you mean a message, say "message". If you mean data, say "data". If you mean a datagram, say "datagram".
Unfortunately, your code problem is enormous. You want B to respond to A with the same message it received. That means you need a protocol that sends and receives messages. TCP is not a message protocol. So you need to implement a message protocol and write code that actually sends and receives messages.
If A write "foobar", B might receive "foobar" or it might first receive "foo" and then later "bar". If A writes "foo" then "bar", B might receive "foobar" or "f" and then "oobar". That's TCP. If you need a message protocol, you need to implement one.
First off, you are not checking for a timeout correctly. recv() could fail for any number of reasons. You need to check errno (or WSAGetLastError() on Windows) to find out WHY it failed. But even if it did actually fail due to timeout, TCP is a byte stream, the delayed data may still show up (especially since 5000 microseconds (0.005 seconds) is way too short a timeout to reliably use for TCP network traffic), but your sender would have moved on. The only sensible thing to do if a timeout occurs in TCP is to close the connection, since you don't know the state of the stream anymore.
In your situation, you are basically implementing an ECHO protocol. Whatever the sender sends just gets echoed back as-is. As such, if you send 4 bytes (which you are not verifying, BTW), then you should keep reading until 4 bytes are received, THEN compare them. If any failure occurs in that process, immediately close the connection.
int sendAll(int sock, void *data, int len)
{
char *ptr = (char*) data;
while (len > 0) {
int sent = send(sock, ptr, len, 0);
if (sent < 0) {
if (errno != EINTR)
return -1;
}
else {
ptr += sent;
len -= sent;
}
}
return 0;
}
int recvAll(int sock, void *data, int len)
{
char *ptr = (char*) data;
while (len > 0) {
int recvd = recv(sock, ptr, len, 0);
if (recvd < 0) {
if (errno != EINTR)
return -1;
}
else if (recvd == 0) {
return 0;
}
else {
ptr += recvd;
len -= recvd;
}
}
return 1;
}
...
int payload_len = strlen(payload);
if (sendAll(sock, payload, payload_len) < 0)
{
// error handling
close(sock);
}
else
{
struct timeval t_out;
t_out.tv_sec = 5;
t_out.tv_usec = 0;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &t_out, sizeof(t_out)) < 0)
{
// error handling
close(sock);
}
else
{
int res = recvAll(sock, rx_buffer, payload_len);
if (res < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
print("Timeout reached");
else
print("recv failed: errno %d", errno);
close(sock);
}
else if (res == 0)
{
print("disconnected");
close(sock);
}
else
{
if (memcmp(rx_buffer, payload, payload_len) == 0)
{
print("data matches");
pack_nr++;
}
else
print("data mismatch!");
}
}
}
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.