Implementing a TCP server/client chat, I wanted to validate that the username of a new client doesn't exist already.
The server's code part is:
do
{
err=0;
if(write(socketFd[(int)idx], nickMsg, strlen(nickMsg))<0)
perror("write");
memset(buff, 0, sizeof(buff));
read(socketFd[(int)idx], buff, sizeof(buff));
for(i=0; i<supportedUsrsNum; i++)
{
if(*names[i]!=0)
{
if(strncmp(buff, names[i], strlen(buff))==0)
{
err=-1;
write(socketFd[(int)idx], usrExstMsg, strlen(usrExstMsg));
break;
}
}
}
if(!err)
break;
}
while(err==-1);
and the client writes:
do
{
gets(sendBuff);
write(sockFd, &sendBuff, sizeof(sendBuff));
sleep(1);
}while(1);
When the second client tries an existing name, the server detects it and goes for a second iteration, in which its read() gets an ASCII value of 3, though no further input occurred on the client side. What am I missing and how do I get to re-read a new value from the client?
It may be that the read() is not actually reading ASCII 3 but readings zero bytes. Perhaps an error condition occurred. Always check the return value of the call to read() since
It may return with a failure condition, or
It may read fewer than the requested number of bytes.
ALSO:
The client is sending the ENTIRE 'sendBuff' with this line:
write(sockFd, &sendBuff, sizeof(sendBuff));
where you probably want to only send the string itself (with some terminating character like '\0' or '\n'). Using sizeof(sendBuff) will send the actual text entered at gets(), the '\0' terminator, and then any random bytes that already existed in sendBuff before the call to gets().
Change that line to something like
write(sockFd, &sendBuff, strlen(sendBuff) + 1);
to write the text and the '\0' only, and not any extra garbage after it.
ALSO, don't use gets(). IT IS EVIL. (Thanks Jonathan Leffler for the reminder on this.)
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I am making a server client program in c.
The server starts first waits for the client then the client "connects".
after the client connects it waits for input from the user while the server is running read() to get that input from the client.
Here is the main issue.
after a new line character has been discovered by the while loop it then sends this data in the buffer from the client to the server which is waiting with a read. The server reads but no code below the read can be run unless it has something to do with the buffer.
so if the code is like this
read(socket_fd, buff, sizeof(buff));
printf("data from the client: %s", buff); // this line will be run in the terminal
printf("TESTING TESTING TESTING"); // this line will never be read
same thing on the clients side.
after it performs the write() on the client side any code that is under the write will not be ran. basically making this essentially a deadlock where both programs (I think) are just waiting on input from the other.
I don't know why this is. perhaps it is waiting for more data with the read, but that wouldn't explain why it runs code that prints the buffer but not anything else.
here is the snippet that sends and recieves data from the client side. The server is set up with TCP
while(1){
//wait for data from user
bzero(buffer, 256);
printf("Enter the string : ");
n = 0;
while ((buffer[n++] = getchar()) != '\n')
;
write(sockfd, buffer, sizeof(buffer));
printf("WE HERE");
read(sockfd, buffer, sizeof(buffer));
printf("READING THE DATA");
printf("From Server : %s", buffer);
if ((strncmp(buffer, "exit", 4)) == 0) {
printf("Client Exit...\n");
break;
}
}
and here is the server code which reads the data from the client and provides a response.
while(1) {
bzero(buffer, 256);
//read the message from the client
read(newsockfd, buffer, sizeof(buffer));
printf("From the client: %s", buffer);
printf("WORKING HERE BEFORE LOWER CASE [SERVER]");
printf("the buffer again: %s", buffer);
lower_string(buffer);
printf("WORKING AFTER THE LOWER CASE [SERVER]");
write(sockfd, buffer, sizeof(buffer));
printf("WRITING TO THE CLIENT");
if (strncmp("exit", buffer, 4) == 0) {
printf("Server Exit...\n");
break;
}
bzero(buffer, 256);
}
Your code contains a number of problems:
You aren't putting newline characters at the end of your printf() statements, which means that the printed text won't be visible until the stdout buffer gets full enough to force it to be flushed to the console. That is probably confusing you about the behavior of your program, since printf() statements are being executed but you aren't seeing their output in a timely manner. You should do e.g. printf("WE HERE\n"); rather than printf("WE HERE");
You aren't capturing the return values from send() and recv() into a variable so that you can examine what values they returned and act on them appropriately. If you don't look at the return values, you don't know how many bytes were actually sent or received (it may be less than the number of bytes you asked to be sent or received!) and you don't know if there was an error or an EOF condition that occurred.
You should be aware that recv() will block until at least one byte of data is available to place into your passed-in buffer, and similarly, write() can block until at least one byte of your passed-in buffer can be consumed by the networking stack. This can indeed lead to a deadlock in certain circumstances (e.g. if the remote peer never sends any data because it is blocked inside a recv() waiting for you to send some data to it first). This problem can be handled via various more advanced techniques (e.g. timeouts, or non-blocking or asynchronous I/O) but I won't go into those here.
Zeroing out your entire 256-byte array and then receiving up to 256 bytes means that in the case where you received 256 bytes of data at once, your array will not be NUL-terminated, and you will invoke undefined behavior if you try to use it as a C-string (e.g. by passing it to printf("%s", buffer);. You'd be better off receiving sizeof(buf)-1 bytes instead (and if you capture the return value of recv() as suggested in point #2, you can then just set buffer[numBytesReceived] = '\0'; afterwards, which is a more efficient way to make sure the string is NUL-terminated than unnecessarily clearing out all 256 bytes)
Note that you cannot assume that you will recv() the entire string within a single recv() call. It's unlikely to happen in this toy program (unless your network conditions are very bad), but in general it's possible for the sender to send() e.g. "123456789" and the receiver's first recv() call to get "1234" and then the second recv() call gets "567" and then the third gets "89", or any other combination of subsets of the string. The receiver is guaranteed to receive all of the bytes in order, but not guaranteed to receive them all at once. Production-level code would need to be smart enough to handle that correctly.
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.
I have the following code in server.c
while (1) {
char msg[1024];
recv(fd, msg, 1024, 0);
}
From client, first, I sent "hello world".
And then I received "hello world" in server.
I then sent "hexx" to server.
But I received "hexxo world" on server.
It seems as if msg wasn't cleared fully.
Please let me know what I'm doing wrong.
Cheers.
You are allocating 1024 bytes for message and never zeroing that data. You are just reading into the same buffer over and over. First pass it reads "hello world" in the second pass you are putting "hexx" in the same starting address resulting in "hexxo world"
As DoxyLover mentions you can null terminate the newly read string and get what you are looking for.
tcp recv not clearing old data
Who said it would? Not that it should be necessary. You're ignoring the count returned by recv(). After calling recv(), there are three possibilities:
Return value of -1: an error; call perror().
Return value of 0: end of stream: close the socket and exit the read loop.
Return value is positive: you have received exactly that many bytes into your buffer, and you should not assume that any data beyond that count is valid.
At present you're doing none of these things correctly.
read() does not null terminate the buffer. Therefore, you are seeing the old data left over in the buffer.
What you want is something like:
while (1) {
char msg[1024];
ssize_t n = recv(fd, msg, 1023, 0);
if (n >= 0)
msg[n] = '\0';
}
Note that I am limiting the read to one less than the size of the buffer to allow space for the null byte. Obviously, this only works with text data. With binary data, you need to record the return value from read() and use that as a byte count when processing the buffer. For example, to copy data from one socket to another:
while (1) {
char msg[1024];
ssize_t n = recv(fd, msg, 1024, 0);
if (n > 0)
send(second_fd, msg, n, 0);
}
All of this is very simplified - you need error checking, check the return from read() to make sure any bytes were received, check the return from send() to make sure all of the byte were sent, etc.
I am developing a client that needs to parse Chunked-type HTTP transfers. I've beat my head against the wall trying to figure out the error with the following, and would appreciate it if someone might be able to catch my error a bit quicker. To sum up the issue: it seems as though, the client does not receive ALL of the chunk, thereby screwing up the rest of the process. Thanks in advance!
while(cflag){
pfile_chunk = malloc(CHUNK_SIZE+1);
memset(pfile_chunk, 0, CHUNK_SIZE);
cPtr = pfile_chunk;
cPtr2 = NULL;
k=0;
while(*(cPtr-1) != '\n'){
k++;
recv(sock, cPtr, 1, 0);
cPtr = pfile_chunk+k;
}
cPtr2 = strchr(pfile_chunk, '\r');
*cPtr2 = '\0';
sscanf(pfile_chunk, "%x", &l);
if(l == 0)
break;
printf("\nServer wants to deliver %ld bytes.\n", l);
pfile_chunk = realloc(pfile_chunk, l+1);
memset(pfile_chunk, 0, l);
recv(sock, pfile_chunk, l, 0);
fputs(pfile_chunk, f);
printf("GOT THIS, SIZE %ld:\n%s\n", strlen(pfile_chunk), pfile_chunk);
//get next \r\n bytes.
recv(sock, NULL, 2, 0);
}
At the very least, you should check the return value of recv to see if you are getting the number of bytes you are expecting to get.
A short read is definitely possible on the network, since the system call will return whatever is available in the socket receive buffer at the time you make the call.
Implement a loop until you have read in your entire chunk, or pass the MSG_WAITALL flag to recv in the last parameter. However, you still need to check for an error from recv.
ssize_t r = recv(sock, pfile_chunk, l, MSG_WAITALL);
if (r < l) {
/* check for errors ... */
} else {
/* got the data */
}
It looks as though your very first dereference for the check in your while loop will access before the beginning of your array, which is likely not to be desired behavior. Hopefully, that memory location usually won't contain \n. That could mess up your read. I expect it probably contains some information to do with your malloc, which is unlikely to be \n, so you might never see a problem from that.
Also, hopefully you can trust the other end of the socket not to send more than CHUNK_SIZE+1 before they give you a \n. Otherwise, it could seg-fault out. Normally, though, I would expect a sender to just send 10 or fewer ASCII numeric characters and a CRLF for a chunk header anyways, but they could theoretically send a bunch of long chunk extension header fields with it.
Apart from that, there's just the more important issue already found by user315052 that you should either tell the recv method to wait for all the data you requested, or check how much data it actually read.
So, I have this piece of code, which will just read the message from the client and reply with a "yup"
while(strcmp(buffer, "QUIT") != 0){
bzero(buffer, 255); //cleans the read buffer
/*this reads the aux (request) from the client*/
recv(newsockfd, buffer, 255, 0);
if(strlen(buffer))
printf("Thread %d: %s\n", thread_no, buffer);
fflush(stdout);
write(newsockfd, "yup\n", 4);
}
The problem is that at the very first reading everything goes ok, but all other readings are messed up, if I send the message "guitar", for example, it gets the 'g', loops and then it gets the "uitar", sending aother "yup".
I have no clue what's happening.
Long story short: TCP isn't a message orientated protocol, it's a stream orientated protocol. Messages might be fragmented or merged together, and your application has to deal with that (the only guarantee is that you'll receive the data in the same order you sent it in).
recv() reads as many data as are available currently. You should read until you hit EOF, an error or a newline. Only if you have that newline, you have a complete line which you then compare with "QUIT", and which youi acknowledge with "Yup."
Three other bugs in the code snippit above.
1) Not checking the return value from recv(). The socket could have been closed gracefully (return value == 0), suffered an error (return value == -1), etc... More importantly, there's not to suggest that you will receive all 4 bytes of the "QUIT" message that was sent by a remote send call. You may only receive "Q".
2) If recv() were to receive 255 characters (none of which being a null char), then the subsequent strlen() will read into invalid memory and possibly crash.
3) Not checking the return value of the write call.