C : String comparing - c

bool done;
done = false;
while (!done) {
/* read the message */
bzero(msg, 100);
printf("[client]Type something: ");
fflush(stdout);
read(0, msg, 100);
if (strcmp(msg, "/done") == 0) {
done = true;
/* sending the message to the server */
if (write(sd, msg, 100) <= 0) {
perror("[client]Error sending the message to the server.\n");
return errno;
}
} else {
/* sending the message to the server */
if (write(sd, msg, 100) <= 0) {
perror("[client]Error sending the message to the server.\n");
return errno;
/* reading the answer given by the server*/
if (read(sd, msg, 100) < 0) {
perror("[client]read() error from server.\n");
return errno;
}
/* printing the received message */
printf("[client]The received message is: %s\n", msg);
}
}
Here's the code that i have problem with. So i want to send messages to the server until I send the message "/done", the code works, I send messages continuously but even when i type and send "/done" the process doesn't end.
I think there's a problem with the bzero function that "clears" the msg or maybe i don't understand it so good.
I also tried to wrote my own function to check if two strings are the same, but no effect also.
So how should i write the condition or "clear" the msg so i can send messages continuously and after i send "/done" the execution ends?
P.S. the msg is declared earlier in the code as char msg[100];

When you read from 0, you're reading from stdin. If that is a terminal that you are typing into (you don't say), you likely have it set up in normal (canonical) mode, so you'll read a line, which probably includes a newline (\n) character. So when you enter /done, the string you get in your msg buffer is "/done\n" which doesn't match "/done"...

read(2) is including the '\n' at the end of the string. When you use low-level read you get everything. When trying to debug strings, it can be helpful to put quotes in your print statement, like
printf("[client]The received message is: '%s'\n", msg);
as this immediately shows invisible whitespace.

TCP is not a message protocol. It does not glue bytes together into messages. If you want to use TCP to send and receive messages, you'll need to implement functions that send and receive messages.
I must strongly caution you not to fail into the trap of thinking that changing your code from happening not to work when you tried it to happening to work when you try it means that you've fixed it. Your code fails if read returns 1. You need to implement a sensible function to receive a message or your code will only be working by luck, and I can tell you from painful experience that one day your luck will run out.

Related

Proper way to break out of a recv() loop in C

I wrote a server/client program where I can copy files from client to server. The problem is that I can't break out of the while loop in my server when writing to the file is done. I can only open the new copied file when I close my server program because the file doesn't get closed with fclose(copyFile). The file gets copied successfully everytime. However it does work properly when I run the server/client on the same machine but when I move the client to another pc, the server keeps blocking on recv() in my server.
Server:
while (1)
{
int res = recv(s, buf, BUFSIZ, 0);
if (res == SOCKET_ERROR)
{
printf("3 error %d\n", WSAGetLastError);
break;
}
fwrite(buf, 1, res, copyFile);
if (strncmp(buf, "exit", 4) == 0)
{
break;
}
}
fclose(copyFile);
Client:
while (size == BUFSIZ)
{
size = fread(buf, 1, BUFSIZ, originalFile);
int r = send(ClientSocket, buf, size, 0);
if (r == SOCKET_ERROR)
{
printf("1 error: %d\n", WSAGetLastError);
}
}
int r = send(ClientSocket, "exit", 4, 0);
if (r == SOCKET_ERROR)
{
printf("2 error: %d\n", WSAGetLastError);
}
fclose(originalFile);
How can I properly exit the while() loop in my server?
You are attempting to indicate the end of the file with the word "exit", but there are at least two problems with that:
if the file being transferred contains the word "exit" then that could be misinterpreted as an end-of-file marker, and
you seem to be assuming that your send() calls at the client will be paired one-to-one with recv() calls at the server, so that the "exit" can be relied upon to appear at the beginning of the buffer when the server receives it. That is not a safe assumption.
(Note also that even if the server did happen to receive the "exit" at the beginning of the buffer, you would still write it to the file before recognizing it as an end-of-file marker. If this were the only issue then it would be easy to fix.)
You need a workable application-level protocol to help client and server communicate -- something that layers the needed additional structure on top of the flat byte stream that the socket connection provides. The "exit" terminator is an attempt at that, but it is not a workable solution, at least not by itself.
In contrast, consider HTTP: messages consist of a sequence of headers that are individually and as a group recognizable by their lexical form, followed by the message body. Among the things that the headers can convey is the length of the message body, and this is how the recipient can recognize the end of the message body without the sender closing the connection.
You do not need to implement HTTP, but you can be inspired by it. If you want to transmit messages with arbitrary length and content, without signaling the end of the message by closing the connection, then your best bet is to tell the recipient in advance how many bytes to expect in the message. That could be as simple as prepending a fixed-length message-length field.

