Related
I am trying to write data from struct into a a file. For instance the data is found in newpackets.data. I am trying to write that into a file, but keep getting segfault. I have looked at other questions on here, but could not find a solution. Adding my code below. How can i solve this issue ? Thank you
to run server do ./server 8500 1
to run client do ./client 127.0.0.1 8500 500 3 ex.txt exout.txt
ex.txt will need to be made and then write random words into it. for instance can be like 85 characters
//server side
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include "sys/un.h"
#define MAXLINE 32768
struct ackdata
{
int ack;
};
struct packetdata
{
int seqnumber;
int lenght;
char data[MAXLINE];
int type;
};
void dg_echo (int sockfd, struct sockaddr *pcliaddr, socklen_t clilen);
int
main (int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
int SERV_PORT = atoi (argv[1]); ///This will grab the port to listen to
int dropc = atoi (argv[2]);
if (SERV_PORT <= 1023)
{
fprintf (stderr, "Error: Port given can not be less than 1023\n");
exit (1);
}
if (SERV_PORT > 65535)
{
fprintf (stderr,
"Error: Port given can not be more than 65535 or less than 1024\n");
exit (1);
}
sockfd = socket (AF_INET, SOCK_DGRAM, 0);
bzero (&servaddr, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons (INADDR_ANY);
servaddr.sin_port = htons (SERV_PORT);
int bb = bind (sockfd, (struct sockaddr *) &servaddr, sizeof (servaddr));
if (bb < 0)
{
fprintf (stderr, "Binding failure");
}
dg_echo (sockfd, (struct sockaddr *) &cliaddr, sizeof (cliaddr));
close (sockfd);
return 0;
}
void
dg_echo (int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
int n;
FILE *outfile = NULL;
socklen_t len;
char mesg[MAXLINE];
struct packetdata newpackets;
char filename[MAXLINE];
recvfrom (sockfd, filename, MAXLINE, 0, pcliaddr, &len);
printf ("Name: %s", filename);
for (;;)
{
len = clilen;
//printf("HERe");
if ((outfile = fopen (filename, "a")) == NULL)
{
fprintf (stderr, "\nwriting File was not opened\n");
exit (1);
}
ssize_t rlen =recvfrom(sockfd, &newpackets, sizeof (newpackets), 0, pcliaddr, &len);
if (rlen ==-1){
printf ("failed recv");
return 1;
}
printf ("\nData: %s\n", newpackets.data);
//fprintf(outfile, "%d",newpackets.data );
fwrite(newpackets.data,rlen,1,outfile);
// sendto(sockfd, mesg, n, 0, pcliaddr, len);
}
}
Here is the client code:
//client side
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#define MAXLINE 4096
#define totaltries 3
struct packetdata data (int seqnumber, int lenght, char*data);
struct packetdata
{
int seqnumber;
int lenght;
char data[MAXLINE];
int type;
};
struct ackdata
{
int ack;
int type;
};
int main (int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
FILE *infile = NULL;
int countpackets;
float calc;
int tries = 0;
int base = 0;
int sequencenumber = 0;
int datasize =0;
int winsz = atoi (argv[4]); //this is for the window size
int mtu = atoi (argv[3]); //for mtu
int SERV_PORT = atoi (argv[2]); // the port to listen to
char sendline[MAXLINE], recvline[MAXLINE];
struct packetdata forpackets;
if (mtu <= 0)
{
fprintf (stderr, "MTU Error: mtu can not be less than or equal to 0\n");
exit (1);
}
//you shouldnt use any ports from 0-1023
//1024-65535 are fines
if (SERV_PORT <= 1023)
{
fprintf (stderr, "Port Error: Port given can not be less than 1023\n");
exit (1);
}
if (SERV_PORT > 65535)
{
fprintf (stderr,
"Port Error: Port given can not be more than 65535 or less than 1024\n");
exit (1);
}
bzero (&servaddr, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons (SERV_PORT);
if (inet_pton (AF_INET, argv[1], &servaddr.sin_addr) <= 0)
{
fprintf (stderr, "Inet Pton error");
return 1;
}
if ((sockfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
{
fprintf (stderr, "\nsocket error\n");
return 1;
}
if ((infile = fopen (argv[5], "r")) == NULL)
{
fprintf (stderr, "\nReading File was not opened\n");
return 1;
}
//file name sending
char filename[MAXLINE];
strcpy(filename, argv[6]);
//printf("Filename: %c", filename);
sendto(sockfd, filename, MAXLINE, 0, (struct sockaddr * ) & servaddr, sizeof(servaddr));
//this is for calculations
fseek (infile, 0, SEEK_END);
countpackets = ftell (infile);
fseek (infile, 0, SEEK_SET);
// printf("countpackets %d\n",countpackets);
// printf("\nMTU %d\n",mtu);
calc = countpackets / mtu; //how many buffer is
if (countpackets % mtu)
{
calc++;
}
printf("\nCalc %f\n",calc);
char fordata[mtu];
//while(1){
while (calc>=sequencenumber && sequencenumber-base<=winsz){
fread(forpackets.data, sizeof(recvline), 1, infile);
//printf("%s", forpackets.data);
// strcpy(fordata, forpackets.data);
strncpy(fordata,(sequencenumber*mtu+forpackets.data),mtu);
printf("%s", fordata);
forpackets = data(sequencenumber,datasize, fordata );
printf("\nSending Packet: %d\n", sequencenumber);
if (sendto(sockfd, &forpackets, sizeof(forpackets), 0, (struct sockaddr * ) & servaddr, sizeof(servaddr)) == -1) {
printf("failed sending");
return 1;
}else{
sequencenumber++;
}
}
// }
close(sockfd);
fclose(infile);
}
struct packetdata data (int seqnumber, int lenght, char*data){
struct packetdata dataa;
dataa. seqnumber = seqnumber;
dataa. lenght = lenght;
dataa. type = 1;
memset(dataa.data, 0, MAXLINE);
strcpy(dataa.data, data);
return dataa;
}
Okay, just to recap the comments:
outfile was missing an fopen call, so the server fwrite would segfault
After the fix, the output file would get extraneous zeros because the length given to fwrite was the whole size rather than the returned value of recvfrom
Actually, the correct length is the length value inside the packet
You're on the right track. The struct you devised has the right things in it (e.g. sequence number, type, length, data field).
I've refactored the server and client code.
An additional issue is that the server didn't know when to stop writing the output file. There are several ways to do this. But, the one I chose was for the client to send an extra data packet with a length field of 0.
I've adjusted both client and server to do this.
Here is the refactored server code:
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/un.h>
#define MAXLINE 32768
struct ackdata {
int ack;
};
struct packetdata {
int seqnumber;
int length;
char data[MAXLINE];
int type;
};
void dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen);
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
#if 1
if (argc < 2) {
printf("usage: serv_port\n");
exit(1);
}
#endif
int SERV_PORT = atoi(argv[1]); // /This will grab the port to listen to
#if 0
int dropc = atoi(argv[2]);
#endif
if (SERV_PORT <= 1023) {
fprintf(stderr, "Error: Port given can not be less than 1023\n");
exit(1);
}
if (SERV_PORT > 65535) {
fprintf(stderr, "Error: Port given can not be more than 65535 or less than 1024\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
int bb = bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (bb < 0) {
fprintf(stderr, "Binding failure");
}
dg_echo(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));
close(sockfd);
return 0;
}
void
dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
int n;
FILE *outfile = NULL;
socklen_t len;
char mesg[MAXLINE];
struct packetdata newpackets;
char filename[MAXLINE];
recvfrom(sockfd, filename, MAXLINE, 0, pcliaddr, &len);
#if 0
printf("Name: %s", filename);
#else
printf("Name: %s\n", filename);
outfile = fopen(filename,"w");
#endif
for (;;) {
len = clilen;
// printf("HERe");
if (recvfrom(sockfd, &newpackets, sizeof(newpackets), 0, pcliaddr, &len) == -1) {
printf("failed recv");
break;
}
printf("DEBUG: seqnumber=%d type=%d length=%d\n",
newpackets.seqnumber,newpackets.type,newpackets.length);
// end of file
if (newpackets.length == 0)
break;
//printf("\nData: %s\n", newpackets.data);
fwrite(newpackets.data, newpackets.length, 1, outfile);
// sendto(sockfd, mesg, n, 0, pcliaddr, len);
}
if (outfile != NULL)
fclose(outfile);
}
And, here is the refactored client code. I cleaned it up a bit.
I changed the code to do the fread directly into the packet struct's data field.
I changed the data function to take a pointer arg to the packet rather than returning one. This eliminates some unnecessary copying
I added an extra zero length data packet to indicate EOF
I changed the primary data read/write loop to look at the returned length from fread rather than (pre)calculate the number of packets.
//client side
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#define MAXLINE 4096
#define totaltries 3
struct packetdata {
int seqnumber;
int length;
char data[MAXLINE];
int type;
};
struct ackdata {
int ack;
int type;
};
void
data(struct packetdata *pkt,int seqnumber, int length, char *data)
{
pkt->seqnumber = seqnumber;
pkt->length = length;
pkt->type = 1;
if ((length > 0) && (data != NULL))
memcpy(pkt->data, data, length);
}
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
FILE *infile = NULL;
int countpackets;
float calc;
int tries = 0;
int base = 0;
int sequencenumber = 0;
int datasize = 0;
int winsz = atoi(argv[4]); // this is for the window size
int mtu = atoi(argv[3]); // for mtu
int SERV_PORT = atoi(argv[2]); // the port to listen to
char sendline[MAXLINE],
recvline[MAXLINE];
struct packetdata forpackets;
if (mtu <= 0) {
fprintf(stderr, "MTU Error: mtu can not be less than or equal to 0\n");
exit(1);
}
// you shouldnt use any ports from 0-1023
// 1024-65535 are fines
if (SERV_PORT <= 1023) {
fprintf(stderr, "Port Error: Port given can not be less than 1023\n");
exit(1);
}
if (SERV_PORT > 65535) {
fprintf(stderr, "Port Error: Port given can not be more than 65535 or less than 1024\n");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
fprintf(stderr, "Inet Pton error");
return 1;
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
fprintf(stderr, "\nsocket error\n");
return 1;
}
if ((infile = fopen(argv[5], "r")) == NULL) {
fprintf(stderr, "\nReading File was not opened\n");
return 1;
}
// file name sending
char filename[MAXLINE];
strcpy(filename, argv[6]);
// printf("Filename: %c", filename);
sendto(sockfd, filename, MAXLINE, 0, (struct sockaddr *) &servaddr,
sizeof(servaddr));
// this is for calculations
fseek(infile, 0, SEEK_END);
countpackets = ftell(infile);
fseek(infile, 0, SEEK_SET);
// printf("countpackets %d\n",countpackets);
// printf("\nMTU %d\n",mtu);
calc = countpackets / mtu; // how many buffer is
if (countpackets % mtu) {
calc++;
}
printf("\nCalc %f\n", calc);
// while(1){
#if 0
while (calc >= sequencenumber && sequencenumber - base <= winsz) {
#else
while (1) {
#endif
ssize_t rlen = fread(forpackets.data, 1, sizeof(forpackets.data),
infile);
data(&forpackets,sequencenumber, rlen, NULL);
printf("\nSending Packet: %d rlen=%d\n", sequencenumber,rlen);
if (sendto(sockfd, &forpackets, sizeof(forpackets), 0,
(struct sockaddr *) &servaddr, sizeof(servaddr)) == -1) {
printf("failed sending");
return 1;
}
else {
sequencenumber++;
}
if (rlen == 0)
break;
}
// }
close(sockfd);
fclose(infile);
return 0;
}
Note that the client and server are "one-shots". That is, the server exits after receiving a given file.
Also, one change I didn't make was to send/receive the filename within a packet (with a different type field).
I'd change the programs to send only packet structs.
Then, the server could have an outer loop, to service new/different requests (e.g. it could receive a file, then send a file, etc.)
With various type fields (e.g.):
send file to server (data is filename)
send file to client (data is filename)
file data (sent by client and/or server and length of 0 indicates EOF)
close connection (sent by client to server)
This may be a bit of overkill for the scope of your project ...
The server could look at the IP address from recvfrom and keep some state in a "connection" struct, based on a match to the client IP address. For example, the connection struct could hold the outfile stream (instead of a single instance).
Then, the server could handle multiple/many client streams, interspersed on the same/single socket descriptor.
UPDATE:
Hey craig. TYSM. But is there a way, where the server is not one shot go? –
juststruggle
It starts by adding my suggestion about sending the filename inside a packet with a type.
And, having the server loop do a switch (pkt.type).
I've added the ruminants of that in the code below. The client is very similar in function to before. The server will now loop after file transfer, waiting for other packets.
Note that there is a bunch of similar code between server and client. I moved some of that to a common.c file. Note that this is crude. I'd like to have a common.h for definitions and function prototypes and a common.c for common functions. But, IRL, I am being "summoned" :-) so I don't have time to fix that.
I moved the struct definition to that file. In the process, I notice that the size of the data element is different between client and server. That's a bug because type is at the end and would be elided/truncated because of the differing lengths of data
Here's common.c:
// common.c -- common code
#define MAXLINE 1024
#define totaltries 3
#ifdef DEBUG
#define dbgprt(_fmt...) printf(_fmt)
#else
#define dbgprt(_fmt...) do { } while (0)
#endif
struct packetdata {
int type;
int seqnumber;
int length;
char data[MAXLINE];
};
struct ackdata {
int ack;
int type;
};
enum {
TYPE_TOSVR,
TYPE_TOCLI,
TYPE_DATA
};
void
pktprep(struct packetdata *pkt,int type,int seqnumber, int length, char *data)
{
pkt->type = type;
pkt->seqnumber = seqnumber;
pkt->length = length;
if ((length > 0) && (data != NULL))
memcpy(pkt->data, data, length);
dbgprt("pktprep: SENDPKT seq=%d type=%d length=%d\n",
seqnumber,type,length);
}
Here is server.c:
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/un.h>
#include "common.c"
void dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen);
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
#if 1
if (argc < 2) {
printf("usage: serv_port\n");
exit(1);
}
#endif
int SERV_PORT = atoi(argv[1]); // /This will grab the port to listen to
#if 0
int dropc = atoi(argv[2]);
#endif
if (SERV_PORT <= 1023) {
fprintf(stderr, "Error: Port given can not be less than 1023\n");
exit(1);
}
if (SERV_PORT > 65535) {
fprintf(stderr, "Error: Port given can not be more than 65535 or less than 1024\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
int bb = bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (bb < 0) {
fprintf(stderr, "Binding failure");
}
dg_echo(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));
close(sockfd);
return 0;
}
void
dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
int n;
FILE *outfile = NULL;
socklen_t len;
char mesg[MAXLINE];
struct packetdata pkt;
char filename[MAXLINE];
for (;;) {
len = clilen;
// printf("HERe");
if (recvfrom(sockfd, &pkt, sizeof(pkt), 0, pcliaddr, &len) == -1) {
printf("failed recv");
break;
}
dbgprt("DEBUG: seqnumber=%d type=%d length=%d\n",
pkt.seqnumber,pkt.type,pkt.length);
switch (pkt.type) {
case TYPE_TOSVR:
dbgprt("TOSVR: %s\n", pkt.data);
if (outfile == NULL)
outfile = fopen(pkt.data,"w");
break;
case TYPE_DATA:
// end of file
if (pkt.length == 0) {
if (outfile != NULL) {
fclose(outfile);
outfile = NULL;
}
break;
}
//dbgprt("\nData: %s\n", pkt.data);
if (outfile != NULL)
fwrite(pkt.data, pkt.length, 1, outfile);
break;
}
}
if (outfile != NULL)
fclose(outfile);
}
And, here is client.c:
//client side
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#include "common.c"
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
FILE *infile = NULL;
int countpackets;
float calc;
int tries = 0;
int base = 0;
int sequencenumber = 0;
int datasize = 0;
int winsz = atoi(argv[4]); // this is for the window size
int mtu = atoi(argv[3]); // for mtu
int SERV_PORT = atoi(argv[2]); // the port to listen to
char sendline[MAXLINE],
recvline[MAXLINE];
struct packetdata pkt;
if (mtu <= 0) {
fprintf(stderr, "MTU Error: mtu can not be less than or equal to 0\n");
exit(1);
}
// you shouldnt use any ports from 0-1023
// 1024-65535 are fines
if (SERV_PORT <= 1023) {
fprintf(stderr, "Port Error: Port given can not be less than 1023\n");
exit(1);
}
if (SERV_PORT > 65535) {
fprintf(stderr, "Port Error: Port given can not be more than 65535 or less than 1024\n");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
fprintf(stderr, "Inet Pton error");
return 1;
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
fprintf(stderr, "\nsocket error\n");
return 1;
}
if ((infile = fopen(argv[5], "r")) == NULL) {
fprintf(stderr, "\nReading File was not opened\n");
return 1;
}
// file name sending
char filename[MAXLINE];
pktprep(&pkt,TYPE_TOSVR,sequencenumber++,strlen(argv[6]) + 1,argv[6]);
// printf("Filename: %c", filename);
#if 0
sendto(sockfd, filename, MAXLINE, 0, (struct sockaddr *) &servaddr,
sizeof(servaddr));
#else
sendto(sockfd, &pkt, sizeof(pkt), 0,
(struct sockaddr *) &servaddr, sizeof(servaddr));
#endif
// this is for calculations
fseek(infile, 0, SEEK_END);
countpackets = ftell(infile);
fseek(infile, 0, SEEK_SET);
// printf("countpackets %d\n",countpackets);
// printf("\nMTU %d\n",mtu);
calc = countpackets / mtu; // how many buffer is
if (countpackets % mtu) {
calc++;
}
dbgprt("\nCalc %f\n", calc);
// while(1){
#if 0
while (calc >= sequencenumber && sequencenumber - base <= winsz) {
#else
while (1) {
#endif
ssize_t rlen = fread(pkt.data, 1, sizeof(pkt.data),
infile);
pktprep(&pkt,TYPE_DATA,sequencenumber, rlen, NULL);
if (sendto(sockfd, &pkt, sizeof(pkt), 0,
(struct sockaddr *) &servaddr, sizeof(servaddr)) == -1) {
printf("failed sending");
return 1;
}
else {
sequencenumber++;
}
if (rlen == 0)
break;
}
// }
close(sockfd);
fclose(infile);
return 0;
}
Compile server.c and client.c with -DDEBUG to start.
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.
I'm using TCP_REPAIR to create a pair of already-connected sockets that talk to each other without actually going through the TCP connect flow.
I've tried to use the ideas in LWN for connection repair and their soccr library, but unfortunately, no matter what I do, I always get an ECONNRESET error the moment I try to write data to any of the sockets.
Here's an example of what I've tried:
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/socket.h>
#include <netinet/tcp.h>
#include <string.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <errno.h>
struct tcp {
char *addr;
uint32_t port;
};
int createSocket(struct tcp src, struct tcp dst) {
int sk, yes = 1;
struct sockaddr_in addr;
sk = socket(AF_INET, SOCK_STREAM, 0);
if (sk < 0) {
printf("Cannot create socket\n");
return -1;
}
if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &yes, sizeof(yes))) {
printf("Cannot set TCP_REPAIR\n");
return -1;
}
if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
printf("Cannot set SO_REUSEADDR\n");
return -1;
}
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(src.port);
if (inet_pton(AF_INET, src.addr, &(addr.sin_addr)) < 0) {
printf("Error converting source host:port to binary\n");
return -1;
}
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr))) {
printf("Cannot bind to source port\n");
return -1;
}
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(dst.port);
if (inet_pton(AF_INET, dst.addr, &(addr.sin_addr)) < 0) {
printf("Error converting destination host:port to binary\n");
return -1;
}
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr))) {
printf("Cannot bind to remote port\n");
return -1;
}
int no = 0;
if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &no, sizeof(no))) {
printf("Cannot set TCP_REPAIR\n");
return -1;
}
return sk;
}
int main(int argc, char **argv) {
struct tcp tcp[2] = {
{"127.0.0.1", 17000},
{"127.0.0.1", 34000}
};
int source_socket = createSocket(tcp[0], tcp[1]);
printf("Source socket created: %d \n",source_socket);
int dest_socket = createSocket(tcp[1], tcp[0]);
printf("Destination socket created: %d \n",dest_socket);
if (source_socket < 0 || dest_socket < 0)
return -1;
char* message = "Hello world";
ssize_t written = write(dest_socket, message, strlen(message)); // ECONNRESET error
if (written < 0) {
printf("Error writing to socket %d\n",errno);
return -1;
}
printf("%ld bytes written\n",written);
char buffer[1024];
ssize_t bytesRead = read(dest_socket, buffer, sizeof(buffer)-1);
printf("Bytes read %ld\n",bytesRead);
buffer[bytesRead] = 0;
printf("Message read: %s\n",buffer);
printf("Finished %d %d \n",source_socket,dest_socket);
return 0;
}
And the output is
Source socket created: 3
Destination socket created: 4
Error writing to socket 104
I get the same result (Connection reset by peer error) if I use the soccr library:
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/socket.h>
#include <netinet/tcp.h>
#include <string.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <errno.h>
#include "./soccr.h"
struct tcp {
char *addr;
uint32_t port;
uint32_t seq;
uint16_t mss_clamp;
uint16_t wscale;
};
int createSocket(struct tcp src, struct tcp dst) {
int sk, yes = 1;
union libsoccr_addr src_addr, dst_addr;
struct libsoccr_sk_data data = {};
struct libsoccr_sk *so;
data.state = TCP_ESTABLISHED;
data.inq_seq = dst.seq;
data.outq_seq = src.seq;
sk = socket(AF_INET, SOCK_STREAM, 0);
if (sk < 0) {
printf("Cannot create socket. Errno %d\n",errno);
return -1;
}
so = libsoccr_pause(sk);
if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
printf("Cannot set socket options. Errno %d\n",errno);
return -1;
}
src_addr.v4.sin_family = AF_INET;
src_addr.v4.sin_port = htons(src.port);
if (inet_pton(AF_INET, src.addr, &src_addr.v4.sin_addr) != 1) {
printf("Cannot convert source host:port to binary. Errno %d\n",errno);
return -1;
}
dst_addr.v4.sin_family = AF_INET;
dst_addr.v4.sin_port = htons(dst.port);
if (inet_pton(AF_INET, dst.addr, &(dst_addr.v4.sin_addr)) != 1) {
printf("Cannot convert destination host:port to binary. Errno %d\n",errno);
return -1;
}
libsoccr_set_addr(so, 1, &src_addr, 0);
libsoccr_set_addr(so, 0, &dst_addr, 0);
data.snd_wscale = src.wscale;
data.rcv_wscale = dst.wscale;
data.mss_clamp = src.mss_clamp;
data.opt_mask = TCPI_OPT_WSCALE | TCPOPT_MAXSEG;
if (libsoccr_restore(so, &data, sizeof(data)) < 0) {
printf("Cannot apply restore options. Errno %d\n",errno);
return -1;
}
libsoccr_resume(so);
return sk;
}
int main(int argc, char **argv) {
struct tcp tcp[2] = {
{"127.0.0.1", 17000, 5000000, 1460, 7},
{"127.0.0.1", 34000, 6000000, 1460, 7}
};
int source_socket = createSocket(tcp[0], tcp[1]);
printf("Source socket created: %d \n",source_socket);
int dest_socket = createSocket(tcp[1], tcp[0]);
printf("Destination socket created: %d \n",dest_socket);
if (source_socket < 0 || dest_socket < 0)
return -1;
char* message = "Hello world";
ssize_t written = write(source_socket, message, strlen(message));
if (written < 0) {
printf("Error writing to socket %d\n",errno);
return -1;
}
printf("%ld bytes written\n",written);
char buffer[1024];
ssize_t bytesRead = read(dest_socket, buffer, sizeof(buffer)-1);
printf("Bytes read %ld\n",bytesRead);
buffer[bytesRead] = 0;
printf("Message read: %s\n",buffer);
printf("Finished\n");
return 0;
}
Any ideas about what I might be doing wrong here?
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);
}
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?