How do open child sockets get closed? - c

I have spawned a child process on a while(1) loop that appears to accept ongoing TCP connections. I am looking for confirmation of 'normal' behaviour of a socket child process, assuming the parent process is not dead (I found plenty of advice on how to kill child processes when the parent is dead...).
void
dostuff (int sock, int* count)
{
while(1){
*count = *count +1;
char cnt[20];
sprintf(cnt, "count: %d\n", *count);
int n;
char buffer[bufsize];
bzero(buffer,bufsize);
n = read(sock,buffer,bufsize);
if (n < 0) error("ERROR reading from socket");
fprintf(stderr,"From the client:\n%s",buffer);
char reply[bufsize];
bzero(reply, bufsize);
strcat(reply, cnt);
n = write(sock, reply, bufsize);
if (n < 0) error("ERROR writing to socket");
}
}
I suspect the child process will automatically close when its TCP session is finished, is this correct?
It appears when reconnecting to its current TCP session, the child in effect start at the top of the while loop, incrementing count before reading and writing. Can I rely on this behaviour? Or make it reliable?

So first coding problems: the fprintf(stderr,"From the client:\n%s",buffer);. Even if buffer is inited with zeros you may get bufsize bytes, so the fprintf will read data after end of buffer. And you are in a loop so each read could gives a different size, so you may whatever print "garbage" from previous buffer content.
You should use functions that takes a number of chars to write (i.e. fwrite).
An other point is more a "style" problem: you get n bytes from your read, so why writing back bufsize bytes and not n? The only advantage is to have "constant size" messages.
For the inner loop none of the functions in it are able to leave this loop, or the function, or the program.
You must explicitly leave the loop (using break) or the function (using return) or even the program (with exit), depending on your needs.
In its current state the function reads from the socket. It then prints an error message but continues its job (it is up to you to decide what to do in case of read error). After it prints the message (probably the previous one as you don't clean content) and writes to the socket, which will fail (if the socket is closed, you can't read, you can't write).
So here the main point for you: trying to read/write from/to a closed socket just returns -1 from read/write functions. It's up to you to decide what to do after that.
The function should looks like:
void dostuff (int sock, int* count) {
while(1){
int n;
*count = *count + 1;
char buffer[bufsize];
n = read(sock,buffer,bufsize);
if (n <= 0) {
error("ERROR reading from socket (or EOF)");
close(sock); // close our side whatever
break; // leave the loop (or return)
}
fprintf(stderr,"From the client:\n");
fwrite(buffer, 1, n, stderr); // write the n elements
char reply[bufsize];
reply[0] = '\0'; // no need to clear all buffer
strcat(reply, cnt);
n = write(sock, reply, n); // just write N elements
if (n < 0) {
error("ERROR writing to socket");
close(sock); // close our side whatever
break; // not able to write -> problem
}
}
}

Related

Read the characters of an executable for file transfers in C

So recently for a course project, I decided to make myself a program that could transfer a file across a lan network and integrate it into the linux operating system (In this case, all I did was add it to the context menu) using a socket server.
The way it works is essentially,
Server is waiting.
Client connects.
Client sends a message of 1024 length with the first 4 characters reserved
The first 4 characters are used to store an int which will state the length of the message
server recieves them, writes them, then waits for the next block
when the server recieves a message where the length is 0
it ends the transfer and closes the files
This works for text files flawlessly. With improvements on my last code thanks to helpful feedback, I've managed to create something where the OS actually recognizes the file extension, regardless of the type. However for things like pngs they show up black, for exe's they immediately segfault.
What can I change in my reading and writing to get this to work regardless of file type? I'm not sure where to go, as what I have should work
Additional info: I am coding in C. To open the file I use fopen, fgetc and fputc.
Here is an exert from my code for my sever:
while (1){
n = read(newsockfd,message,1024);
if (n < 0) {
fclose(fptr2);
error("ERROR reading from socket");
}
//The first 4 bytes/characters are used to store the length.
//I read them by getting a pointer to the first char and then reading it as
//an int by casting it. This works with no problem
char *p=&message;
int *p2=(int*)p;
int length=*p2;
//Checks if the length is 0, if so, exit
if (length==0)
break;
//writes to the file
for (int i=4;i<length;i++){
fputc(message[i], fptr2);
}
n = write(newsockfd,"Ready",5);
if (n < 0)
error("ERROR writing to socket");
bzero(message,255);
}
fclose(fptr2);
//n = write(newsockfd,"I got your message",18);
//if (n < 0) error("ERROR writing to socket");
printf("Done.\n");
return 0;
}
Exert from my client, which reads the file in and then sends it.
while (finished!=0&&c!=EOF)
{
for (int i =4;i<1024;i++)
{
if (c==EOF)
{
char* p=&message;
int* pi=(int*)p;
*pi=i;
finished=0;
//printf("length is:%d\n",i);
break;
}
//printf("%c",c);
message[i]=c;
//fputc(c, fptr2);
c = fgetc(fptr1);
}
if (finished!=0)
{
char* p=&message;
int* pi=(int*)p;
*pi=1024;
}
n = write(sockfd,message,1024);
if (n < 0)
{
fclose(fptr1);
error("ERROR writing to socket");
}
bzero(message,1024);
//reading
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
}
0x00 is a valid character for a binary file, so you can't "stop" when you see one [like you can for a string].
You are using the first char of a packet as an EOF marker (i.e. non-zero means valid data and zero means EOF). But, note that the first data char in a packet could be zero, so you have to use a one byte "header" that doesn't have data chars in it, merely the "stop" flag char [if you will]:
while (1) {
// Reading
n = read(newsockfd, message, 1023);
if (n < 0) {
fclose(fptr2);
error("ERROR reading from socket");
}
// Checks if the first character is null, if so, exit
if (message[0] == 0)
break;
// writes to the file
// NOTE: now the data starts at offset 1!
int i = 1;
for (; i < n; ++i) {
fputc(message[i], fptr2);
}
i = 0;
n = write(newsockfd, "Ready", 5);
if (n < 0)
error("ERROR writing to socket");
bzero(message, 1023);
}
fclose(fptr2);
But, a simpler way is to just read until the length comes back zero:
while (1) {
// Reading
n = read(newsockfd, message, 1024);
// end of data
if (n == 0)
break;
// error
if (n < 0) {
fclose(fptr2);
error("ERROR reading from socket");
}
// writes to the file
fwrite(message,1,n,fptr2);
i = 0;
n = write(newsockfd, "Ready", 5);
if (n < 0)
error("ERROR writing to socket");
bzero(message, 1023);
}
fclose(fptr2);
In short, the most important change to get this to work is changing when the program stops reading. EOF is simply -1, in a text file, this is no problem, however in other files this value can be found potentially anywhere. In order to read in the characters properly you must first get the file length, then simply read characters until you reach that.
fseek(fptr1, 0L, SEEK_END);
int sz = ftell(fptr1);
rewind(fptr1);
int count=0;
while (count!=sz)
{
//other code left out for simplicity
c = fgetc(fptr1);
count++;
}
With this change, my program works properly.

