Reputation: 51
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!
Upvotes: 4
Views: 4163
Reputation: 51
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!
Upvotes: 1
Reputation: 336
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);
Upvotes: 0