How to use recv to recieve long text strings - c

I am trying to create a server and client program that sends a string from client to server where the server executes that string and sends the output back to the client. I am doing this in linux and I am very confused why my program isnt working the least bit. Here is the code.
**Client**
int main()
{
//Code to use unix socket here
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
perror("connect");
exit(1);
}
printf("Connected.\n");
while(printf("> "), fgets(str, MAX, stdin), !feof(stdin)) {
if (send(s, str, strlen(str), 0) == -1) {
perror("send");
exit(1);
}
}
done=0;
do {
if(t=recv(s, str, MAX, 0)<0)
perror("recv failed at client side!\n");
str[t] = '\0';
if(strcmp(str, "ENDOFTRANS")==0)
{
printf("\nRead ENDOFTRANS. Breaking loop.\n");
done=1;
}
printf("Server > %s", str);
} while(!done);
}
And then the server code is:
**Server**
#define MAX 1000
int main(void)
{
//Unix socket code
//This process is now a daemon.
daemon();
//Listens for client connections, up to 5 clients can queue up at the same time.
if (listen(s, 5) == -1) {
perror("listen");
exit(1);
}
for(;;) {
int done, n, status;
printf("Waiting for a connection...\n");
t = sizeof(remote);
if ((newsock= accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
done = 0;
do {
switch(fork())
{
case -1: //ERROR
perror("Could not fork.\n");
break;
case 0: //CHILD
//Accept string from client.
//Edit: Why am I getting an error here? says: Invalid argument.
if(n = recv(newsock, str, MAX, 0)) {
perror("Recv error at server side.\n");
exit(1);
}
str[n]='\0';
if (n <= 0) {
if (n < 0)
perror("recv");
done = 1;
}
printf("String=>%s<",str);
//Redirect socket to STDOUT & STDERR.
test = close(WRITE); assert(test==0);
test = dup(newsock); assert(test==WRITE);
test = close(ERROR); assert(test==0);
test = dup(newsock); assert(test==ERROR);
if (!done)
{
if (str==something)
{
//execute command
}
else {
//Fork and execvp the command
}
//Sends End of Transaction character.
ENDTHETRANS();
exit(0);
}
break;
default: //PARENT
//Parent keeps accepting further clients.
wait(&status);
if ((newsock= accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
done=1;
break;
}
} while (!done);
}
close(s);
}
Im relatively new to programming in general and from my understanding the client code is good except that when it recieves the text back from the server it only recieves the text in small bits (2 rows at a time). I have to keep pressing enter on client promt to get the rest of the input. I have tried so many things that by this point I dont even know what I am doing wrong anymore.
Firstly, in the server code, after it recieves the string from the client I have a printf("String=>%s<",str); that outputs the string. However when the server prints the output as String=>ls -l the < key at the end gets eaten up somehow. It shouldnt be doing that right?
Any help much appreciated. Please bare in mind that I am a beginner and have only used pipes as inter process communcation before. Now I wanna make my first unix socket program.
Thanks in advance.

The usual problem in cases such as this is not realizing that SOCK_STREAM sockets don't preserve message boundaries. So data sent with a send call might be split up and received in multiple recvs, or it might be coalesced and multiple sends end up in a single recv. Most importantly, when a kernel send buffer fills up, a send call might write partial data (sending only some of the requested data) and return a short return value. You need to test for this and resend the rest of the data.
Another problem that often shows up is issues with line endings (particularly when talking between linux and windows). There may be extra carriage return characters (\r) in the either the client or server that confuse the other side. These tend to result in apparently missing or truncated output when printed.
edit
The line
if(t=recv(s, str, MAX, 0)<0)
is equivalent to
if(t = (recv(s, str, MAX, 0)<0))
that is, it sets t to 0 or 1 depending on whether there was an error or not. As with most errors of this type, turning on warnings will give you some indication about it.

Related

Stuck in while when transfer file through socket using TCP

I write program and it works fine, but i want to rewrite it using sendfile() and now i got stuck in a loop.
Server side:
send name = ok
send md5 checksum = ok
send size = ok
send file = ko
Client side:
recv name = ok
recv md5 cecksum = ok
recv size = ok
create dir and create file = ok
write data to created file = ko
P.S In previous version of program i stuck some time to, but it depend how much i use printf why? for e.x i add one line with printf program stuck, delete it, works fine.
UPDT: rewrite code client/server
client
/* Received file name */
int rc_byte = 0;
rc_byte = recv(fd, rx_tx_file->out_name, sizeof(rx_tx_file->out_name),0);
if (rc_byte < 0){
perror("Failed to receive file name: ");
exit(-1);
} else
printf("Recv out name %s\n", rx_tx_file->out_name);
//printf("file name rc %s\n", rx_tx_file->out_name);
trimm_path_name(rx_tx_file);
/* Received md5sum */
rc_byte = recv(fd, rx_tx_file->md5sum, sizeof(rx_tx_file->md5sum), 0);
if (rc_byte < 0) {
perror("Failed to receive check sum: ");
exit(-1);
} else
printf("recv md5s %s\n", rx_tx_file->md5sum);
/* Received file size */
rc_byte = recv(fd, &size, sizeof(size), 0);
if(rc_byte < 0) {
perror("Recevid size of file: ");
exit(-1);
}
printf("%d recv size\n", size);
to_read = size;
if (stat(dir, &st) == -1){
mkdir(dir, 0777);
}
send_data: (add func to server)
void send_data(int client_fd, m_file *rx_tx_file, int option, int size) {
int send_byte = 0;
int total_send = 0;
if (option == SEND_NAME) {
while (total_send < strlen(rx_tx_file->in_name)) {
send_byte = send(client_fd, rx_tx_file->in_name, sizeof(rx_tx_file->in_name),0);
if(send_byte == -1) {
perror("Failed to send file name to client: ");
exit(SEND_TO_CLIENT_ERROR);
}
total_send += send_byte;
}
}
else if (option == SEND_MD5) {
total_send = 0;
send_byte = 0;
while (total_send < strlen(rx_tx_file->md5sum)) {
send_byte = send(client_fd, rx_tx_file->md5sum, sizeof(rx_tx_file->md5sum),0);
if(send_byte == -1){
perror("Failed to send file md5sum to client: ");
exit(-1);
}
total_send += send_byte;
}
}
else if (option == SEND_SIZE) {
send_byte = send(client_fd, &size, sizeof(size),0);
if (send_byte == -1) {
perror("Failed to send size: ");
}
}
}
server:
client_fd = accept(server_fd, (struct sockaddr*) &client_addr, &length)
/*send name of file*/
send_data(client_fd, rx_tx_file, SEND_NAME, 0);
/*send md5 sum*/
take_check_sum(rx_tx_file,rx_tx_file->file_in, 0);
send_data(client_fd, rx_tx_file, SEND_MD5, 0);
/*send size of file*/
size = stats.st_size;
send_data(client_fd, rx_tx_file, SEND_SIZE, size);
remain_data = stats.st_size;
printf("File [%s] ready to send\ncheck sum [%s]\n", rx_tx_file->in_name,rx_tx_file->md5sum);
while (((send_byte = sendfile(client_fd, file_fd, &offset, size)) > 0) && (remain_data > 0))
{
remain_data -= send_byte;
printf("remain %d", remain_data);
}
printf("Succesfully");
Since i work with one client and pass file which should send on server side through command line args, i dont need to wait in while (client_fd = accpet) i just work with one connection and close server. Now its work good. But one question is open, how i should rewrite client side to recv data in a loop. I don't know which size i should recv and because of that i cant write right condition to my while loop. THX all for helping.
TCP is a stream. It has no message boundaries. Your code won't work because of that.
First, you send the name of the file:
send(client_fd, rx_tx_file->in_name, strlen(rx_tx_file->in_name)+1,0)
then you immediately send the md5 sum and then the file size:
send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0)
send(client_fd, &size, sizeof(int),0)
Since the first two strings don't have a fixed number of bytes, it's quite likely that when you try to read the file size or md5 sum from the server you also read the size of the file and maybe even some of the file data.
First, stop trying to put as much of your send and read code as you can into the conditional clause of your if and while statements.
What exactly does
if (send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0) == -1) {
perror("Failed to send file md5sum to client: ");
exit(-1);
}
gain you over
ssize_t bytes_sent = send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0);
if ( bytes_sent < 0 )
{
perror("Failed to send file md5sum to client: ");
exit(-1);
}
Putting all that code into the if clause gains you nothing on the send. And what if strlen(rx_tx_file->md5sum)+1 is 87 and the send() call returns 15? That's a possible return value that your code can't handle because it stuffs everything into the if clause.
ssize_t bytes_sent = send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0);
if ( bytes_sent < 0 )
{
perror("Failed to send file md5sum to client: ");
exit(-1);
}
else if ( bytes_sent < strlen(rx_tx_file->md5sum)+1 )
{
// partial send...
}
That's actually better coded as a loop.
You didn't post your receive code, but if it's in the same style you not only don't gain anything, by putting everything into the if clause you again can't do any decent error detection or correction.
If your file name recv code is similar to
char filename[1024];
if (recv(fd, &filename, sizeof(filename), 0) < 0) {
perror("Failed to read file name: ");
exit(-1);
}
you can't tell what you just received. How many bytes did you just receive? You may have received the file name. You may have received only part of the file name. You may have received the file name, the md5 sum, and some of the file contents itself.
You don't know what you received, and with your code you can't tell. If you zero out the file name and md5 receive buffers and only recv up to one byte less than the size of the buffer, you at least avoid undefined behavior. But if you don't zero out the buffer, or if you read up the the last byte of the buffer, you can also wind up without a nul-terminated string for your filename or md5 sum. And when you then try to treat it as a nul-terminated string you get undefined behavior.
And if you did get extra bytes in the recv calls you make before trying to read the file data, that explains why your code gets stuck - it already read some of the file contents before getting to the loop, so the loop will never see all the content - some of it is gone.
You should avoid using strlen here in your server:
if(send(client_fd, rx_tx_file->in_name, strlen(rx_tx_file->in_name)+1,0) == -1)
Rather just send fixed length string of size sizeof(rx_tx_file->out_name) as you expect in your client
If the filename is smaller just pad it with spaces to make it of length sizeof(rx_tx_file->out_name)
You should also put each receive call in while loop, and add checks that it actually received expected number of bytes, at times recv will just return partial data, you need to post another recv to receive rest of the expected data