pthreads: wait until read() returns a value > 0

I'm working on a client program that will operate as a basic instant messenger. I'm using pthread to to open up a thread dedicated to waiting for a message to be received and the the message to be read. Is using pthread_cond_wait the correct way to go about waiting for read(sockfd, buffer, 256) to be above 0?
void *threadRead() {
while (1) {
bzero(buffer,256);
pthread_cond_wait(&buffer_lock, read(sockfd, buffer, 255) > 0);
n = read(sockfd, buffer, 255);
printf("%s\n",buffer);
}
}
You see I just need to wait until read() comes back with a value above 0 to continue and I can't find the right system to do that. If anyone could link something that would put me on the right track or give me a hint that would be great.
No. pthread_cond_wait() is for waiting on a condition that will be changed by one of your other threads.
If you just want to wait for read() to return something, just call read(). Unless you have specifically marked the socket as non-blocking, it will block the calling thread until there is something to return.
If read() ever returns 0 then it indicates end of file: it means that the socket has been closed on the remote side, so there will never be any more to read.
You should use select() instead, like this
int running;
running = 1;
while (running != 0) /* Just in case you want to end the loop, you can */
{
fd_set rdset;
struct timeval timeout;
timeout.tv_sec = NUMBER_OF_SECONDS_TO_WAIT;
timeout.tv_usec = YOU_CAN_HAVE_MICRO_SECONDS_PRECISION;
FD_ZERO(&rdset);
FD_SET(fd, &rdset);
if (select(fd + 1, &rdset, NULL, NULL, &timeout) == 1)
{
ssize_t length;
char buffer[100];
length = read(fd, buffer, sizeof(buffer));
/* use buffer now */
}
else
{
/* Timed out and still nothing to read */
/* do something meanwhile and retry if */
/* you want to. */
}
running = use_a_function_to_check_this();
}
you can use it in a different thread, but you need to be careful.
Non-blocking IO is difficult, it doesn't matter how you implement it is hard.
One more thing, this
n = read(sockfd, buffer, 255);
printf("%s\n",buffer);
is likely undefined behavior, since apparently buffer is
char buffer[256];
you could
n = read(sockfd, buffer, 255 /* or sizeof(buffer) - 1 */);
buffer[n] = '\0';
printf("%s\n",buffer);
ensuring that buffer is nul terminated.

Writing send_all and recv_all functions for sockets in C

