Recv() function messing up other parts of my code - c

I am trying to execute cat|grep using a client server set-up, works as follows: client sends word to search for using grep, server executes cat|grep, sends results to client but the recv() function seems to be messing up with my code.
What's the problem?
Adding the recv() function makes other parts of my code not working, every puts() works until puts("test5"); which is where my code is stuck in the execution, putting the recv() function as a comment makes the code run fine.
Till now I am not using the word I send from the client in any way so the problem must be with the receive function itself, it's not giving an error and when I print the content I send it works fine.
Here is the relevant client part:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include<errno.h>
#define PORT 8080
int main(int argc, char const *argv[])
{
int sock = 0, valread;
struct sockaddr_in serv_addr;
int buffer[1024];
char buffer2[1024]={0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0)
{
perror("Invalid address \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
perror("Connection Failed \n");
return -1;
}
int i, array[argc], countsize=0;
if(argc>=2)
{
for(i=1; i<argc; i++)
{
int number=atoi(argv[i]);
array[i-1]=number;
countsize++;
}
if(send(sock, array, countsize*sizeof(int), 0)<0)
{
printf("Error in send! %s\n", strerror(errno));
return -1;
}
if(argc>=2)
{
int i=0;
for(int i=0; i<argc; i++)
{
if(atoi(argv[i])==6)
{
puts("Please enter the name/word you want to search for in the history file: ");
char word[30];
fgets(word, 30, stdin);
if(send(sock, &word , 30, 0)<0)
printf("Error in send! %s\n", strerror(errno));
valread = read( sock , buffer2, 1024);
puts("The result cat|grep is:");
printf("%s\n", buffer2);
}
}
}
}
return 0;
}
Here is the server's main method:
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include<errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <sys/wait.h>
#include<time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <arpa/inet.h>
#define PORT 8080
void *catgrep(void *);
int main()
{
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer2[1024]={0};
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR , &opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
while (1)
{
if (listen(server_fd, 20) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,(socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
int arguments[10]={0};
int n = recv(new_socket, arguments ,1024*sizeof(int),0);
int j;
int argumentsize=n/sizeof(int);
for(j=0; j<argumentsize;j++)
{
if(arguments[j]==6)
{
pthread_t th5;
pthread_attr_t attr5;
pthread_attr_init(&attr5);
if(pthread_create(&th5,&attr5, catgrep,&new_socket)!=0)
{
printf("Error in pthread_create %s\n", strerror(errno));
return -1;
}
pthread_join(th5, NULL);
return -1;
}
}
close(new_socket);
}
close(server_fd);
return 1;
}
Here is my catgrep() method:
void *catgrep(void * param)
{
int *sock = (int*) param;
int new_sock = *sock;
int fd[2];
pipe(fd);
pid_t pid = fork();
char word[30];
recv(new_sock, word ,30, 0); //when I put this line code
starts messing up.
puts(word);
if(pid==0)
{
close(1);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
char *cat_args[] = {"/bin/cat", "GameData.txt", NULL};
if(execv(cat_args[0], cat_args)<0)
{
printf("Error in execv! %s\n", strerror(errno));
}
exit(0);
}
if(pid > 0)
{
close(0);
dup(fd[0]);
close (fd[1]);
close(fd[0]);
puts("test2");
FILE *fp2;
if ((fp2 = popen("grep -w tries", "r")) == NULL)
{
perror("popen failed");
return NULL;
}
puts("test3");
size_t str_size = 1024;
char *stringts2 = malloc(str_size);
if (!stringts2)
{
perror("stringts allocation failed");
return NULL;
}
puts("test4");
stringts2[0] = '\0';
char buf[128];
size_t n;
puts("test5"); //when I use the recv() program gets stuck here.
while ((n = fread(buf, 1, sizeof(buf) - 1, fp2)) > 0)
{
puts("test10");
buf[n] = '\0';
size_t capacity = str_size - strlen(stringts2) - 1;
while (n > capacity)
{
str_size *= 2;
stringts2 = realloc(stringts2, str_size);
if (!stringts2)
{
perror("stringts realloation failed");
return NULL;
}
capacity = str_size - strlen(stringts2) - 1;
}
strcat(stringts2, buf);
}
puts("test6");
if (pclose(fp2) != 0)
{
perror("pclose failed");
return NULL;
}
puts("test7");
if(send(new_sock, stringts2, 10000, 0)<0)
{
printf("Error in send! %s\n", strerror(errno));
}
}
return NULL;
}
Few notes:
I am aware that in this particular piece of code I am not using the word sent by the client, hence why some lines are as comments, I will implement this when my problem gets fixed.
I am using popen() as I want to return the output of catgrep().
I isolated the problem and not it's only happening when I include the recv() function.
The word I am sending is being printed when I use recv() so the function isn't causing errors but it's messing up other parts.
UPDATE:
As suggested by someone in the comments I changed the way I receive the word sent by my client, I am now using the following:
int count = 0;
int total = 0;
while ((count = recv(new_sock, &word[total], sizeof word - count, 0)) > 0)
{
total=total+count;
}
if (count==-1)
{
perror("error in recv()");
}
Still having the same problem and same output.

The basic problem is that you are confusing strings and byte streams -- they're not the same thing.
In your client, you send some data with:
char word[30];
fgets(word, 30, stdin);
if(send(sock, &word , 30, 0)<0)
This will read a line (including a newline) into the beginning of an on-stack buffer, and then send the entire buffer, including whatever garbage happens to be in it after the end of the string. You probably don't want the newline, maybe don't want the NUL terminator, and certainly don't want the garbage.
In addition, you don't check the return value of send for a short send -- in some (admittedly rare) situations, a send might not send all the data you request.
On the reading side you don't check the return value of recv to see how many bytes you got, which may be different from what you expect -- there's no guarentee that there will be 1:1 correspondence between send and recv calls on a connection. One send might get broken up and split across multiple recvs, and several sends might have their data combined and returned in one recv. So you always need to check the return value of recv to see how many bytes you actually got.

Related

C Program Attempting to send data, from serveride to client via tcp causing immediate crash

As the title stated - any atempts made by the serverside to send data back to the client result in an imediate crash (segmentation fault). This is a simple tcp chat app - and I am only looking to send strings bidirectionaly between client and server.
Server side below - the chat() function handles communication , after calling fgets , inputting my string , and attempting to send the data - I get an immediate (segmentation fault) and crash.
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <ifaddrs.h>
#define SA struct sockaddr
int chat(int sockfd, int port) {
for (;;) {
char *buffer_send;
char *buffer_recv;
recv(sockfd, buffer_recv, port , 0);
printf("%s", buffer_recv);
printf(":"); fgets(buffer_send, 512, stdin);
char* exit_func;
exit_func = strstr(buffer_send, "exit");
if (exit_func = strstr(buffer_send, "exit")) {
close(sockfd);
return 0;
} else {
send(sockfd, buffer_send, 512, 0);
}
}
}
int main () {
int server_socket, new_socket, c;
struct sockaddr_in socket_address, client;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
printf("socket creation failed! \n");
return 1;
} printf("socket created! \n");
socket_address.sin_addr.s_addr = inet_addr("192.168.0.10");
socket_address.sin_family = AF_INET;
socket_address.sin_port = (8003);
if( bind(server_socket,(struct sockaddr *)&socket_address , sizeof(socket_address)) < 0) {
printf("bind failed! \n");
return 1;
} printf("bind done! \n");
listen(server_socket , 3);
printf("Waiting for incoming connections...\n");
c = sizeof(struct sockaddr_in);
new_socket = accept(server_socket, (struct sockaddr *)&client, (socklen_t*)&c);
if (new_socket<0) {
printf("accept failed\n");
return 1;
} printf("connection accepted!\n");
chat(new_socket, socket_address.sin_port);
return 0;
}
however the same way of sending data on my client seems to work fine (without crashing while trying to send data):
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <ifaddrs.h>
int chat(int sockfd, int port) {
for (;;) {
char *buffer_send;
char *buffer_recv;
printf(":"); fgets(buffer_send, 512, stdin);
char* exit_func;
exit_func = strstr(buffer_send, "exit");
if (exit_func = strstr(buffer_send, "exit")) {
close(sockfd);
return 0;
} else {
send(sockfd, buffer_send, 512, 0);
}
recv(sockfd, buffer_recv, port , 0);
printf("%s", buffer_recv);
}
}
int main () {
int target_socket;
struct sockaddr_in target_server;
target_socket = socket(AF_INET, SOCK_STREAM, 0);
if (target_socket == -1) {
printf("socket creation failed!\n");
return 1;
} printf("socket created!\n");
target_server.sin_addr.s_addr = inet_addr("192.168.0.10");
target_server.sin_family = AF_INET;
target_server.sin_port = (8003);
if (connect(target_socket , (struct sockaddr *)&target_server , sizeof(target_server)) < 0) {
printf("connection failed!\n");
return 1;
} printf("connected!\n");
chat(target_socket, target_server.sin_port);
return 0;
}
You did not allocated the room for incoming messages, the same for the buffer you want to send. I expect to do some char buffer_send[512 + 1] = {}; and char buffer_recv[512 + 1] = {}; to make some place for the message content.
The + 1 is added for the extra safety, to not overwrite the NULL terminator when the message received is large enough to fill the entire allocated buffer.

C browser displays server socket response on webpage, but webpage keeps loading

I am new to learning C sockets, and I was able to successfully send an html <h1>hello world!</h1> as text/html content from a C socket server to the browser(client). However, even though the h1 tag displays correctly, I'm not sure why the page is stuck with a loading indicator. I tried adding a Content-Length property to indicate the length of my response, which works, but I was told that this shouldn't be necessary.
I think I am reading and writing properly to the socket, so I'm not sure what's hanging. Code:
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void servConn(int port)
{
int sd, new_sd;
struct sockaddr_in name, cli_name;
int sock_opt_val = 1;
int cli_len;
char data[256]; /* Our receive data buffer. */
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("(servConn): socket() error");
exit(-1);
}
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt_val, sizeof(sock_opt_val)) < 0)
{
perror("(servConn): Failed to set SO_REUSEADDR on INET socket");
exit(-1);
}
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sd, (struct sockaddr *)&name, sizeof(name)) < 0)
{
perror("(servConn): bind() error");
exit(-1);
}
listen(sd, 5);
for (;;)
{
cli_len = sizeof(cli_name);
new_sd = accept(sd, (struct sockaddr *)&cli_name, &cli_len);
printf("Assigning new socket descriptor: %d\n", new_sd);
if (new_sd < 0)
{
perror("(servConn): accept() error");
exit(-1);
}
if (fork() == 0)
{ /* Child process. */
close(sd);
char reply[] = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n";
char *requestType;
char *filename;
int status = 200;
char *strPter;
int index = 0;
char c;
while (1)
{
read(new_sd, &c, 1);
if (index > 254)
{
data[index] = '\0';
break;
}
if (c == '\n')
{
data[index] = '\0';
break;
}
else
{
data[index++] = c;
}
}
printf("read: %d bytes: %s\n", index, data);
requestType = strtok_r(data, " ", &strPter);
if (strcmp(requestType, "GET") != 0)
status = 501;
else
printf("request was GET\n");
filename = strtok_r(NULL, " ", &strPter);
printf("filename: %s\n", filename);
strtok_r(NULL, " ", &strPter);
char *response = "<h1>hello world!</h1>";
strcat(reply, response);
printf("\nresponse is (%d): \n%s\n\n", strlen(reply), reply);
send(new_sd, reply, strlen(reply), 0);
close(new_sd);
printf("closed connection!\n");
exit(0);
}
}
}
int main()
{
servConn(5050); /* Server port. */
return 0;
}
Here is my output, which seems to be in the correct HTTP format:
Here is the browser output, which is stuck in loading even though the content is displayed:
How do I correctly close the socket and stop the page from loading after sending hello world?
Before calling
close(new_sd);
you need
shutdown(new_sd, SHUT_RDWR);
It is this call that sends proper connection termination sequence. close doesn't, it just destroys the socket.