UNIX Domain socket programming in C, printing issue

I am having a printing issue with my server. I want there to be simultaneous printing when I have 2 or more clients active on terminals. However, I am only printing from one client at a time. Once I close a client, the other clients are free to write to the server. What can I do to fix my problem?
I have tried to fork the printing section, which I think didn't really do anything. (Just realized if I do this, then the select system call is a waste, i'd rather use the select system call) *edit
while(TRUE) {
FD_ZERO(&readfds);
FD_SET(socket1, &readfds);
FD_SET(socket2, &readfds);
FD_SET(socket3, &readfds);
select(socket3+1, &readfds, NULL, NULL, NULL);
//add socket1
if(FD_ISSET(socket1, &readfds)) {
if((client_socket1 = accept(socket1, NULL, NULL)) < 0) {
perror("accept1");
exit(EXIT_FAILURE);
}
printf("New Connection\n");
puts("Welcome message1 sent successfully\n");
}
//add socket2
if(FD_ISSET(socket2, &readfds)) {
if((client_socket2 = accept(socket2, (struct sockaddr *)&addr2, (socklen_t*)&addr2)) < 0) {
perror("accept2");
exit(EXIT_FAILURE);
}
printf("New Connection\n");
puts("Welcome message2 sent successfully\n");
}
//add socket 3
if(FD_ISSET(socket3, &readfds)) {
if((client_socket3 = accept(socket3, (struct sockaddr *)&addr3, (socklen_t*)&addr3)) < 0) {
perror("accept3");
exit(EXIT_FAILURE);
}
printf("New Connection\n");
puts("Welcome message3 sent successfully\n");
}
//print from socket 3
while( (ready = read(client_socket3, buffer, sizeof(buffer))) > 0) {
printf("%s\n", buffer);
}
//print from socket 2
while( (ready = read(client_socket2, buffer, sizeof(buffer))) > 0) {
printf("%s\n", buffer);
}
//print from socket 1
while( (ready = read(client_socket1, buffer, sizeof(buffer))) > 0) {
printf("%s\n", buffer);
}
}
You need to add your client sockets to the fd_set and select statement before attempting to read from them. Also, you should make all your sockets non-blocking. Otherwise, the read call will block until you get data.
Here's a quick fix that uses recv instead of read to read the sockets, but with the async flag of MSG_DONTWAIT.
I didn't see anywhere where you were closing your client sockets or handling errors properly. So I inserted some code as a hint. Also, it's never a good idea to "printf" a buffer of data from a socket directly. Because you never know if the data you received is null terminated. Always null terminate your buffer after you read the data off the socket.
Change this block of code:
//print from socket 3
while( (ready = read(client_socket3, buffer, sizeof(buffer))) > 0) {
printf("%s\n", buffer);
}
To this:
while (1)
{
int result;
result = recv(client_socket3, buffer, sizeof(buffer)-1, MSG_DONTWAIT);
if ((result == -1) &&
((errno == EAGAIN) || (errno==EWOULDBLOCK)) )
{
// no more data available, but could be available later
// use the socket with "select" above to wait for more data
}
else if ((result == -1) || (result == 0))
{
// remote close or unrecoverable error
close(client_socket3);
client_socket3=-1;
}
else
{
// null terminate the buffer before printing
buffer[result] = '\0';
printf("%s\n", buffer);
}
}