I am a new C programmer and so you will have to excuse my lack of knowledge. I am trying to use sockets in C on a windows machine to send data back and forth between a client and server. I am using the tools of cygwin with the codeblocks IDE. Simple send and receives were not working and so after some searching I was under the impression my problem was I needed a send_all and recv_all function. I have written the following two functions but receive seems to always get stuck in an infinite loop. I am not really sure why.
void send_all(int socket, void *buffer, int length) {
size_t i = 0;
for (i = 0; i < length; i += send(socket, buffer, length - i, 0)){
printf("Completed: %d bytes \r", i);
}
printf("Send Completed: %d bytes \n", length);
}
void recv_all(int sockfd, void *buffer, int length){
size_t i = 0;
for (i = 0; i < length; i+= recv(sockfd, buffer + i, length - i, 0)){
printf("Completed: %d bytes \r", i);
}
printf("Receive Completed: %d bytes \n", length);
}
I am wondering if it is because the receive doesn't know how many bytes the send is sending it. All advice is appreciated but please keep it constructive. Thanks.
recv() actually returns a signed value (int in Winsock, ssize_t in POSIX). Its return value can be a negative number if a read error occurred, OR if the socket is in non-blocking mode and no data is available. Its return value is zero if the socket was closed gracefully (this would cause an infinite loop in your code).
You will need to check the return value before you add it to your byte counter, to detect both of these conditions.
If your socket is in blocking mode (the default), your code will block indefinitely until the required amount of data has been received, or an error occurs (once you add code to check for that). Given the name of your function this seems to be the behavior you want. If so, your general approach is sound.
ssize_t bytesRead = 0;
while (bytesRead < length)
{
ssize_t rv = recv(/* ... */);
if (rv == 0)
{
printf("Socket closed gracefully before enough data received\n");
break;
}
else if (rv < 0)
{
// if your socket is non-blocking, check for EAGAIN which
// would mean no data is currently available; in this case you
// could do something like call select() on the socket to
// go to sleep until more data comes in
printf("Read error occurred before enough data received\n");
break;
}
bytesRead += rv;
}

Send a File with socket in C for Linux

I'm writing a small and simple server (in C language for Linux stations).
A client requests a file to my server, my server asks this file to another server which sends it to my server.
My server should NOT receive ALL the file before sending it to the client BUT must send the bytes of the file so as they arrive.
This is an exercise in school so I can not dissociate myself from this requirement.
I have implemented the function explained below. The problem is that the client receives a non-deterministic number of bytes and NEVER the entire file.
int Recv_and_send_file (int socketa, int socketb, char *buffer, size_t file_size){
size_t n;
ssize_t nread;
ssize_t nwritten;
char c;
for (n=1; n<file_size; n++)
{
nread=recv(socketa, &c, 1, 0);
if (nread == 1)
{
nwritten = send(socketb,&c,1,0);
}
else if (nread == 0)
{
*buffer = 0;
return (-1); /* Errore */
}
else
return (-1); /* Errore */
}
}
*buffer = 0;
return (n);
}
Someone could kindly tell me where I'm wrong?
Is it an stupid idea to change the values ​​SO_SNDBUF and SO_RCVBUF on both the server and the client?
Assuming the file_size is the total number of bytes you want to send, then your for loop will only send file_size - 1 bytes. In other words, you are off by one. Start from 0 instead to fix this:
for (n=0; n<file_size; n++)
{ //..
You capture the return value of send(), but you do not check to see if it was successful or not.
You are treating a 0 return value from recv() the same as an error. Since you do not show what you do after returning -1 from your function, I don't know if this may be contributing to your problem or not.
Certain errors on send() and recv() are "soft", in that you are allowed to retry the operation for those particular errors. One such error is EINTR, but check the documentation on your system to see if there are others.
In order to optimize performance and simplify your code, you can use splice()+pipes. Sendfile enables you to "forward" data between file descriptors, without the copy to user space.
Are you sure you have copied the correct code? That part as it is would not compile, there is a } in the last else which don't match with a corresponding {.
Also, how you get to know the file size? if it's send thru the socket as an integer, bear in mind the possible byte order of the source and destination machines.
Anyway, you are reading one byte at a time, you should improve it this way:
EDIT: use buffer and not the extra buff[2048];
int Recv_and_send_file (int socketa, int socketb, char *buffer, size_t file_size){
ssize_t nread;
ssize_t nwritten;
ssize_t bLeft=file_size;
while (bLeft > 0)
{
nread=recv(socketa, buffer, bleft, 0);
if (nread > 0)
{
nwritten = send(socketb, buffer, nread, 0);
bLeft -= nread;
buffer+=nread;
}
else if (nread == 0)
{
// I think this could raise a memory exception, read below
*buffer = 0;
return (-1); /* Errore */
}
else
{
return (-1); /* Errore */
}
}
// If buffer is allocated with file_size bytes this one will raise a memory exception
// *buffer = 0;
return (file_size-bLeft);
}

Trying to pipe data from a child-process server to its parent process

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;
}

Resources