TCP write() prints char* to itself rather than to buffer only on first socket connection, but works OK on second connection onwards

On the first connection to the Server socket by a Client, the Server prints the output to itself, and leaves the Client blocked. But the second Client onwards receives the output from the Server.
The buffer that is expected to output to the Client is the server uptime.
Why does this happen, is there a way to immediately send the output to the Client and not block it?
To replicate, run 'ruptimeServer' on one terminal, and run 'ruptimeClient [localIPAddress] [serverIPAddress]'.
Below is an example of the I/O to the Server.
[user#linux-3 Lab2]$ ./ruptimeServer
Awaiting connection.
20:42:05 up 2 days, 17:38, 5 users, load average: 0.00, 0.01, 0.06 <-- buffer
Write Success
Awaiting connection.
Write Success
Awaiting connection.
Write Success
Awaiting connection.
^CCaught Ctrl+C, closing all connections.
Below is the I/O to the Client.
[user#linux-3 Lab2]$ ./ruptimeClient 127.0.0.1 10.24.87.66
Connection Success.
^C
[user#linux-3 Lab2]$ ./ruptimeClient 127.0.0.1 10.24.87.66
Connection Success.
10.24.87.66: 20:42:14 up 2 days, 17:38, 5 users, load average: 0.00, 0.01, 0.06
[user#linux-3 Lab2]$ ./ruptimeClient 127.0.0.1 10.24.87.66
Connection Success.
10.24.87.66: 20:42:18 up 2 days, 17:38, 5 users, load average: 0.00, 0.01, 0.06
Below is the code for Server.
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/select.h>
char* get_uptime();
void sig_handl(int sig_num);
int sersock, consock;
int main(int argc, char* argv[]){
struct sockaddr_in serveraddr, clientaddr;
struct sigaction sigIntHandler;
int on = 1;
sigIntHandler.sa_handler = sig_handl;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
char input_buffer[1024] = {0};
int len = sizeof(clientaddr);
char* IP_ADDRESS = "192.168.254.11";
if((sersock = socket(PF_INET, SOCK_STREAM, 0)) < 0){
perror("socket() error");
exit(1);
}
serveraddr.sin_family = PF_INET;
serveraddr.sin_port = htons(28189);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sersock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){
perror("bind() error");
exit(1);
}
if(listen(sersock, 10) < 0){
perror("listen() error");
exit(1);
}
char *output;
output = malloc(sizeof(char) * 1024);
signal(SIGINT, sig_handl);
while(1){
printf("Awaiting connection.\n");
if(consock = accept(sersock, (struct sockaddr *)&clientaddr, &len) < 0){
perror("accept() error");
exit(1);
}
output = get_uptime();
if(write(consock, output, 1024) < 0){
perror("write() error");
exit(1);
}
printf("Write Success\n");
close(consock);
}
close(sersock);
}
char * get_uptime(){ //returns uptime on server
char *buffer;
buffer = malloc(sizeof(char) * 1024);
FILE* file = popen("uptime", "r");
fgets(buffer, 100, file);
pclose(file);
return buffer;
}
void sig_handl(int sig_num){
printf("Caught Ctrl+C, closing all connections.\n");
close(consock);
close(sersock);
exit(0);
}
Below is the code for Client.
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
int main(int argc, char* argv[]){
if(argc != 3){
printf("Not enough arguments. To run, \"./ruptimeClient <localhost_IP> <server_IP>\"\n");
return 0;
}
struct sockaddr_in remoteaddr;
char input_buffer[100];
//input_buffer = malloc(sizeof(char) * 100);
int clisock;
char* SERVER_IP = argv[2];
if((clisock = socket(PF_INET, SOCK_STREAM, 0)) < 0){
perror("socket() error");
exit(1);
}
remoteaddr.sin_family = PF_INET;
remoteaddr.sin_port = htons(28189);
remoteaddr.sin_addr.s_addr = inet_addr(SERVER_IP);
if(connect(clisock, (struct sockaddr *)&remoteaddr, sizeof(remoteaddr)) < 0){
perror("Connection failed");
exit(1);
}
printf("Connection Success.\n");
if(read(clisock, input_buffer, 100) < 0){
perror("read() error");
exit(1);
}
//input_buffer = "test";
printf("%s: %s", SERVER_IP, input_buffer);
close(clisock);
return 0;
}
The problem is here:
if(consock = accept(sersock, (struct sockaddr *)&clientaddr, &len) < 0){
That's parsed as if you wrote this:
if(consock = (accept(sersock, (struct sockaddr *)&clientaddr, &len) < 0)){
As a result, consock ends up getting set to 0, which when interpreted as a file descriptor, means standard input. This then gets closed after the first client (the one that hangs), so it's available for subsequent clients, which then get reassigned that now-free FD number. To fix it, add explicit parentheses, like this:
if((consock = accept(sersock, (struct sockaddr *)&clientaddr, &len)) < 0){
the following proposed code:
is for the server
cleanly compiles
does not leak memory
incorporates the comments to the OPs question
and now, the proposed code for the server:
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/select.h>
#include <unistd.h>
#include <string.h>
char* get_uptime( char *buffer, int size );
void sig_handl(int sig_num);
int sersock, consock;
int main( void )
{
struct sockaddr_in serveraddr, clientaddr;
struct sigaction sigIntHandler;
//int on = 1;
sigIntHandler.sa_handler = sig_handl;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
//char input_buffer[1024] = {0};
socklen_t len = sizeof(clientaddr);
//char* IP_ADDRESS = "192.168.254.11";
if((sersock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket() error");
exit(1);
}
serveraddr.sin_family = PF_INET;
serveraddr.sin_port = htons(28189);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sersock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("bind() error");
exit(1);
}
if(listen(sersock, 10) < 0)
{
perror("listen() error");
exit(1);
}
char *output;
int size = 1024;
char buffer[ size ];
signal(SIGINT, sig_handl);
while(1)
{
printf("Awaiting connection.\n");
if( (consock = accept(sersock, (struct sockaddr *)&clientaddr, &len)) < 0)
{
perror("accept() error");
exit(1);
}
output = get_uptime( buffer, size );
if( write( consock, output, strlen( output ) ) < 0 )
{
perror("write() error");
exit(1);
}
printf("Write Success\n");
close(consock);
}
close(sersock);
}
char * get_uptime( char *buffer, int size )
{ //returns uptime on server
FILE* file = popen("uptime", "r");
fgets( buffer, size, file );
pclose(file);
return buffer;
}
void sig_handl(int sig_num)
{
(void)sig_num;
printf("Caught Ctrl+C, closing all connections.\n");
close(consock);
close(sersock);
exit(0);
}

C socket programming - write() from server writes to server instead of client

I'm working on a TCP client server program which is supposed to support several clients using threads.
The socket creation, connection, bind and accept work as expected since I receive no errors when running the code.
However whenever I try to read() from the server the code enters an infinite loop and nothing happens.
I tried writing from the server first and the write result was written to the server's terminal.
Client code:
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
int main(int argc, char *argv[]) {
//Get command line arguments
unsigned int port = atoi(argv[2]);
int length = atoi(argv[3]); //Number of bytes to read
char* buffer = malloc(length * sizeof(char)); //Buffer to hold data read from file
char* recvBuf = malloc(10 * sizeof(char)); // Buffer to hold response from server
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in serv_addr;
int sockfd = -1;
//int rv;
//char ip[100];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0) {
perror("getaddrinfo error\n");
exit(1);
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0)
continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0)
break;
close(sockfd);
sockfd = -1;
}
// inet_aton(ip, &h.sin_addr);
freeaddrinfo(servinfo);
//Open file for reading
FILE *fp;
fp = fopen(FILE_ADDR, "r");
if (fp == NULL) {
perror("Error in file open\n");
}
printf("file opened\n");
size_t numOfbytesRead = fread(buffer, sizeof(char), length, fp);
if (numOfbytesRead != length) {
perror("Error reading from file\n");
}
printf("Buffer is %s\n", buffer);
char* ptr;
unsigned int N = strtoul(argv[3],&ptr,10);
int convertedNum = htonl(N);
if (write(sockfd, &convertedNum, sizeof(unsigned int)) < 0) { //Send number of bytes
perror("error writing to socket");
}
if (write(sockfd, buffer, sizeof(buffer) < 0)) {//Send bytes read from file
perror("error writing to socket");
}
printf("send is done \n");
int bytes_read = read(sockfd, recvBuf, sizeof(recvBuf)); //Recieve response from server
if (bytes_read <= 0) {
perror("Error in recieving result from server\n");
}
unsigned int C = 0;
sprintf(recvBuf[0], "%d", C);
fclose(fp);
printf("# of printable characters: %u\n", C);
exit(0);
free(buffer);
free(recvBuf);
}
Server code:
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>
static volatile int keepRunning = 1;
int pcc_total[159];
void intHandler(int dummy) {
keepRunning = 0;
}
void *compute(void *socket_desc) {
int count = 0;
int sock = *(int*) socket_desc;
printf("now will allocate N \n");
int n=0;
if (write(sock, "hi", 2) < 0) { //Send number of bytes
perror("error writing to socket\n");
}
if (read(sock, &n, sizeof(unsigned int)) < 0) {
perror("Error reading from socket\n");
exit(1);
}
int N = ntohl(n);
printf("len is %d\n", N);
char* data = calloc(N, sizeof(char));
int len = read(sock, data, N);
printf("data is %s\n", data);
if (len < 0) {
perror("Error reading from socket\n");
exit(1);
}
for (int i = 0; i < len; i++) {
int tmp = 0;
sprintf(data[i], "%d", tmp);
if (tmp >= 32 & tmp <= 126) {
count++;
__sync_fetch_and_add(&pcc_total[tmp], 1);
}
}
char scount[100];
atoi(count);
write(sock, count, strlen(scount));
free(data);
pthread_exit(NULL);
close(sock);
exit(0);
}
int main(int argc, char *argv[]) {
unsigned int port = atoi(argv[1]);
signal(SIGINT, intHandler);
int socket_desc, client_sock, c, *new_sock;
struct sockaddr_in server, client;
c = sizeof(struct sockaddr_in);
socket_desc = socket( AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
perror("Could not create socket");
exit(1);
}
printf("socket created\n");
memset(&server, 0, c);
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(port);
if (0 != bind(socket_desc, (struct sockaddr*) &server, sizeof(server))) {
perror("\n Error : Bind Failed \n");
exit(1);
}
printf("bind created\n");
if (0 != listen(socket_desc, 10)) {
perror("\n Error : Listen Failed \n");
exit(1);
}
printf("listen created\n");
while (keepRunning) {
client_sock = accept(socket_desc, (struct sockaddr *) &client,
(socklen_t*) &c);
if (client_sock < 0) {
perror("\n Error : Accept Failed\n");
exit(1);
}
printf("accept created\n");
pthread_t tid;
new_sock = malloc(100*sizeof(int));
*new_sock = client_sock;
if ((pthread_create(&tid, NULL, compute, (void*) new_sock)) < 0) {
perror("could not create thread\n");
exit(1);
}
printf("thread created\n");
// close socket
close(client_sock);
free(new_sock);
pthread_join(tid, NULL);
}
exit(0);
}
I run the code with the following commands:
gcc -std=c99 -O3 -Wall -o pcc_server pcc_server.c -pthread
gcc -std=gnu99 -O3 -Wall -o pcc_client pcc_client.c
There are a number of problems with your code.
On the client side:
When calling fread(), you need to use "rb" instead of "r".
when calling printf() to output the file data that was actually read, you are not null-terminating buffer, or passing its length to printf(). You need to do so.
You are assigning the return value of htonl() to an int instead of an unsigned int.
when calling write() to send the buffer, you are using sizeof(buffer) when you should be using length or N instead (and why are you using two separate variables to hold the same command-line parameter value?). buffer is a pointer to memory allocated with malloc(), so sizeof(buffer) is the same as sizeof(void*), which is not what you want. Also, you are not even calling write() correctly, because your parenthesis are all wrong (they are correct on the previous write() call when sending convertedNum).
likewise, when calling read() to read the recvBuf, you are using sizeof(recvBuf) when you should be using 10 instead, sicne recvBuf is also a pointer to malloced memory.
you are not reading the "hi" greeting that the server sends to the client upon connection, so you lump in those bytes with the bytes of the following size value of the next message, and thus end up with a corrupted C value.
On the server side:
your compute thread routine sends a "hi" greeting to the client, but it does not use any kind of delimiter, like prefixing the greeting with its length, or terminating it with a line break or null character or other unique character, to separate it from any subsequent data. You should always delimit your messages in some manner.
you are closing the accepted socket and freeing the malloced new_sock as soon as you create a worker thread to handle that client. You are ripping away memory from behind the thread's proverbial back. The thread needs to be the one to close the socket and free the memory when it is done using them, not the accept loop.
The thread does attempt to close the socket (but not free the memory), but after it calls pthread_exit() first, which is wrong. pthread_exit() terminates the calling thread, so it needs to be the last thing that the thread calls (DO NOT call exit()!). In fact, don't even call pthread_exit() directly at all, just return from compute(), the pthreads library will then call pthread_exit() for you, passing it whatever void* value you choose to return.
your accept loop should not be calling pthread_join() at all. It blocks the calling thread until the specified thread terminates. That defeats the whole purpose of using threads to handle your clients, and prevents your server from accepting more than 1 client at a time. If you are going to use pthread_join() at all, it should be after the accept loop has ended, so you can wait for any worker threads that may still be running before exiting the app. But that also means keeping track of the pthread_t values that pthread_create() returns, which is more work.
With that said, try this code instead:
Client code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
char* readMsg(int sockfd, size_t *msgSize)
{
*msgSize = 0;
unsigned int length = 0;
int bytes_read = read(sockfd, &length, sizeof(length)); //Receive number of bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
return NULL;
}
length = ntohl(length);
char *buffer = malloc(length+1);
if (!buffer) {
perror("Error in allocating memory to receive message from server\n");
return NULL;
}
char *pbuf = buffer;
unsigned int buflen = length;
while (buflen > 0) {
bytes_read = read(sockfd, pbuf, buflen); // Receive bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
free(buffer);
return NULL;
}
pbuf += bytes_read;
buflen -= bytes_read;
}
*msgSize = length;
return buffer;
}
int sendMsg(int sockfd, char *msg, size_t msgSize)
{
unsigned int convertedNum = htonl(msgSize);
if (write(sockfd, &convertedNum, sizeof(convertedNum)) < 0) { //Send number of bytes
perror("error writing to socket");
return -1;
}
if (write(sockfd, msg, msgSize) < 0) { //Send bytes
perror("error writing to socket");
return -1;
}
return 0;
}
int main(int argc, char *argv[]) {
char* ptr;
//Get command line arguments
unsigned int port = atoi(argv[2]);
unsigned int length = strtoul(argv[3], &ptr, 10); //Number of bytes to read
char* buffer = malloc(length); //Buffer to hold data read from file
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in serv_addr;
int sockfd = -1;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0) {
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0)
continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0)
break;
close(sockfd);
sockfd = -1;
}
freeaddrinfo(servinfo);
if (sockfd == -1) {
perror("socket create/connect error\n");
return 1;
}
size_t msgSize;
char *msg = readMsg(sockfd, &msgSize);
if (!msg) {
close(sockfd);
return 1;
}
printf("%.*s\n", (int)msgSize, msg);
free(msg);
//Open file for reading
FILE *fp = fopen(FILE_ADDR, "rb");
if (fp == NULL) {
perror("Error in file open\n");
close(sockfd);
return 1;
}
printf("file opened\n");
if (fread(buffer, 1, length, fp) != length) {
perror("Error reading from file\n");
fclose(fp);
close(sockfd);
return 1;
}
fclose(fp);
printf("Buffer is %.*s\n", (int)length, buffer);
if (sendMsg(sockfd, buffer, length) != 0) {
free(buffer);
close(sockfd);
return 1;
}
free(buffer);
printf("send is done \n");
msg = readMsg(sockfd, &msgSize); // response from server
if (!msg) {
close(sockfd);
return 1;
}
printf("# of printable characters: %.*s\n", (int)msgSize, msg);
free(msg);
return 0;
}
Server code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>
static volatile int keepRunning = 1;
int pcc_total[159];
void intHandler(int dummy) {
keepRunning = 0;
}
char* readMsg(int sockfd, size_t *msgSize)
{
*msgSize = 0;
unsigned int length = 0;
int bytes_read = read(sockfd, &length, sizeof(length)); //Receive number of bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
return NULL;
}
length = ntohl(length);
char *buffer = malloc(length+1);
if (!buffer) {
perror("Error in allocating memory to receive message from server\n");
return NULL;
}
char *pbuf = buffer;
unsigned int buflen = length;
while (buflen > 0) {
bytes_read = read(sockfd, pbuf, buflen); // Receive bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
free(buffer);
return NULL;
}
pbuf += bytes_read;
buflen -= bytes_read;
}
*msgSize = length;
return buffer;
}
int sendMsg(int sockfd, char *msg, size_t msgSize)
{
unsigned int convertedNum = htonl(msgSize);
if (write(sockfd, &convertedNum, sizeof(convertedNum)) < 0) { //Send number of bytes
perror("error writing to socket");
return -1;
}
if (write(sockfd, msg, msgSize) < 0) { //Send bytes
perror("error writing to socket");
return -1;
}
return 0;
}
void *compute(void *socket_desc) {
int sock = * (int*) socket_desc;
free(socket_desc);
if (sendMsg(sock, "hi", 2) != 0) {
perror("error writing to socket\n");
close(sock);
return NULL;
}
size_t length = 0;
char *data = readMsg(sock, &length);
if (!msg) {
close(sock);
return NULL;
}
printf("len is %d\n", (int)length);
printf("data is %.*s\n", (int)length, data);
int count = 0;
for (size_t i = 0; i < length; i++) {
// ...
}
free(data);
char scount[20];
sprintf(scount, "%d", count);
sendMsg(sock, scount, strlen(scount));
close(sock);
return NULL;
}
int main(int argc, char *argv[]) {
unsigned int port = atoi(argv[1]);
signal(SIGINT, intHandler);
int socket_desc, client_sock, c, *new_sock;
struct sockaddr_in server, client;
socket_desc = socket( AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
perror("Could not create server socket");
return 1;
}
printf("server socket created\n");
memset(&server, 0, c);
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(port);
if (bind(socket_desc, (struct sockaddr*) &server, sizeof(server)) < 0) {
perror("\n Error : Bind Failed \n");
close(socket_desc);
return 1;
}
printf("bind created\n");
if (listen(socket_desc, 10) < 0) {
perror("\n Error : Listen Failed \n");
close(socket_desc);
return 1;
}
printf("listening\n");
while (keepRunning) {
c = sizeof(client);
client_sock = accept(socket_desc, (struct sockaddr *) &client,
(socklen_t*) &c);
if (client_sock < 0) {
perror("\n Error : Accept Failed\n");
continue;
}
printf("client accepted\n");
new_sock = malloc(sizeof(int));
if (!new_sock) {
perror("\n Error : Malloc Failed\n");
close(client_sock);
continue;
}
*new_sock = client_sock;
pthread_t tid;
if (pthread_create(&tid, NULL, &compute, new_sock) != 0) {
perror("\n Error : Thread Create Failed\n");
free(new_sock);
close(client_sock);
continue;
}
printf("thread created\n");
}
close(socket_desc);
return 0;
}
I think you should remove the two lines
close(client_sock);
free(new_sock);
in the server code because the newly created thread can't perform on those variables and memory area if it is freed up at such an early point. Can you try the code again without it?
Your server closes the connected socket and frees the memory in which you stored its file handle immediately after launching the thread to handle that connection. You're unlucky that the server only hangs as a result, but you have data races, so formally your program's behavior is undefined.
Since the server isn't going to do anything else until the thread has finished, you might as well move the close() and free() after the pthread_join(). Or, considering that you do join before creating any other threads, how about just calling compute() synchronously instead of creating a new thread for it to run in?