String comparison after transfer through TCP socket in C

I am sending a file through TCP, and have the server sending a message containing "END_OF_MESSAGE" to alert the client that they have received the whole file and can close the socket. The file is being sent, and the client receives the "END_OF_MESSAGE" string, however, when I use strcmp to compare the received information to "END_OF_MESSAGE", it never says that they match. I have tried strncmp and memcmp but am confused as to why strcmp does not tell me the strings match.
Code snippets:
Server:
char endMessage[MESSAGESIZE] = "END_OF_MESSAGE";
if ((send(clntSocket, endMessage, sizeof endMessage, 0))!= sizeof endMessage) DieWithError("Sending failed");
The above code snippet does get sent.
Client:
if ((bytesRcvd = recv(sock, echoBuffer, RCVBUFSIZE - 1, 0)) <= 0)
DieWithError("recv() failed or connection closed prematurely");
totalBytesRcvd += bytesRcvd; /* Keep tally of total bytes */
echoBuffer[bytesRcvd] = '\0'; /* Terminate the string! */
if (!(strcmp(echoBuffer, "END_OF_MESSAGE")==0)){
printf(echoBuffer); /* Print the echo buffer */
printf("\n");
}else{
break; //break out of while loop
}
the strcmp of the echoBuffer and "END_OF_MESSAGE" never returns 0, even though "END_OF_MESSAGE" is what I am sending from the server..I have tried strncmp to compare the first 3 characters ("END") to no avail.
Note: when I print out the echoBuffer, the very last one does print out END_OF_MESSAGE which is just adding to my confusion.
Does anyone have any insights into what I am doing wrong?
Thank you.
am sending a file through TCP, and have the server sending a message containing "END_OF_MESSAGE" to alert the client that they have received the whole file and can close the socket.
Why? Just close the socket. That will tell the client exactly the same thing..
What you're attempting is fraught with difficulty. What happens if the file contains END_OF_MESSAGE? You're going to need an escape convention, and an escape for the escape, and inspect all the data when both sending and receiving.
The actual problem that you're seeing is that END_OF_MESSAGE can arrive along with the last bit of the file, so it isn't at the start of the buffer.
But it's all pointless. Just close the socket.

How to recv until theres nothing more to recv without eof?

So i need to recv an html file from the server to the client, the file is bigger than the buffer so i make several sends. Thats why i have this loop when i recv
while (i = recv(s, buf, TAM_BUFFER, 0)) {
if (i == -1) {
perror(argv[0]);
fprintf(stderr, "%s: error reading result\n", argv[0]);
exit(1);
}
while (i < TAM_BUFFER) {
j = recv(s, &buf[i], TAM_BUFFER - i, 0);
if (j == -1) {
perror(argv[0]);
fprintf(stderr, "%s: error reading result\n", argv[0]);
exit(1);
}
i += j;
}
/* Print out the file line by line. */
printf("%s", buf);
}
the send looks something like this:
while (fgets(buf, sizeof(buf), fp)){
if (send(s, buf, TAM_BUFFER, 0) != TAM_BUFFER) errout(hostname);
}
The problem is the loop never ends, becase it doesnt recv the eof and i is never 0, its just remain blocked there.
I cant do the close to send the eof because after he recv the whole file, the client will ask for another file.
I tryed to send a SIGALRM if the loop stays blocked for longer than 5 seconds but it doesnt work as expected, because the loop wont stop, and it will throw an error.
Also how can i do to be able to recv less than TAM_BUFFER?(in the send, change the TAM_BUFFER -> strlen(buf)) I know i need to change the interior loop, but then ill have the same problem, j will not be 0 never, so i dont know how could i end it.(or maybe i dont need the second loop in this case).
EDIT: i cant send the lenght of the file beucause of the protocol im following
TCP is a protocol used to transport a single unstructured octet stream in each direction. Shutdown of the connection (i.e. EOF) is the only way in TCP to signal to the peer that no more data will be sent in this connection. If you need a different way because you need to distinguish between multiple messages inside the same TCP connection then you need to use an application level protocol which can specify such message boundaries. This is usually done by fixed message size, prefixing the message with a length or by special boundary markers.
If you can't embed payload size in your protocol, you have to identify EOF by closing socket or checking for timeout. You can use select function and set timeout for it, see here Using select and recv to obtain a file from a web server through a socket and https://stackoverflow.com/a/30395738/4490542