C Socket Server, memory increases with file writes

I have a simple server program written in C, and the program is running on an Ubuntu Linux distribution. The program is intended to listen for messages sent from the client, write those messages to a file (each message goes into a separate file), and send an acknowledgement back to the client once the message has been received and stored.
I have noticed that, as the server continues to receive and store messages, the amount of available system memory quickly decreases and continues to decrease until messages have stopped. Memory remains constant when no messages are being sent. However, I have also noticed that I can free up the memory again by deleting the written files from disk (I can do this even while the server is still running). I am therefore led to believe that the memory issue has something to do with my file operations, though I can't see any issue with the code that writes the files.
Can someone help?
NOTE: I am observing the memory usage with "top".
I have included an excerpt from the program. The below function handles input from the client and writes that information to file. This is where I currently believe the problem to be:
void handleinput (int sock)
{
char filename[strlen(tempfolder) + 27];
generatefilename(filename);
int rv;
int n = 1;
int received = 0;
char buffer[BUFFER_SIZE];
FILE *p = NULL;
fd_set set;
char response[768];
struct timeval timeout;
timeout.tv_sec = 360;
timeout.tv_usec = 0;
FD_ZERO(&set);
FD_SET(sock, &set);
bzero(buffer, BUFFER_SIZE);
bzero(response, sizeof response);
rv = select(sock + 1, &set, NULL, NULL, &timeout);
if (rv == -1)
{
error("error on select in handleinput");
close(sock);
exit(1);
}
else if (rv == 0)
{
close(sock);
exit(0);
}
else
{
n = read(sock, buffer, BUFFER_SIZE-1);
if (n <= 0)
{
close(sock);
exit(0);
}
}
// open file
if (n != 0)
{
p = fopen(filename, "a");
if (p == NULL)
{
error("ERROR writing message to file");
close(sock);
exit(1);
}
}
// loop until full message is received
while (n != 0)
{
if (n < 0)
{
error("ERROR reading from socket");
close(sock);
exit(1);
}
received = 1;
// write content to file
fwrite(buffer, strlen(buffer), 1, p);
if (buffer[strlen(buffer)-1] == 0x1c)
{
break;
}
bzero(buffer, BUFFER_SIZE);
rv = select(sock + 1, &set, NULL, NULL, &timeout);
if (rv == -1)
{
error("ERROR select in loop in handleinput");
close(sock);
exit(1);
}
else if (rv == 0)
{
close(sock);
exit(0);
}
else
{
n = read(sock, buffer, BUFFER_SIZE-1);
}
}
// close file if we opened it earlier
if (p != NULL)
{
fclose(p);
}
// send acknowledgement back to client
if (received == 1)
{
generateResponse(response, filename);
n = write(sock, response, strlen(response));
if (n < 0)
{
error("ERROR writing to socket");
close(sock);
exit(1);
}
}
}
Its because of the caching mechanism of writing. IF you have a lot of clients trying to write files, the IO buffer fills in kernel memory, but doesn't actually write to that file until you close the socket or the buffer fills. You can fix this just by flushing the buffer. What some other people have suggested is to get rid of the write and read wrappers in stdio, and just use the kernel call write, since this will help performance and will probably flush the buffer automatically.
You can flush with fsync() btw.

