This is a test program. The client must send a text first and then the server replies. When client receive the "yellow" text from the server, the client receives and prints it just fine but when the server sends "red" after that, the printed message on client is "redlow". Why is this happening? is recvline[MAXLINE] combining the two together?
tcpclient.c
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXLINE 20000
int main()
{
int sockfd;
int ret;
char recvline[MAXLINE];
char sendline[MAXLINE];
struct sockaddr_in servaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(49152);
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
ret = connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if(ret == 0)
{
for(;;)
{
write(1,"\nsftc>",6);
if(fgets(sendline,MAXLINE,stdin)==NULL)
{
return 0;
}
send(sockfd, sendline, strlen(sendline),0);
if(recv(sockfd, recvline,MAXLINE,0)==0)
{
close(sockfd);
break;
}
else
{
printf("%s\n",recvline);
}
}
}
else
{
perror("connect error\n");
}
return 0;
}
tcpserver.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/select.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXLINE 20000
//to create listen fd and bind
int createListenFD(struct sockaddr_in* servaddr)
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(servaddr, sizeof(*servaddr));
servaddr->sin_family = AF_INET;
servaddr->sin_addr.s_addr = htonl(INADDR_ANY);
servaddr->sin_port = htons(49152);
bind(listenfd,(struct sockaddr*)servaddr,sizeof(*servaddr));
return listenfd;
}
void* child_server(void* args)
{
printf("inside child\n");
char recvline[MAXLINE];
char sendline[MAXLINE];
int connfd = *((int*)args);
char* reply = "yellow";
int i = 0;
while(i<3)
{
recv(connfd, recvline, MAXLINE, 0);
send(connfd,reply,strlen(reply),0);
reply = "red";
i ;
}
return (NULL);
}
int main()
{
int* connfd;
struct sockaddr_in cliaddr, servaddr;
socklen_t clilen;
int listenfd = createListenFD(&servaddr);
listen(listenfd, 5);
pthread_t tid;
clilen = sizeof (cliaddr);
connfd = malloc(sizeof(int));
*connfd = accept(listenfd, (struct sockaddr*) &cliaddr, &clilen);
pthread_create(&tid, NULL, child_server,(void*)connfd);
pthread_join(tid, NULL);
close(listenfd);
}
CodePudding user response:
recv()
stores only bytes that were actually received into the receiving buffer. In particular, it does not add a string terminator after the bytes it actually receives. If you want to interpret the data as a C string, then it is your responsibility to either
- add one, or
- ensure that the server sends one, and that the client doesn't consider the transmission complete until it receives that.
So,
is recvline[MAXLINE] combining the two together?
Effectively, yes. The characters 'y', 'e', 'l', 'l', 'o', 'w' are recorded in that array by the first recv()
call. You are lucking out (or maybe unlucking out) that the next byte happens to be a null character, so that when you print the contents of recvline
, you get exactly "yellow". The second recv()
call overwrites the first three bytes of the buffer with the characters 'r', 'e', 'd', but again, it does not write a string terminator. It follows that if you then print recvline
, you get "redlow".
It is important to consider the recv()
function's return value. It informs you about several things that you really need to know:
on a successful reception of one or more bytes, it tells you exactly how many bytes were received. (You can use this in your program to know where to put a string terminator.)
on an error, including a fairly benign one such as the function being interrupted by a signal before transferring any bytes, it notifies you that an error in fact occurred (by being the value -1; and
errno
then tells you what kind of error).when no more data are available as a result of the remote end having closed the connection, it notifies you of this (by being 0).
It is also important to understand that on a stream-oriented socket such as one based on TCP, send()
and recv()
are not message-oriented. It is not safe to assume that each send()
on one side pairs perfectly with one recv()
on the other. Data sent by one send()
may be split across more than one recv()
, and data sent by multiple send()
s may be received by a single recv()
.
Nor is it in general safe to assume that a send()
or recv()
will transfer the full number of bytes requested. Both partial sends and partial receives are possible.