How to send and receive newline character over sockets in a client server model?

I am trying to learn client server model in Linux and I have setup two C files namely server.c and client.c. These are the code snippets that I seem to have problems with.
server.c code snippet
char* message = "<query>\n";
write(client_socket_filedescriptor, message, sizeof(message));
client.c code snippet
char* message = "<query>\n";
read(socket_filedescriptor, buffer, sizeof(buffer));
printf("%s", buffer);
printf("\n\n");
printf("%s", message);
Now when I run my server and then when I run my client, I expect the printf statements to print the same strings that is <query>\n, but I keep getting different outputs for buffer and message variables.
The output looks a bit like this when I run client code.
Output image
As you see, these two strings are different. I am trying to simulate a typical TCP handshake and I want to make sure that these two strings are same and then client will start writing or doing something with that server. But I am having this trivial problem. Could anyone tell my how to resolve it? I plan to use strcmp to compare buffer and message variables, but as it stands now, strcmp doesn't return 0 since these are different strings afterall.
You are ignoring the count returned by read(). It can be -1, indicating an error, or zero, indicating end of stream, or a positive number, indicating how many bytes were received. You cannot assume that read() fills the buffer, or that a single send() or write() corresponds to a single recv() or read().
In detail:
write(client_socket_filedescriptor, message, sizeof(message));
You are only sending four bytes, the size of the pointer. And you're ignoring the return value. It should be
int count = write(client_socket_filedescriptor, message, strlen(message));
if (count == -1)
perror("write"); // or better
char* message = "<query>\n";
read(socket_filedescriptor, buffer, sizeof(buffer));
That should be
int count = read(socket_filedescriptor, buffer, sizeof(buffer));
if (count == -1)
perror("read"); // or better
else if (count == 0)
; // end of stream: the peer has disconnected: close the socket and stop reading
else
Back to your code:
printf("%s", buffer);
That should be
printf("%.*s", count, buffer);
I plan to use strcmp()
You should plan to use strncmp(), with count above as the length parameter. In any case you can't assume the input ends with a null unless you (a) ensure you send the null, which you aren't, and (b) write a read loop that stops when you've read it.

Redirecting stdout to socket in client-server situation

