I created a simple tftp server that only handles read requests (RRQ). Everything was working fine until I started to make a multi-threaded version of the server. In the application, I simply receive requests in the main thread and I then forward the request to a new thread that does the packet analysis. Therefore, I need to forward the socket, the received packet and the sockaddr_in struct that contains the client information to the thread. With that said, I created a struct that holds all of these and forward them to the pthread.
I get two identical error messages, one in the main and the other in the connection handler. The problems seems to be in the referencing these variables in the struct and retrieving them in the thread. It seems the problem is in the following statements: in connection_handler(): buffer = cthread->buffer; and in the main(): clientT.buffer = buffer;
Here's the code, I've written so far...
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#define TIMEOUT 5000
#define RETRIES 3
void *connection_handler(void *);
struct clientThread
{
int clientSock;
char opcode;
char buffer[1024];
struct sockaddr_in client;
};
int main()
{
char buffer[1024];
int udpSocket, client_socket, nBytes;
struct sockaddr_in serverAddr, client;
socklen_t addr_size;
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(69);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
while(1)
{
memset(buffer, 0, 1024);
nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &addr_size);
// Creating a thread and passing the packet received, the socket and the sockaddr_in struct...
pthread_t client_thread;
struct clientThread clientT;
strcpy(clientT.buffer,buffer);
clientT.clientSock = udpSocket;
clientT.client = client;
pthread_create(&client_thread, NULL, connection_handler, &clientT);
}
return 0;
}
void* connection_handler (void *clientThreaded)
{
char buffer[1024], filename[200], mode[20], *bufindex, opcode;
struct clientThread *cthread = clientThreaded;
int udpSocket = cthread->clientSock;
strcpy(buffer, cthread->buffer);
//opcode = cthread->opcode;
struct sockaddr_in client = cthread->client;
bufindex = buffer;
bufindex++;
// Extracting the opcode from the packet...
opcode = *bufindex++;
// Extracting the filename from the packet...
strncpy(filename, bufindex, sizeof(filename)-1);
bufindex += strlen(filename) + 1;
// Extracting the mode from the packet...
strncpy(mode, bufindex, sizeof(mode)-1);
// If we received an RRQ...
if (opcode == 1)
{
puts("Received RRQ Request");
struct timeval tv;
tv.tv_sec = 5;
char path[70] = "tmp/";
char filebuf [1024];
int count = 0, i; // Number of data portions sent
unsigned char packetbuf[1024];
char recvbuf[1024];
socklen_t recv_size;
socklen_t optionslength = sizeof(tv);
setsockopt(udpSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, optionslength);
FILE *fp;
char fullpath[200];
strcpy(fullpath, path);
strncat(fullpath, filename, sizeof(fullpath) -1);
fp = fopen(fullpath, "r");
if (fp == NULL)
perror("");
memset(filebuf, 0, sizeof(filebuf));
while (1)
{
int acked = 0;
int ssize = fread(filebuf, 1 , 512, fp);
count++;
sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
memcpy((char *) packetbuf + 4, filebuf, ssize);
packetbuf[2] = (count & 0xFF00) >> 8;
packetbuf[3] = (count & 0x00FF);
int len = 4 + ssize;
memset(recvbuf, 0, 1024);
printf("\nSending Packet #%i", count);
sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
for (i=0; i<RETRIES; i++)
{
int result = recvfrom(udpSocket, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);
if ((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
{
sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
printf("\nRetransmitting Packet #%i", count);
}
else if (result == -1)
{
// Handle Error
}
else
{
acked++;
printf("\nReceived ACK For Data Packet #%i", count);
break;
}
}
if (acked!=1)
{
puts("\nGave Up Transmission After 3 Retries");
break;
}
if (ssize != 512)
break;
}
}
return 0;
}
Thanks in advance :)
In the main loop, the variable clientT is local inside that loop, once the loop iterates the variable will go out of scope and any pointer to it will become invalid. Dereferencing such a pointer will lead to undefined behavior.
Instead what you should to is to dynamically allocate the structure using malloc, and pass that pointer instead. Don't forget to free the structure once you're done with it in the thread.
the current posted code, 7pm PDT,
causes the compiler to emit several warnings
(all of which need to be fixed)
plus some errors.
Errors like: 'buffer = cthread->buffer;'
is copying the address of 'cthread->buffer' to the address of the array 'buffer'.
That probably is not what is wanted.
suggest something similar to: strcpy(buffer, cthread->buffer);
#include <time.h> is missing
so this line: 'struct timeval tv;' is referencing an undefined struct.
The compiler needs to be run with all warnings enabled.
then fix the warnings and the errors.
as a minimum, for gcc, use the parameters:
-Wall -Wextra -Wshadow -pedantic
There are plenty of other error/warning messages that can be enabled
but the above list will catch ~99percent of all errors/warnings
Some googling should find info on how to fix the current errors and warnings.
(although to me, the error/warning messages
make it very clear as to the root cause of the problem.
however, I have been programming for 40 some years)
Each compiler message indicates:
1) which line in the current translation unit (file)
2) and what is wrong with that line.
Related
I got a segmentation fault problem when I write a client-server project in UDP. It happens on server side, when I receive a packet from client and going to send an ACK back. I tried to search the solutions and got UDP Server giving Segmentation Fault and C concurrent UDP socket , weird segmentation fault, but seems both of those are not the answer I'm looking for.
Here is my server side code
#include <ctype.h> /* for toupper */
#include <stdio.h> /* for standard I/O functions */
#include <stdlib.h> /* for exit */
#include <string.h> /* for memset */
#include <sys/socket.h> /* for socket, sendto, and recvfrom */
#include <netinet/in.h> /* for sockaddr_in */
#include <unistd.h> /* for close */
#define STRING_SIZE 1024
#define SERV_UDP_PORT 12311
int main(void) {
int sock_server;
struct sockaddr_in server_addr;
unsigned short server_port;
struct sockaddr_in client_addr;
unsigned int client_addr_len;
char sentence[STRING_SIZE];
char modifiedSentence[STRING_SIZE];
unsigned int msg_len;
int bytes_sent, bytes_recd;
unsigned int i;
struct Pkt
{
short length;
short seqnum;
char databytes[80];
};
struct Pkt* pkt;
int j ; //for loop
int seq;
short num_of_bytes;
//char ack_num[2];
struct Ack
{
short ack_num;
};
struct Ack* ack;
/* open a socket */
if ((sock_server = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("Server: can't open datagram socket\n");
exit(1);
}
/* initialize server address information */
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl (INADDR_ANY);
server_port = SERV_UDP_PORT;
server_addr.sin_port = htons(server_port);
/* bind the socket to the local server port */
if (bind(sock_server, (struct sockaddr *) &server_addr,
sizeof (server_addr)) < 0) {
perror("Server: can't bind to local address\n");
close(sock_server);
exit(1);
}
/* wait for incoming messages in an indefinite loop */
printf("Waiting for incoming messages on port %hu\n\n",
server_port);
client_addr_len = sizeof (client_addr);
for (;;) {
bytes_recd = recvfrom(sock_server, pkt, sizeof(*pkt), 0, (struct sockaddr *) &client_addr, &client_addr_len);
ack->ack_num = pkt->seqnum;
printf("%02d\n", ack->ack_num);
num_of_bytes = pkt->length;
printf("The sequence number is: %d\n", ack->ack_num);
printf("Received Sentence is: %s\n with length %d\n\n", pkt->databytes, num_of_bytes);
msg_len = 3;
/* send message */
bytes_sent = sendto(sock_server, (struct Ack*)&ack, msg_len, 0, (struct sockaddr*) &client_addr, client_addr_len); //Here is the segmentation fault comes from
}
}
I'm not really good at C, so forgive me if the code is silly.
Please point out anything wrong or just looks weird.
Thanks in advance for any help.
pkt is pointer to a Pkt, but you haven't initialized it. Likewise with ack. You can either a) malloc a Pkt and assign the result to pkt, or b) change pkt to be a Pkt structure (rather than a pointer). The second option would look something like:
struct Pkt pkt;
struct Ack ack;
bytes_recd = recvfrom(sock_server, &pkt, sizeof(pkt), 0, (struct sockaddr *) &client_addr, &client_addr_len);
ack.ack_num = pkt.seqnum;
printf("%02d\n", ack.ack_num);
num_of_bytes = pkt.length;
printf("The sequence number is: %d\n", ack.ack_num);
printf("Received Sentence is: %s\n with length %d\n\n", pkt.databytes, num_of_bytes);
/* send message */
bytes_sent = sendto(sock_server, &ack, sizeof(ack), 0, (struct sockaddr*) &client_addr, client_addr_len);
I created a simple tftp server that only handles read requests (RRQ). Everything was working fine until I started to make a multi-threaded version of the server. In the application, I simply receive requests in the main thread and I then forward the request to a new thread that does the packet analysis. Therefore, I need to forward the socket, the received packet and the sockaddr_in struct that contains the client information to the thread. With that said, I created a struct that holds all of these and forward them to the pthread.
I suspect the problem to be in the struct clientThread initialization part and forwarding part; since I'm certain of the correctness of the processing inside connection_handler.
Note: You can use the tftp client that comes with linux to test it.
Here's the code I've written so far (Threaded Version). Please compile it with the -pthread flag...
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <time.h>
#define TIMEOUT 5000
#define RETRIES 3
void *connection_handler(void *);
struct clientThread
{
int clientSock;
char buffer[1024];
struct sockaddr_in client;
};
int main()
{
char buffer[1024];
int udpSocket, client_socket, nBytes;
struct sockaddr_in serverAddr, client;
socklen_t addr_size;
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(69);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
while(1)
{
memset(buffer, 0, 1024);
nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &addr_size);
// Creating a thread and passing the packet received, the socket and the sockaddr_in struct...
pthread_t client_thread;
struct clientThread clientT;
strcpy(clientT.buffer,buffer);
clientT.clientSock = udpSocket;
clientT.client = client;
pthread_create(&client_thread, NULL, connection_handler, &clientT);
}
return 0;
}
void* connection_handler (void *clientThreaded)
{
char buffer[1024], filename[200], mode[20], *bufindex, opcode;
struct clientThread *cthread = clientThreaded;
int udpSocket = cthread->clientSock;
strcpy(buffer, cthread->buffer);
struct sockaddr_in client = cthread->client;
bufindex = buffer;
bufindex++;
// Extracting the opcode from the packet...
opcode = *bufindex++;
// Extracting the filename from the packet...
strncpy(filename, bufindex, sizeof(filename)-1);
bufindex += strlen(filename) + 1;
// Extracting the mode from the packet...
strncpy(mode, bufindex, sizeof(mode)-1);
// If we received an RRQ...
if (opcode == 1)
{
puts("Received RRQ Request");
struct timeval tv;
tv.tv_sec = 5;
char path[70] = "tmp/";
char filebuf [1024];
int count = 0, i; // Number of data portions sent
unsigned char packetbuf[1024];
char recvbuf[1024];
socklen_t recv_size;
socklen_t optionslength = sizeof(tv);
setsockopt(udpSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, optionslength);
FILE *fp;
char fullpath[200];
strcpy(fullpath, path);
strncat(fullpath, filename, sizeof(fullpath) -1);
fp = fopen(fullpath, "r");
if (fp == NULL)
perror("");
memset(filebuf, 0, sizeof(filebuf));
while (1)
{
int acked = 0;
int ssize = fread(filebuf, 1 , 512, fp);
count++;
sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
memcpy((char *) packetbuf + 4, filebuf, ssize);
packetbuf[2] = (count & 0xFF00) >> 8;
packetbuf[3] = (count & 0x00FF);
int len = 4 + ssize;
memset(recvbuf, 0, 1024);
printf("\nSending Packet #%i", count);
sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
for (i=0; i<RETRIES; i++)
{
int result = recvfrom(udpSocket, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);
if ((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
{
sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
printf("\nRetransmitting Packet #%i", count);
}
else if (result == -1)
{
// Handle Error
}
else
{
acked++;
printf("\nReceived ACK For Data Packet #%i", count);
break;
}
}
if (acked!=1)
{
puts("\nGave Up Transmission After 3 Retries");
break;
}
if (ssize != 512)
break;
}
}
return 0;
}
Here's my code for the Non-threaded version...
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#define TIMEOUT 5000
#define RETRIES 3
int main()
{
int udpSocket, nBytes;
char buffer[1024], filename[200], mode[20], *bufindex, opcode;
struct sockaddr_in serverAddr, client;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(69);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
while(1)
{
memset(buffer, 0, 1024);
nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &addr_size);
printf("%s",buffer);
bufindex = buffer;
bufindex++;
// Extracting the opcode from the packet...
opcode = *bufindex++;
// Extracting the filename from the packet...
strncpy(filename, bufindex, sizeof(filename)-1);
bufindex += strlen(filename) + 1;
// Extracting the mode from the packet...
strncpy(mode, bufindex, sizeof(mode)-1);
// If we received an RRQ...
if (opcode == 1)
{
puts("Received RRQ Request");
struct timeval tv;
tv.tv_sec = 5;
char path[70] = "tmp/";
char filebuf [1024];
int count = 0, i; // Number of data portions sent
unsigned char packetbuf[1024];
char recvbuf[1024];
socklen_t recv_size;
socklen_t optionslength = sizeof(tv);
setsockopt(udpSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, optionslength);
FILE *fp;
char fullpath[200];
strcpy(fullpath, path);
strncat(fullpath, filename, sizeof(fullpath) -1);
fp = fopen(fullpath, "r");
if (fp == NULL)
perror("");
memset(filebuf, 0, sizeof(filebuf));
while (1)
{
int acked = 0;
int ssize = fread(filebuf, 1 , 512, fp);
count++;
sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
memcpy((char *) packetbuf + 4, filebuf, ssize);
packetbuf[2] = (count & 0xFF00) >> 8;
packetbuf[3] = (count & 0x00FF);
int len = 4 + ssize;
memset(recvbuf, 0, 1024);
printf("\nSending Packet #%i", count);
sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
for (i=0; i<RETRIES; i++)
{
int result = recvfrom(udpSocket, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);
if ((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
{
sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
printf("\nRetransmitting Packet #%i", count);
}
else if (result == -1)
{
// Handle Error
}
else
{
acked++;
printf("\nReceived ACK For Data Packet #%i", count);
break;
}
}
if (acked!=1)
{
puts("\nGave Up Transmission After 3 Retries");
break;
}
if (ssize != 512)
break;
}
}
}
return 0;
}
Thanks in advance :)
You loop listening on port 69 but the actual data transfer will be carried out from a different randomly selected port (please read RFC 1350).
Then your main loop must create a new thread for every new transfer, this new thread should receive a structure containing the path of the file to serve, the destination IP/port, the randomly selected local port, etc.
Something you must consider when passing a structure pointer to a thread is the memory supporting the structure.
In your case
struct clientThread clientT;
is dinamicaly created in the stack and of-course the structure is "discarded" when the block of code goes out of scope (in your case on every loop) that means you are passing a pointer to "soon to be garbage" to the just created thread.
I recommend using malloc/free when passing structures to just created threads.
Finally your main thread (dispatcher) should maintain a structure taking into account all the created threads and their status. This is necessary to detect dead threads or when needing to close the main program when there are transfers in progress.
As you can see implementing a server even for a simple protocol like TFTP is not really easy.
Your connection handler is wrong here because there is no locking whatsoever on the socket that you pass to each thread.
Most servers that are udp based are in fact not forking multiple threads. TCP servers can do it , because with each accept you get a new socket that can be delegated to a new thread that won't be used by any other thread.
But for udp, you are basically using the same socket for all your threads and this is not ok. If you were to provide protection on the socket you can make it work, but you will lose the benefits that you are trying to obtain by making it multithreaded.
I wrote a simple tftp server that only handles read requests (RRQ) from clients. The problem is that the server seems not to be sending DATA packets to the client. I made sure that the server is not sending the DATA packet through checking the length of the sent bytes against the data packet size.
Note: I use the standard tftp client that comes with linux.
Here's the code I've written so far...
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void sendFile (char *Filename, char *mode, struct sockaddr_in client)
{
char path[70] = "tmp/";
char filebuf [1024];
int acked = 0; // Number of acked data portions
int count = 0; // Number of data portions we sent
unsigned char packetbuf[1024];
char recvbuf[1024];
socklen_t recv_size;
int sock = socket(PF_INET, SOCK_DGRAM, 0);
FILE *fp;
char fullpath[200];
strcpy(fullpath, path);
strncat(fullpath, Filename, sizeof(fullpath) -1);
fp = fopen(fullpath, "r");
if (fp == NULL)
perror("");
memset(filebuf, 0, sizeof(filebuf));
while (1)
{
int ssize = fread(filebuf, 1 , 512, fp);
count++;
sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
memcpy((char *) packetbuf + 4, filebuf, ssize);
packetbuf[2] = (count & 0xFF00) >> 8;
packetbuf[3] = (count & 0x00FF);
int len = 4 + ssize;
if (sendto(sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client)) != len)
puts("SENDING FAILED!");
memset(recvbuf, 0, 1024);
recvfrom(sock, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);
if (recvbuf[1] == 4)
puts("Acked");
if (ssize != 512)
break;
}
}
int main()
{
int udpSocket, nBytes;
char buffer[1024], filename[200], mode[20], *bufindex, opcode;
struct sockaddr_in serverAddr, client;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(69);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
memset(buffer,0,1024);
while(1)
{
nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &addr_size);
bufindex = buffer;
bufindex++;
// Extracting the opcode from the packet...
opcode = *bufindex++;
// Extracting the filename from the packet...
strncpy(filename, bufindex, sizeof(filename)-1);
bufindex += strlen(filename) + 1;
// Extracting the mode from the packet...
strncpy(mode, bufindex, sizeof(mode)-1);
// If we received an RRQ...
if (opcode == 1)
sendFile(filename, mode, client);
}
return 0;
}
Thanks in advance :)
Your opcode check appears to be incorrect.
Try:
bufindex++;
opcode = *bufindex++;
Also, why 1024 bytes in sendto. Shouldn't it be size of file? You are just passing on the size of the packetbuf instead of actual size of file.
Change to :
sendto((sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof (client)) );
I'm writing a simple proxy using socket programming and C.
I have a problem in getting HTTP response from the webserver. When I run this code and try to visit cisco.com website, my program gives me a segmentation fault error.
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
int main()
{
int serverSocket;
char ip[INET_ADDRSTRLEN];
int i;
int j;
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serverAddress ;
char pageFilter[] = "HTTP/1.1 302 Found\n\n<html><head><title>this web page is filter !..!</title></head><body><h1>This page in filter</h1><p><h3>Please Leave This site and get out of here!</h3></p></body></html>";
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(9090);
bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress));
listen(serverSocket, 20);
while (1)
{
int clientSocket ;
int size;
struct sockaddr_in clientAddress;
char * pch;
char * site;
char buffer[9000];
size = sizeof(clientAddress);
clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddress, &size);
recv(clientSocket, buffer, sizeof(buffer), 0);
site = strstr(buffer, "yahoo.com");
if(site != NULL)
{
send(clientSocket, pageFilter, sizeof(pageFilter) ,0);
close(clientSocket);
}
else
{
int middleSocket;
char buf[128];
char response[10000];
struct sockaddr_in middleAddress;
struct hostent *he;
struct in_addr **addr_list;
char site[] = "cisco.com";
middleSocket = socket(AF_INET, SOCK_STREAM, 0);
middleAddress.sin_family = AF_INET;
middleAddress.sin_port = htons(80);
he = gethostbyname(site);
addr_list = (struct in_addr **)he -> h_addr_list;
middleAddress.sin_addr.s_addr = inet_addr(inet_ntoa(*addr_list[0]));
connect(middleSocket, (struct sockaddr *)&middleAddress, sizeof(middleAddress));
send(middleSocket, buffer, sizeof(buffer),0);
while((unsigned)strlen(buf) != 0)
{
recv(middleSocket, buf, sizeof(buf), 0);
strcat(response,buf);
}
send(clientSocket, response, strlen(response),0);
close(clientSocket);
}
}
return 0;
}
First, capture the return value of recv, which either indicates an error, or the number of bytes received.
ssize_t n;
n = recv(middleSocket, buf, sizeof(buf) - 1);
Note that you shouldn't pass sizeof(buf), since you need room to guarantee zero-termination of buf.
Check for errors.
if (n == -1) { /* handle error */ }
else if (n == 0) { /* handle empty response */ }
And zero-terminate buf.
buf[n] = '\0';
Your segmentation fault is probably occurring because you call strstr on an unbounded string.
A segmentation fault is caused by invalid memory access. Read more: http://en.wikipedia.org/wiki/Segmentation_fault
Use GDB to find the where exactly the program is executing when the error occurs. After this, you should see the problem in your code.
I'm trying to get a simple send and receive UDP program working, but I'm having a bit of trouble with saving the received data. As far as I can tell the data is being sent and received properly, as I've printed it out on both ends. Before I write the data to the file (if I just print out the received chunk) it doesn't have any extra characters, so I'm a bit lost as to where they are coming from.
When I append each chunk of received data to the file it adds a "^P^B^GÐ^?" after every chunk written. for example one of the chunks ended with "We, therefore^P^B^GÐ^?," instead of "We, therefore,".
Any help is appreciated, thanks in advance.
UPDATE:
I've seemed to have gotten things working semi-better, I'm now having an issue with it replacing the first character of every chunk with a null character, for example:
"^#N CONGRESS, July 4, 1776." instead of "IN CONGRESS, July 4, 1776."
It's doing this for the first char of every chunk received, I've tried multiple debug print statements but can't seem to figure out what the issue is.
Here is my Receive and Send functions:
void receiveFile() {
int addr_len, bytesRead;
char recvData[BUFSIZE]; // Buffer to store received data
struct sockaddr_in server_addr, client_addr;
// Set up struct to receive data from our port and address
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_addr.sin_zero), 8);
addr_len = sizeof (struct sockaddr);
printf("\nWaiting for data on port %d\n", port);
//Keep reading data from the socket
while (1) {
FILE *fp;
fp=fopen("dummyfile.txt", "ab");
memset(recvData, 0, BUFSIZE);
bytesRead = recvfrom(sock, recvData, BUFSIZE, 0,
(struct sockaddr *) &client_addr, &addr_len);
int x;
for(x = 0; x < bytesRead; x++) {
fputc(recvData[x], fp);
}
// Print out who we're receiving from and what we're recieving
printf("Receiving data from %s : %d\n", inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
fclose(fp);
}}
Here is the Send Function:
void sendFile() {
// Announce who we're sending data to
if(DEBUG) { printf("\nSending %s to %s:%d\n", filename, address, port); }
// Open file
FILE * file = fopen(filename, "rb");
if (file == NULL) {
perror("Invalid File\n");
exit(1);
}
// Get size of the file
fseek(file, 0, SEEK_END);
int filesize = ftell(file);
rewind(file);
int curPos = 0;
int dataSize = 0;
while(curPos < filesize) {
struct sockaddr_in server_addr;
struct hostent *recvr;
char sendData[BUFSIZE]; // stores message to be sent
memset(sendData, 0, BUFSIZE);
int byte, i;
for(i = 0; i < BUFSIZE; i++){
if((filesize - curPos) > 0) {
byte = fgetc(file);
sendData[i] = byte;
curPos++;
dataSize++;
}
else { break; }
}
recvr = gethostbyname(address);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr *) recvr->h_addr);
bzero(&(server_addr.sin_zero), 8);
if(DEBUG) {
char tempData[1201];
strncpy(tempData, sendData, 1200);
tempData[1201] ='\0';
printf("%s\n\n\n\n\n", tempData);
}
sendto(sock, sendData, dataSize, 0,
(struct sockaddr *) &server_addr, sizeof (struct sockaddr));
dataSize = 0;
}
fclose(file);}
What happens when you change the printing to:
fprintf(fp, "%.*s", bytesRead, recvData);
There is a guarantee that recvfrom() will not null terminate your messages; you would have to transmit the null terminator yourself.
I can't tell what your residual problem is. I have the following to complete programs working back to back. I've scrutinized the saved file for NULs with no problem.
I ran them as:
./recv & sleep 1; ./send; kill %1
recv.c
#include "posixver.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h> /* sockaddr_in */
#include <arpa/inet.h> /* inet_ntoa() */
#include "stderr.h"
static void receiveFile(int sock, int port, char *filename)
{
//Keep reading data from the socket
FILE *fp = fopen(filename, "ab");
if (fp == 0)
err_syserr("failed to open file %s", filename);
printf("\nWaiting for data on port %d\n", port);
while (1)
{
char recvData[BUFSIZ]; // Buffer to store received data
struct sockaddr_storage addr;
struct sockaddr_in *client_addr = (struct sockaddr_in *)&addr;
memset(recvData, 0, sizeof(recvData));
socklen_t addr_len = sizeof (struct sockaddr_storage);
int bytesRead = recvfrom(sock, recvData, sizeof(recvData), 0,
(struct sockaddr *) &client_addr, &addr_len);
if (bytesRead < 0)
err_syserr("Failed to read from socket");
err_remark("Read %d bytes\n", bytesRead);
for (int x = 0; x < bytesRead; x++)
{
fputc(recvData[x], fp);
}
fflush(fp);
// Print out who we're receiving from and what we're receiving
//char *rem_host = inet_ntoa(client_addr->sin_addr);
//int rem_port = ntohs(client_addr->sin_port);
//printf("Receiving %d bytes from %s:%d\n", bytesRead, rem_host ? rem_host : "<unknown>", rem_port);
}
fclose(fp);
}
int main(int argc, char **argv)
{
int fd;
struct sockaddr_storage addr;
struct sockaddr_in *server_addr = (struct sockaddr_in *)&addr;
memset(&addr, 0, sizeof(addr));
server_addr->sin_family = AF_INET;
server_addr->sin_addr.s_addr = htonl(INADDR_ANY);
server_addr->sin_port = htons(5190);
err_setarg0(argv[0]);
if (argc > 1)
err_usage("");
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
err_syserr("Failed to open DGRAM socket");
if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0)
err_syserr("Failed to bind DGRAM socket");
receiveFile(fd, 5190, "dummy.text");
return(0);
}
send.c
#include "posixver.h"
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "stderr.h"
#define bzero(b,len) (memset((b), '\0', (len)), (void)0)
enum { DEBUG = 1 };
static void sendFile(int sock, const char *filename, char *address, int port)
{
// Announce who we're sending data to
if (DEBUG)
printf("\nSending %s to %s:%d\n", filename, address, port);
// Open file
FILE * file = fopen(filename, "rb");
if (file == 0)
err_syserr("Failed to open file %s", filename);
// Get size of the file
fseek(file, 0, SEEK_END);
int filesize = ftell(file);
rewind(file);
int curPos = 0;
int dataSize = 0;
while (curPos < filesize)
{
struct sockaddr_in server_addr;
struct hostent *recvr;
char sendData[BUFSIZ]; // stores message to be sent
memset(sendData, 0, BUFSIZ);
int byte, i;
for (i = 0; i < BUFSIZ; i++){
if ((filesize - curPos) > 0) {
byte = fgetc(file);
sendData[i] = byte;
curPos++;
dataSize++;
}
else
break;
}
recvr = gethostbyname(address);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr *) recvr->h_addr_list[0]);
bzero(&(server_addr.sin_zero), 8);
if(DEBUG) {
char tempData[1201];
strncpy(tempData, sendData, 1200);
tempData[1201] ='\0';
printf("SEND:\n%s\n\n\n", tempData);
}
if (sendto(sock, sendData, dataSize, 0,
(struct sockaddr *) &server_addr, sizeof (struct sockaddr)) < 0)
err_syserr("Failed to send %d bytes\n", dataSize);
dataSize = 0;
}
fclose(file);
}
int main(int argc, char **argv)
{
int fd;
err_setarg0(argv[0]);
if (argc > 1)
err_usage("");
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
err_syserr("Failed to open DGRAM socket");
sendFile(fd, "/etc/passwd", "localhost", 5190);
return(0);
}
posixver.h
#ifndef JLSS_ID_POSIXVER_H
#define JLSS_ID_POSIXVER_H
/*
** Include this file before including system headers. By default, with
** C99 support from the compiler, it requests POSIX 2001 support. With
** C89 support only, it requests POSIX 1997 support. Override the
** default behaviour by setting either _XOPEN_SOURCE or _POSIX_C_SOURCE.
*/
/* _XOPEN_SOURCE 700 is loosely equivalent to _POSIX_C_SOURCE 200809L */
/* _XOPEN_SOURCE 600 is loosely equivalent to _POSIX_C_SOURCE 200112L */
/* _XOPEN_SOURCE 500 is loosely equivalent to _POSIX_C_SOURCE 199506L */
#if !defined(_XOPEN_SOURCE) && !defined(_POSIX_C_SOURCE)
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600 /* SUS v3, POSIX 1003.1 2004 (POSIX 2001 + Corrigenda) */
#else
#define _XOPEN_SOURCE 500 /* SUS v2, POSIX 1003.1 1997 */
#endif /* __STDC_VERSION__ */
#endif /* !_XOPEN_SOURCE && !_POSIX_C_SOURCE */
#endif /* JLSS_ID_POSIXVER_H */
stderr.c and stderr.h
Actually, not standard at all, except in my code. The functions used have the declarations:
extern void err_setarg0(const char *argv0);
extern void err_error(const char *format, ...) PRINTFLIKE(1,2) NORETURN();
extern void err_remark(const char *format, ...) PRINTFLIKE(1,2);
extern void err_syserr(const char *format, ...) PRINTFLIKE(1,2) NORETURN();
extern void err_usage(const char *usestr) NORETURN();
The first records the program name. The second reports an error message and exits; the third reports a message and returns; the fourth reports an error message and adds error information from 'errno' and 'strerror()' if there is any to use; the last reports on how to use the program - in this case, the programs accept no arguments. The full source code (quite large) is available from the IIUG Software site as part of the SQLCMD package available there, and various other programs that I've also submitted there.