select() not responding?

The objective of my program is to use select to manage multiple sockets. However, I thought of trying it with one socket first. Now, the problem that I am facing is that initially client sends data to server, and server receives it and displays it, but then when client again sends some data, the server code remains still at select command.
here are some snippets that will give you an idea of how I am initializing the socket.
if((master_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
exit(1);
}
if((bind(master_socket, (struct sockaddr *)&req, sizeof(req))) < 0)
{
exit(1);
}
listen(master_socket, 5);
FD_SET(master_socket,&listening);
/* wait for connection, then receive and print text */
len = sizeof(struct sockaddr);
while(1)
{
FD_ZERO(&listening); //Flush out everything in socket
FD_SET(master_socket,&listening); // Add master
if(f_client>0) // Add client if any
{
FD_SET(f_client,&listening);
}
printf("Checking for new connection \n");
//Timeout is null, so waiting indefinitely
rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
if (FD_ISSET(master_socket, &listening))
{
printf("Master side invoked\n");
if((f_client = accept(master_socket, (struct sockaddr *)&req, &len)) < 0)
{
exit(1);
}
}
else if (FD_ISSET(f_client,&listening))
{
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
}
}
Basically in above program, it connects to the server, maintains a file descriptor for client f_client and add it. And in every round, it clears the socket, add master socket, and client socket if any, and then checks. Problem here is, first time it works, but second time when client sends some data. it gets hang to rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
I am not to understand what is wrong here. Can anyone help?
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
This code is broken. The fputs function can only be used with a C-style string. You just have arbitrary data with no particular structure. Since you ignore valread, you also have no idea how many bytes you read. (Think about it, how could fputs possibly know how many bytes to output? That information is only in valread, and you don't pass it that information.)
You've already received the data, this broken code just threw it away. If you log valread, you'll see that you actually already read it in your last call to read before the call to select that hung.
instead of fputs, you could use something like this:
for (int i = 0; i < valread; ++i)
putchar(buf[i]);

C: Question about Beej's networking guide... Is there an assumption here?

I was just going through the Networking Guide by Beej and am curious about this part of the code (specifically marked with "From here" and "To here"):
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1) {
perror("accept");
} else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
printf("selectserver: new connection from %s on "
"socket %d\n",
inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),
newfd);
}
} else {
// handle data from a client
//----------------- FROM HERE --------------------------
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
} else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
//----------------- TO HERE ----------------------------
} else {
// we got some data from a client
for(j = 0; j <= fdmax; j++) {
// send to everyone!
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)--and you thought it would never end!
return 0;
Now I know that read doesn't always read "everything" that is to be read on a socket and that it sometimes can return only part of it. In that case, wouldn't this code be incorrect? I mean, after one read, the connection is being closed... Instead, aren't we supposed to have some other mechanism in place? If so, what is the right approach here?
The socket is only going to get closed there if there was an error from recv(), otherwise it'll deal with the data that was read even if it isnt all read. It will then read more out when it loops through again. Pretty sure this is what you're asking?
Yes you would keep reading until you got all the data you expected, obviosuly you need someway of knowing how much to expect - which is why http puts the document size first
Your only calling close when recv() has returned a negative value which means that recv had some sort of error. Notice that the block where you do the close has a comment stating // got error or connection closed by client).
When you actually get some data (the else branch starting with // we got some data from a client), the connection is not being closed.
You are right that you can't assume the data arrives all at one time. Your mistake is in following how the code is working.

Resources