I'm new to this forum, so I'm sorry if my question is not correctly asked. I'll try to be as clear as possible.
I'm trying to code two programs (client.c and server.c, using TCP sockets) in Linux, with the following behavior:
Client sends messages to Server that contain commands (ls, mkdir, etc) to be run by Server.
Server runs these commands, and sends program output (stdout) back to Client.
Client prints the recieved program output.
So far, I have this:
server.c:
/*After creating socket with socket(), binding to address and port,
putting in listening mode and accepting connection*/
dup2(sock_client,1); //redirect stdout
while(1){
recv(sock_client,buf,MAX_MSG_LENGTH,0);
/*If the message recieved was END_STRING, exit this loop*/
if (strncmp(buf, END_STRING, MAX_MSG_LENGTH) == 0)
break;
system(buf);
}
client.c:
/*After creating socket and connecting*/
while(fgets(buf,MAX_MSG_LENGTH,stdin)){
send(sock,buf,MAX_MSG_LENGTH,0);
if (!strncmp(buf,END_STRING,MAX_MSG_LENGTH)){
/*If the message recieved was END_STRING, exit this loop*/
break;
}
read(sock,buf,MAX_MSG_LENGTH); //read stdout from program
printf("%s\n",buf);
}
My problem is that, if a command has a long output, there's some "garbage" left from it when showing the output of the next commands, so I was wondering if there was a way to flush the socket (apparently not, based on my google research), or maybe to accomplish the expected server-client behavior in some other way.
Thank you!
EDIT:
Ok, I've finished the client. Here's the code:
client.c:
/* After creating socket and connecting*/
while(fgets(buf,MAX_MSG_LENGTH,stdin)){
send(sock,buf,MAX_MSG_LENGTH,0);
if (!strncmp(buf,END_STRING,MAX_MSG_LENGTH)){
/*If the message recieved was END_STRING, exit this loop*/
break;
}
while(1){
read_size = read(sock,buf,MAX_MSG_LENGTH);
/*Print read_size characters from buf*/
printf("%.*s\n", read_size, buf);
if (read_size < MAX_MSG_LENGTH){
/*Keep on reading until read_size < MAX_MSG_LENGTH, then exit this loop*/
break;
}
}
/*Clear buffer, just in case*/
for (i = 0; i < MAX_MSG_LENGTH; i++){
buf[i] = 0;
}
Just as a comment, this program will not work properly if the command sent to the server doesn't have any standard output (for example, mkdir new_directory), since, in this case, read() will leave the client permanently blocked, causing the server to never recieve the next command to be run or the END_STRING message to leave the program from the client. You could probably fix this by using a non-blocking socket and using select() to read from socket, just like synther suggested. Additionally, in the server, after the system(buf); line, you should add fflush(0), which will flush all the buffers (including the socket, which could be useful if the command send by the client has a really short output).
Thanks a lot!
Thank you for your answers!
I tried adding this to my client.c code:
/*After creating socket and connecting*/
while(fgets(buf,MAX_MSG_LENGTH,stdin)){
/*Send command to server*/
send(sock,buf,MAX_MSG_LENGTH,0);
if (!strncmp(buf,END_STRING,MAX_MSG_LENGTH)){
/*If the message recieved was END_STRING, exit this loop*/
break;
}
while(1){
read_size = read(sock,buf,MAX_MSG_LENGTH); //read stdout from program
printf("%.*s\n", read_size, buf);
if (read_size < MAX_MSG_LENGTH){
/*Exit this loop when reading less that MAX_MSG_LENGTH*/
break;
}
}
/*Clear the 'buf' array. I don't know if this is really necessary*/
for (i = 0; i < MAX_MSG_LENGTH; i++){
buf[i] = 0;
}
}
And now, after every command, the client only prints the output of the last command sent. I will test it more thoroughly and edit my original post if this solution is correct, so thanks a lot!
Perhaps, you get "garbage" in client when your command's output exceeds MAX_MSG_LENGTH. read(sock,buf,MAX_MSG_LENGTH); reads just MAX_MSG_LENGTH bytes from socket, remaining chars in socket are read the next time when you except it from the next command.
You can fix it in multiple ways in client.
read returns the actual number of bytes read. You can compare it with MAX_MSG_LENGTH and decide to read one more time or not. But if your actual data is exactly MAX_MSG_LENGTH bytes then you decide to read again and read blocks waiting data that is not available at the moment (and stdin blocks too, user can't send new command).
Use non-blocking socket to fix issue in 1. read will return immediately when no available data.
Add end-of-command marker to your server's output and client will know when to stop reading and switch to stdin reading.
Use select() mechanism to read from socket and user input "simultaneously". It allows to read from multiple file descriptors (socket and stdio) when data available on any of them.
Additionally, you use the same buffer for user commands and server responses. Typically, user commands are shorter then server output and buf could contain parts of last server outputs. Then you send this mix of user command and last server output to server.
And, as stated above, read returns the actual number of bytes read. You should print the exactly received number of bytes from buf, not all the data.
int ret = read(sock,buf,MAX_MSG_LENGTH); //read stdout from program
printf("%.*s\n", ret, buf);

Resources