Processes not terminating

There are some strange things happening in my client-server application. Please, look at these simple fork client/server:
CLIENT:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/wait.h>
#define IP_SERVER "192.168.1.89"
#define PORT_SERVER 65000
#define BUFFERSIZE 1024
#define NUMFILES 3
double timeElapsed(struct timeval* before, struct timeval* after) {
return after->tv_sec - before->tv_sec + (double) (after->tv_usec - before->tv_usec)/1000000;
}
void getFile(char *request, struct sockaddr_in server) {
char buffer[1024];
int sockProc, res;
int file;
int sizeServ = sizeof(server);
int writeFile;
sockProc = socket(AF_INET, SOCK_STREAM, 0);
if (sockProc < 0) {
printf("Error on creating socket client\n");
perror("");
exit(1);
}
file = open(request, O_CREAT | O_WRONLY, S_IRWXU);
res = connect(sockProc, (struct sockaddr*)&server, (socklen_t)sizeServ);
if (res < 0) {
printf("Error on connecting to server!\n");
perror("");
exit(1);
}
res = send(sockProc, (void*)request, strlen(request), 0);
memset(buffer, 0, sizeof(buffer));
while((res = recv(sockProc, (void*)buffer, sizeof(buffer), 0)) > 0) {
write(file, (void*)buffer, strlen(buffer));
memset(buffer, 0, sizeof(buffer));
}
close(sockProc);
close(file);
return;
}
int main(int argc, char** argv) {
int sockCli, res, i;
struct sockaddr_in server;
int sizeServ = sizeof(server);
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
inet_pton(AF_INET, IP_SERVER, &server.sin_addr);
server.sin_port = htons(PORT_SERVER);
char files[NUMFILES][32];
char nameFile[32];
char command[32] = "rm *.txt";
system(command);
struct timeval begin;
struct timeval end;
pid_t processes[NUMFILES];
for(i = 0; i<NUMFILES; i++) {
memset(nameFile, 0, sizeof(nameFile));
printf("Inserisci nome file (con estensione) da ricevere:\n");
scanf("%s", nameFile);
strcpy(files[i], nameFile);
}
gettimeofday(&begin, NULL);
for(i=0; i<NUMFILES; i++) {
pid_t child = fork();
if(child == 0) {
getFile(files[i], server);
exit(0);
}
else {
processes[i] = child;
continue;
}
}
/*for(i=0; i<NUMFILES; i++) {
waitpid(processes[i], NULL, 0);
}*/
wait(NULL);
gettimeofday(&end, NULL);
printf("Time elapsed on TCP is %f seconds\n", timeElapsed(&begin, &end));
return 0;
}
and the SERVER:
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#define IP_SERVER "192.168.1.89"
#define PORT_SERVER 65000
#define BUFFERSIZE 1024
void execRequest(int* sockCli, struct sockaddr_in* client) {
char buffer[BUFFERSIZE];
char request[BUFFERSIZE];
int res;
memset(request, 0, sizeof(request));
res = recv(*sockCli, (void*)request, sizeof(request), 0);
if(res < 0) {
printf("Error on recv()\n");
perror("");
exit(1);
}
printf("Requested file %s\n", request);
char resource[32] = "files/";
strcat(resource, request);
int file = open(resource, O_RDONLY);
if (file < 0) {
printf("File %s does not exist\n", request);
exit(1);
}
memset(buffer, 0, sizeof(buffer));
while((res = read(file, (void*)buffer, sizeof(buffer))) > 0) {
send(*sockCli, (void*)buffer, strlen(buffer), 0);
memset(buffer, 0, sizeof(buffer));
}
close((*sockCli));
close(file);
free(sockCli);
free(client);
return;
}
int main(int argc, char** argv) {
int sockServ, i, res;
int *sockCli;
struct sockaddr_in server;
struct sockaddr_in* client;
sockServ = socket(AF_INET, SOCK_STREAM, 0);
if(sockServ < 0) {
printf("Error in creating socket\n");
perror("");
exit(1);
}
memset(&server, 0, sizeof(server));
server.sin_addr.s_addr = inet_addr(IP_SERVER);
server.sin_port = htons(PORT_SERVER);
server.sin_family = AF_INET;
int reuse = 1;
res = setsockopt(sockServ, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
if (res < 0) {
printf("setsockopt() REUSEADDR failed\n");
perror("");
exit(1);
}
res = bind(sockServ, (struct sockaddr*)&server, sizeof(server));
if (res < 0) {
printf("Error on bindind TCP server!\n");
perror("");
exit(1);
}
res = listen(sockServ, 5);
if (res < 0) {
printf("Error on listening TCP server!\n");
perror("");
exit(1);
}
while(1) {
sockCli = (int*)malloc(sizeof(int));
client = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
int sizeClient = sizeof(struct sockaddr_in);
*sockCli = accept(sockServ, (struct sockaddr*)client, &sizeClient);
if ((*sockCli) < 0) {
printf("accept() failed\n");
perror("");
continue;
}
printf("Connected to %s:%d\n", inet_ntoa(client->sin_addr), client->sin_port);
if( !fork() ) {
execRequest(sockCli, client);
exit(0);
}
else
continue;
}
return 0;
}
This is very strange. The processes created by the client don't terminate even if the server closes the sockets and so recv() should return 0 and let client processes exit from the loop. Moreover there's something strange about reading files:
the server simply reads files.txt but in doing this it includes the string ".txt" in the read characters and sends all this mixture to the client...why?
they are simple file mono character like
aaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaa
but the server reads and and sends:
aaaaaaaaaaaaaaaaaa.txt
aaaaaaaaaaaaaaaaaa
can I solve all this?
You can't use strlen(buffer), just because you're loading characters from a text file doesn't mean that buffer will be a valid string unless you take steps to ensure it is. And you don't; there's no termination since you can fill all of buffer with data from the file.
How many times must we play the broken record here on Stack Overflow? Don't cast malloc!
I chalk this error to failure to read the manual(s), to find out what header to include, what a string is (and hence what strlen/strcat/str*{anything}* expects of its input, what printf expects of arguments that correspond to a %s format specifier, etc.) and what read/recv produces.
res = recv(*sockCli, (void*)request, sizeof(request), 0);
if(res < 0) {
printf("Error on recv()\n");
perror("");
exit(1);
}
printf("Requested file %.*s\n", res, request); // NOTE the field width provided by 'res'
By the manual, examples such as res = read(file, (void*)buffer, sizeof(buffer)) supposedly store either an error or a length. The condition ensures that the send code will only execute when it's a length value, so why not use it as one? send(*sockCli, (void*)buffer, res, 0);?
The presense of these problems seems to indicate that your method of learning isn't working. Which book are you reading? Learning C without a book is a bit like learning which berries are poisonous without communication.

Resources