I'm new to socketpairs and I need my children each to pass information from a structure to the parent.I was told this can be done using SOCK_DGRAM but I don't know how to do it.I looked over the internet but i couldn't find a concrete example.Can you please show for example hoe can you pass to the parent a structure made out of 2 ints and a string maybe ?I just want an example so I can understand how I could build this kind of socketpair and send information through it.Thank you
How about the following:
int sockets[2];
if (socketpair(AF_INET, SOCK_DGRAM, 0, sockets) != -1)
{
int res = fork();
if (res == 0)
{
/* In child process */
/* We only need one socket, so close the other */
close(sockets[0]);
struct some_structure my_struct;
write(sockets[1], &my_struct, sizeof(my_struct));
/* All done */
exit(0);
}
else if (res > 0)
{
/* In parent process */
/* We only need one socket, so close the other */
close(sockets[1]);
struct some_structure my_struct;
read(sockets[0], &my_struct, sizeof(my_struct));
}
}
The above code doesn't check for, or handle, errors. It can't handle structures containing pointers, structures using arrays are okay though.
Assuming that your string is represented as a char* as in
struct data {
int i, j;
char *s;
};
you need to devise some serialization format, because sending a pointer won't work; the pointee is not passed so it won't point to anything useful in the receiver (the parent). A simple format would be to put the integers end-to-end, then directly append the string including its NUL terminator, so you'd get
int senddata(int fd, struct data const *d)
{
size_t msglen = 2 * sizeof(int) + strlen(d->s) + 1;
char *msg = malloc(msglen);
if (msg == NULL)
return -1;
((int *)msg)[0] = d->i;
((int *)msg)[1] = d->j;
strcpy(msg + 2 * sizeof(int), d->s);
ssize_t r = send(fd, msg, msglen, 0);
free(msg);
return r;
}
with a corresponding receive function for the parent. You might want to put some maximum length on the string, because the parent needs to know the size of the message in advance.
Related
I am working on a client/server program that is supposed to take user input (two integers) and allow the user to calculate those to receive an answer.
When I run my program there seems to be an issue with either connecting to the client or opening the directory and I'm not sure what it could be. I'm entirely new to setting up servers and utilizing directories to read and write .txt files to.
Here are the parts of code that I think might be wrong and causing the issues I am facing, which is the program asks for a port number to connect to (2000 is what is recommended to use) so I enter that and then nothing happens.
// PURPOSE: To run the server by 'accept()'-ing client requests from
// 'listenFd' and doing them.
void doServer (int listenFd)
{
/* Application validity check: */
/* Server clients: */
pthread_t threadId;
pthread_attr_t threadAttr;
int threadCount = 0;
int *iPtr;
/* YOUR CODE HERE: */
while(1)
{
/* Accept connection to client: */
int connfd = accept(listenFd, NULL, NULL); //if I change this to getServerFileDescriptor (another function within the code, it will continuously loop through threadNumbers and do nothing as well).
/* Malloc memory for two integers: */
iPtr = (int *)calloc(2, sizeof(int));
/*Put file descriptor in the first space: */
iPtr[0] = listenFd; // or just listenFd not sure
/* put threadCount into the second space and increment: */
iPtr[1] = threadCount++;
/* Creates detached thread for handleClient and passes the address of iPtr */
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
pthread_create(&threadId, &threadAttr, handleClient, (void*)iPtr);
pthread_join(threadId, NULL);
pthread_attr_destroy(&threadAttr);
}
}
void* handleClient(void* vPtr)
{
/* Read command: */
char buffer[BUFFER_LEN];
char command;
int fileNum;
char text[BUFFER_LEN];
int shouldContinue = 1;
int threadNum;
int fd;
/* Cast void* vPtr back to an int */
int *iPtr = (int *)vPtr;
/* Assign file descriptor to a local value named 'fd'*/
fd = iPtr[0];
/* Assign thread number to local value named 'threadNum'*/
threadNum = iPtr[1];
free(iPtr);
while (shouldContinue)
{
memset(buffer,'\0',BUFFER_LEN);
memset(text ,'\0',BUFFER_LEN);
read(fd,buffer,BUFFER_LEN);
printf("Thread %d received: %s\n",threadNum,buffer);
sscanf(buffer,"%c %d \"%[^\"]\"",&command,&fileNum,text);
/* YOUR CODE HERE: */
if(command == DIR_CMD_CHAR)
{
/* 1. Open the current directory (named "."). If an error occurs then just send STD_ERROR_MSG back to the client: */
DIR* dirPtr = opendir(".");
struct dirent* entryPtr;
/* If error occurs send STD_ERROR_MSG to client: */
if ((dirPtr = opendir (".")) == NULL) {
{
write(fd, STD_ERROR_MSG, sizeof(STD_ERROR_MSG));
//return(EXIT_FAILURE);
}
/* Read as many entries that will fit into BUFFER_LEN
put as many entries into the buffer and send the buffer to client
d_name=entryPtr into the bufffer using strcat_s,
make sure buffer starts empty
buffer[0]='\n';
add new line char using stringcat "\n"
make sure do not go over buffer lengh */
if (dirPtr)
{
while ((entryPtr = readdir(dirPtr)) != NULL)
{
buffer[0]='\0';
int i;
int sizebuf = sizeof(buffer);
for (i = 0; i < sizebuf; i++)
{
strcat(buffer,entryPtr->d_name);
strcat(buffer,"\n");
}
}
}
/* 3. Close directory */
closedir(dirPtr);
}
Here's how the correct output should look.
$ ./mathClient Machine name [localhost.localdomain]? (I just pressed enter)
Port number? 2000
What would you like to do:
(1) List files
(2) Read a math file
(3) Write a math file
(4) Calculate a math file
(5) Delete a math file
(0) Quit
Your choice? 1
Sending "l"
0.bc
Here are the instructions for the code that I am having trouble with.
Implementing doServer(int listenFd) (10 Points):
doServer() should have a loop in which it waits for a client to connect to listenFd. When a client does, it should:
malloc() enough memory for 2 integers
put the file descriptor from accept() in one of those spaces
put the value of threadCount in the other space, and increment threadCount
Make a detached thread to handle this new client. I called my function handleClient(), but you may call yours whatever. Pass the address of your malloc()-ed array.
The loop should then go back for another accept().
void* handleClient(void* vPtr) (10 Points):
(Or whatever you call your function that runs a thread for the client.)
The thread id and the file descriptor are passed, but they come in as a void* pointer.
Use another pointer to cast back to int*
Save the file descriptor and thread number in local vars
free() the memory
Print the thread number and do a loop like this:
// II.B. Read command:
char buffer[BUFFER_LEN];
char command;
int fileNum;
char text[BUFFER_LEN];
int shouldContinue = 1;
while (shouldContinue)
{
text[0] = '\0';
read(fd,buffer,BUFFER_LEN);
printf("Thread %d received: %s\n",threadNum,buffer);
sscanf(buffer,"%c %d \"%[^\"]\"",&command,&fileNum,text);
// YOUR CODE HERE
}
It read()s a line of text from the client into buffer[], and parses the line into a command character, fileNum integer, and quote-delineated text[] string. (The fileNum and text[] may or may not be given, depending upon the value of command.)
Then do the following operations based upon the value of command. Except for QUIT_CMD_CHAR I strongly recommend using a different function for each!
When the function ends just have it do:
printf("Thread %d quitting.\n",threadNum);
return(NULL);
command == DIR_CMD_CHAR (15 Points):
Open the current directory (named "."). If an error occurs then just send STD_ERROR_MSG back to the client.
Copy as many entries that will fit into a buffer of length BUFFER_LEN. Be sure to put a separating '\n' after each entry.
Close the directory.
Any help would be appreciated, if you need the full code I could send that to you if that would help.
EDIT: Here are two additional functions, one called getPortNum() and another called getServerFileDescriptor() which address receiving a port number and setting up sockets for the connection. Additionally I included the main() which utilizes these.
// PURPOSE: To decide a port number, either from the command line arguments
// 'argc' and 'argv[]', or by asking the user. Returns port number.
int getPortNum (int argc,
char* argv[]
)
{
// I. Application validity check:
// II. Get listening socket:
int portNum;
if (argc >= 2)
portNum = strtol(argv[1],NULL,0);
else
{
char buffer[BUFFER_LEN];
printf("Port number to monopolize? ");
fgets(buffer,BUFFER_LEN,stdin);
portNum = strtol(buffer,NULL,0);
}
// III. Finished:
return(portNum);
}
// PURPOSE: To attempt to create and return a file-descriptor for listening
// to the OS telling this server when a client process has connect()-ed
// to 'port'. Returns that file-descriptor, or 'ERROR_FD' on failure.
int getServerFileDescriptor
(int port
)
{
// I. Application validity check:
// II. Attempt to get socket file descriptor and bind it to 'port':
// II.A. Create a socket
int socketDescriptor = socket(AF_INET, // AF_INET domain
SOCK_STREAM, // Reliable TCP
0);
if (socketDescriptor < 0)
{
perror(THIS_PROGRAM_NAME);
return(ERROR_FD);
}
// II.B. Attempt to bind 'socketDescriptor' to 'port':
// II.B.1. We'll fill in this datastruct
struct sockaddr_in socketInfo;
// II.B.2. Fill socketInfo with 0's
memset(&socketInfo,'\0',sizeof(socketInfo));
// II.B.3. Use TCP/IP:
socketInfo.sin_family = AF_INET;
// II.B.4. Tell port in network endian with htons()
socketInfo.sin_port = htons(port);
// II.B.5. Allow machine to connect to this service
socketInfo.sin_addr.s_addr = INADDR_ANY;
// II.B.6. Try to bind socket with port and other specifications
int status = bind(socketDescriptor, // from socket()
(struct sockaddr*)&socketInfo,
sizeof(socketInfo)
);
if (status < 0)
{
perror(THIS_PROGRAM_NAME);
return(ERROR_FD);
}
// II.B.6. Set OS queue length:
listen(socketDescriptor,5);
// III. Finished:
return(socketDescriptor);
}
int main (int argc,
char* argv[]
)
{
// I. Application validity check:
// II. Do server:
int port = getPortNum(argc,argv);
int listenFd = getServerFileDescriptor(port);
int status = EXIT_FAILURE;
if (listenFd >= 0)
{
doServer(listenFd);
close(listenFd);
status = EXIT_SUCCESS;
}
// III. Finished:
return(status);
I need to write a small client/server application in C on Linux.
I have built a short example in order to explore a little bit more since i am new to network programming.
I am basically trying to send an array of double dynamically allocated by the client.
I found the following way to do it ( client side ) :
write(sd,&datas.size,sizeof(int)); /* send size */
write(sd,datas.yi,datas.size*sizeof(double));/* send array */
and on the server side :
read(sd_cli,&datas.size,sizeof(int)); /* receive size */
datas.yi=(double *)malloc(datas.size*sizeof(double));
read(sd_cli,datas.yi,datas.size*sizeof(double)); /* receiving datas */
At first sight my code seems to work fine.
But since the write calls are non blocking, i ask myself if the read sequence can receive , for example the array of double before its size ?
Is there any guarantee that this can never happen ?
Thanks.
Sockets of type SOCK_STREAM provide reliable, in-order data transmission, though details depend on the nature of the underlying transport. The reader will receive all the data successfully written (if it in fact chooses to read them all), byte-for-byte in the order they were written, but not necessarily in the same size chunks.
Blocking vs. non-blocking has nothing to do with it, though I don't actually see what makes you say your writes are non-blocking. Perhaps you're remarking on the fact that neither write() nor read() promises to transfer the full number of bytes requested on any given call. That in itself provides no guarantee against blocking, but you absolutely do need to account for it correctly, especially with sockets, and even more especially if you really have put one or both ends of the socket in non-blocking mode. The original version of your question seemed to claim that you do account for it.
In any case, barring some kind of kernel bug, your client will never read the array size after any part of the array, nor otherwise receive bytes in a different relative order than they were written.
To be perfectly clear, however, here are reasonable implementations for reading and writing variable-size double arrays via a stream socket. They assume that sender and receiver have identical representations of type double, which will certainly be the case for UNIX-domain sockets. They are not at all trivial, though the helper functions comprising around half the code are suitable for reuse:
/******
* helper functions
*/
/*
* Returns the number of bytes written, which may be zero, or a number
* less than zero on failure.
*/
ssize_t write_fully(int fd, const void *buf, size_t count) {
const unsigned char *next = buf;
size_t remaining = count;
while (remaining) {
ssize_t n_written = write(fd, next, remaining);
if (n_written < 0) {
/* error */
return n_written;
} else {
assert(n_written <= remaining);
next += n_written;
remaining -= n_written;
}
}
/* all bytes successfully written */
return count;
}
/*
* Returns the number of bytes read on success, or a number less
* than zero on error. It is accounted "success" if the end of the stream
* is reached before the requested number of bytes is read; that case
* can be distinguished by the return value, but no recovery is
* possible.
*/
ssize_t read_fully(int fd, void *buf, size_t count) {
unsigned char *next = buf;
size_t remaining = count;
while (remaining) {
ssize_t n_read = read(fd, next, remaining);
if (n_read < 0) {
/* error */
return n_read;
} else if (n_read) {
assert(n_read <= remaining);
next += n_read;
remaining -= n_read;
} else {
/* premature end of file */
return count - remaining;
}
}
/* all bytes successfully read */
return count;
}
/******
* Array-transfer functions
*/
/* returns 0 on success, else nonzero */
int write_double_array(int fd, unsigned n, double d[n]) {
ssize_t bytes_written;
bytes_written = write_fully(fd, &n, sizeof(n));
if (bytes_written < 0) return bytes_written;
bytes_written = write_fully(fd, d, n * sizeof(double));
return (bytes_written < 0) ? bytes_written : 0;
}
/*
* returns 0 on success, else nonzero.
* On success, the caller takes responsibility for freeing the
* dynamically-allocated result array.
*/
int read_double_array(int fd, unsigned *n, double **d) {
unsigned temp_n;
ssize_t bytes_read = read_fully(fd, &temp_n, sizeof(temp_n));
if (bytes_read < 0) {
return -1;
} else if (bytes_read != sizeof(temp_n)) {
return 1;
} else if (temp_n) {
size_t n_bytes = temp_n * sizeof(double);
double *temp = malloc(n_bytes);
if (!temp) return -1; /* allocation failure */
if (read_fully(fd, temp, n_bytes) < n_bytes) {
free(temp);
return -1;
}
/* success */
*d = temp;
}
*n = temp_n;
return 0;
}
You could implement the array-transfer protocol differently, but that approach sends the data in the same form that you claim to do. You cannot safely do it any more simply than that.
I am trying to send across a character array using a transmitter and a receiver program using raw sockets. I am able to get the correct number of bytes sent at the receiver side, but the values printed out are garbage. Could someone help me out here?
Transmitter:
int create_raw_socket(char *dev)
{
struct sockaddr_ll sll;
struct ifreq ifr;
int fd, ifi, rb;
bzero(&sll, sizeof(sll));
bzero(&ifr, sizeof(ifr));
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
assert(fd != -1);
strncpy((char *)ifr.ifr_name, dev, IFNAMSIZ);
ifi = ioctl(fd, SIOCGIFINDEX, &ifr);
assert(ifi != -1);
sll.sll_protocol = htons(ETH_P_ALL);
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
rb = bind(fd, (struct sockaddr *)&sll,sizeof(sll));
assert(rb != -1);
return fd;
}
int SendPacket(char *dev ,unsigned char *send_packet, int packet_len)
{
int num_sent= 0;
int sockaddress = create_raw_socket(dev);
if((num_sent = write(sockaddress, &send_packet, packet_len)) != packet_len)
{
close(sockaddress);
return 0;
}
else
{
close(sockaddress);
return 1;
}
}
int main(int argc, char**argv)
{
int x,fd,s;
char *send_packet="HELLO";
int len = sizeof(send_packet);
while(1)
{
if(!SendPacket((argv[1]), send_packet, len))
perror("Error sending packet");
else
printf("Packet sent successfully with payload : %s\n" ,send_packet);
}
return 0;
}
Receiver :
int main(int argc, char **argv)
{
struct sockaddr addr;
int sock_fd, fromlen,s;
char buf[PACKET_LENGTH];
char *dev = argv[1];
while(1)
{
fromlen=sizeof(addr);
sock_fd = create_raw_socket(dev); /* Creating the raw socket */
int x= recvfrom(sock_fd,&buf,sizeof(buf),0,&addr,&fromlen);
printf("\n Number of bytes of data received is %d \n",x);
printf("\nPayload Received from client... is %s \n", buf);
close(sock_fd);
}
return 0;
}
Change
write(sockaddress, &send_packet, packet_len)
to
write(sockaddress, send_packet, packet_len)
send_packet is already the address of the buffer to be sent, if you take the address of this address (more precisely the address of the variable holding the address), you will read the wrong memory for the buffer
Similarly for recvfrom:
recvfrom(sock_fd, buf, sizeof(buf), 0, &addr, &fromlen)
You have several problems:
This line
if((num_sent = write(sockaddress, &send_packet, packet_len)) != packet_len)
Should say just send_packet instead of &send_packet. send_packet is a pointer that points to the desired packet data, so there's no need to take its address -- you don't want to write out the literal address of that pointer into the packet, that just simply won't work.
This is wrong:
char *send_packet="HELLO";
int len = sizeof(send_packet);
sizeof(send_packet) will always be the size of a pointer on your system, typically either 4 or 8 bytes. You really want to either declare send_packet as an array type (e.g. char send_packet[] = ...), or use strlen to compute its length at runtime (e.g. int len = strlen(send_packet) + 1;). In your case, you're either sending too little data (4 bytes) or too much data (8 bytes), both of which are problematic.
Your printf code in the client assumes that the data it receives is null-terminated, which it is not necessarily. You should either manually null-terminate the data before printing it (or using any other string functions, for that matter), or tell printf the limit of how much data to print. I'd suggest null-terminating it like so:
char buf[PACKET_LENGTH + 1]; // +1 for null terminator
int x = recvfrom(sock_fd,buf,PACKET_LENGTH,0,&addr,&fromlen);
if(x >= 0)
buf[x] = 0;
Your code has poor const correctness. SendPacket should take a const char* instead of char* parameter, and send_packet should either be declared as char[] or as const char*. The conversion from string literals to char* is deprecated and should be avoided in all new C code.
Using printf to print the buffer will print a string until the en of string character is reached. If you see the original string followed by garbage characters, this may be the reason.
You should probably introduce a 0 after the last byte returned by recvfrom, or you will print whatever value was in the memory that recvfrom did not overwrite. It could even try to access memory outside the buffer.
Try adding something like:
int x= recvfrom(sock_fd,&buf,sizeof(buf) - 1,0,&addr,&fromlen);
buf[x - 1] = 0;
Note: that changes the maximum size of what is read, it is just an example of how to do it.
What is the appropriate way to transfer an int over a socket in C?
What I am doing so far is:
int n = 4;
int tmp = htonl(n);
write(socket, &tmp, sizeof(tmp));
and
int tmp,n;
read(socket, &tmp, sizeof(tmp));
n = ntohl(tmp);
However, the integer received is sometimes 0. Not always, but let's say 2 out of 5 times. It is never some other value, always 0. Why?
UPDATE: Return value from read is -1 and an error is:
Resource temporarily unavailable
First of all, sizeof(int) may differ on your sender and receiver machine. So I would recommend you to use something like int32_t from stdint.h.
Also, it is not guaranteed that read(..,..,sizeof(int)) will read exactly sizeof(int) bytes - it can read nothing, or it can read less bytes. So, the correct variant will be something more like this:
int send_int(int num, int fd)
{
int32_t conv = htonl(num);
char *data = (char*)&conv;
int left = sizeof(conv);
int rc;
do {
rc = write(fd, data, left);
if (rc < 0) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
// use select() or epoll() to wait for the socket to be writable again
}
else if (errno != EINTR) {
return -1;
}
}
else {
data += rc;
left -= rc;
}
}
while (left > 0);
return 0;
}
int receive_int(int *num, int fd)
{
int32_t ret;
char *data = (char*)&ret;
int left = sizeof(ret);
int rc;
do {
rc = read(fd, data, left);
if (rc <= 0) { /* instead of ret */
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
// use select() or epoll() to wait for the socket to be readable again
}
else if (errno != EINTR) {
return -1;
}
}
else {
data += rc;
left -= rc;
}
}
while (left > 0);
*num = ntohl(ret);
return 0;
}
This should work without any problem, try this :
On the sender (Server) side :
int number_to_send = 10000; // Put your value
int converted_number = htonl(number_to_send);
// Write the number to the opened socket
write(client_socket, &converted_number, sizeof(converted_number));
On the receiver(client) side :
int received_int = 0;
return_status = read(client_socket, &received_int, sizeof(received_int));
if (return_status > 0) {
fprintf(stdout, "Received int = %d\n", ntohl(received_int));
}
else {
// Handling erros here
}
Hope this will help.
I also had the same problem and wasted 30 minutes trying to find out what I did wrong. But, finally, I found the solution. From the documentation we can see that htonl() and ntohl() functions work with 32 bit unsigned integers. So, to fix the issue, you have to use unsigned __int32 or uint32_t from <stdint.h>.
So the code will look like this:
#include <stdint.h>
uint32_t n = 4;
uint32_t tmp = htonl(n);
write(socket, &tmp, sizeof(tmp));
and:
#include <stdint.h>
uint32_t tmp,n;
read(socket, &tmp, sizeof(tmp));
n = ntohl(tmp);
Since no one mentioned sprintf
you can just convert any variable to char* using it and send
if(strcmp(buf,"movUP") == 0)
{
char* msg = calloc(1, 20);
pos.y += 0.0001f;
sprintf(msg,"NEW::POS::Y=%.4f", pos.y);
sendto(master, msg, 20, 0, (struct sockaddr*)&client, addrlen);
}
Test
movUP
NEW::POS::Y=0.0001
movUP
NEW::POS::Y=0.0002
movUP
NEW::POS::Y=0.0003
movUP
NEW::POS::Y=0.0004
Use %d for integers, %f for floats
to convert back to an integer, use atoi(char*)
to convert back to an float, use atof(char*)
before converting, be sure to use strstr() to get the float value only, starting from "0"
float myPos; // floating variable that stores Object's Position in the World
...
....
memset(buf, 0, MAXBUFFER); // clears the previous buffer
recvfrom(master, buf, MAXBUFFER, 0, (struct sockaddr*)&server, &addrlen);
char* newY = strstr(buf, "0");// NEW::POS::Y=0.0001 --->> 0.000100
myPos = atof(newY); // new object's position by the server
printf("My New Position is %.4f\n", myPos); // Out: My New Position is 0.0011 -> 0.0012 -> 0.0013 -> 0.0014.
For integers (not positions), you can use the same technique and just multiply it like
float f = 0.000002f; // that is supposed to be 2 integer value
int i = (int)(f*1000000); // now, i = 2
the above methods are totally secure
If you want a more solid converting, you can use strncpy or memcpy and cut the string starting from a given index with some length assuming that you already know the incoming buffer, but in my personal view, I don't really recommend it, specifically in connectionless sockets like this one, lots of calculations and calls for buffer length, Not easy to debug sometimes if you're not totally aware of what you're doing.
Note 1: Be careful to not include zeros in your buffer when you're waiting for a server move/position command or any
quantity variable if you're planning to use the 1st method.
Note 2: You can just send your integer or float, convert it and vice versa without needing to cut or multiply it.
May new game networking developers find this answer useful too since we can't send or receive except char* with UDP sendto(), recvfrom().
I'm working on an assignment for my Distributed Systems class. I'm a master's student in C.S., but my specialty in programming is .NET and I'm working on a project that requires some fairly involved Unix knowledge, which is tripping me up.
The assignment is implementing a flush channel protocol API. So I'm coding a small function library that other apps can implement to use flush channel communication. I've set it up so that when the init function is called, it forks a child process to act as the server for incoming messages. The child communicates with the parent process by sending incoming data to the parent through a pipe.
This works OK if messages are sent and received one at a time; e.g.,
send -> receive -> send -> receive -> etc.
However, if multiple messages are sent before doing any receives; e.g.,
send -> send -> send -> receive
then it gets messed up. Specifically, the first message is received correctly, but when I go to receive the second message, the program hangs and needs to be killed. I've done a lot of searching online and been plugging away at this for hours but haven't made much progress.
The program as a whole is far too large to show here, but here are the most relevant bits. Here's the part where I get the server going and receive messages. Note the line
write(fd[1], buffer, (strlen(buffer)+1));
-- I think that's a good candidate for being the source of the problem here, but not sure what to do differently. (Tried fwrite() and that didn't work at all.)
fd = malloc(2 * sizeof(int));
int nbytes;
if (pipe(fd) < 0) {
perror("Could not create pipe");
return -1;
}
pID = fork();
if (pID < 0) {
perror("Failed to fork");
return -1;
} else if (pID == 0) { // child
close(fd[0]); // close input side of pipe
int cc;
int fsize;
struct sockaddr_in from;
int serials[500];
int i;
for (i = 0; i < 500; i++) serials[i] = 0;
char buffer[2048];
while (1) {
fsize = sizeof(from);
cc = recvfrom(socketNo, buffer, 2048, 0, (struct sockaddr*)&from, &fsize);
if (cc < 0) perror("Receive error");
datagram data = decodeDatagram(buffer);
if (serials[data.serial] == 0) {
write(fd[1], buffer, (strlen(buffer)+1));
serials[data.serial] = 1;
}
}
} else { // parent
close(fd[1]); // close output side of pipe
return 0;
}
(The "serials" array is for not forwarding repeated messages, as messages are sent multiple times to improve reliability. I know a fixed size for this array is not good practice, but the tests for this assignment don't send that many messages so it's OK in this context.)
The beginning of the receive function looks like this:
int fRecv(int* id, char* buf, int nbytes) {
checkDatagramTable(*id);
char* tbuf = malloc((nbytes + 9) * sizeof(char));
int rbytes = read(fd[0], tbuf, nbytes + 9);
The "+9" is to accommodate additional information that gets packaged along with the message to be sent, for flush channel ordering. This is also a pretty sketchy area, but allocating more space to be extra sure has not helped the issue.
I know there's quite a bit of extraneous stuff in here, references to other functions etc. But the problem surely lies in how I'm piping the data through, so the source of my issue should lie there somewhere.
Thanks in advance for your assistance; it is truly appreciated.
This looks suspicious. (what is in the packets? They could be binary) Where is the typedefinition for datagram ?
fsize = sizeof(from);
cc = recvfrom(socketNo, buffer, 2048, 0, (struct sockaddr*)&from, &fsize);
if (cc < 0) perror("Receive error");
datagram data = decodeDatagram(buffer);
if (serials[data.serial] == 0) {
write(fd[1], buffer, (strlen(buffer)+1)); // <-- ????
serials[data.serial] = 1;
}
I'd try instead:
write(fd[1], buffer, cc);
UPDATE:
If the message is not null terminated, you'll have to terminate it explicitly:
(if cc == 2048) cc -= 1;
buffer [cc] = '\0'; // <<--
datagram data = decodedatagram(buffer);
...
Also, it is advisable to use "sizeof buffer" instead of "2048".
UPDATE2:
You could test if the strings in the packets are really null-terminated by:
unsigned pos;
cc = recvfrom(socketNo, buffer, 2048, 0, (struct sockaddr*)&from, &fsize);
if (cc < 0) perror("Receive error");
for pos=0; pos < cc; pos++) {
if (buff[pos] == 0) break;
}
switch (cc-pos) {
case 0: fprintf (stderr, "No nul byte found in packet: I lose!\n" ); break;
default: fprintf (stderr, "Spurious nul byte found in the middle of packet\n" );
case 1: break;
}
datagram data = decodeDatagram(buffer);
if (serials[data.serial] == 0) {
write(fd[1], buffer, cc);
serials[data.serial] = 1;
}