I'm trying to send a .txt file to a Linux socket client from a Linux server client (I use the loopback interface). I tried to a send a string, i.e. "OK", and everything worked fine, but when I try to send a file, the client prints out a strange output. Obviously I've done all the previous steps like socket, connect, accept, listen etc.
This is the server-side code:
printf("Sending file\n);
if ((fp=fopen(filename, "r"))!=NULL){
while ( (nbytes = fread(sendline, sizeof(char), 512, fp) > 0)){
printf("%s\n",sendline);
sent = writen(clientfd, sendline, nbytes);
}
close(fp);
}else
perror("Open file");
The 'writen' function is:
ssize_t writen(int fd, const void *vptr, size_t n){
/* Write "n" bytes to a descriptor. */
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return(-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
The client-side code is:
while (fgets(sendline, 10000,stdin) != NULL)
{
sendto(sockfd,sendline,strlen(sendline),0,
(struct sockaddr *)&servaddr,sizeof(servaddr));
read(sockfd,recvline,10000);
fputs(recvline,stdout);
recvline[n]=0;
if((recvline[0]=='-')&&(recvline[1]=='E')&&(recvline[2]=='R')&&(recvline[3]=='R')){
close(sockfd);
exit(1);
}
}
The strange client that I get is in the image.
Client Output
So what's my mistake? Why do I receive this kind of output and how could I fix it?
Couple of issues,
In server code, parenthesis is at incorrect place,
while ( (nbytes = fread(sendline, sizeof(char), 512, fp) > 0)){
should be
while ( (nbytes = fread(sendline, sizeof(char), 512, fp)) > 0){
---------^ parenthesis close here
And in client side, set '\0' in recvline before printing as
n = read(sockfd,recvline,10000);
recvline[n] = '\0'
fputs(recvline,stdout);
You never check how many bytes read returned (or whether it failed). Even if it does return n bytes as you assume, you don't null-terminate the buffer until after you print it, so fputs(recvline,stdout) will print whatever garbage it finds in the uninitialized buffer on the stack.
Related
I have written the server client program using linux tcp sockets.
Client ask server for present directory files list by
sending the ls command
server replies all the list of files in server dir.
I was testing it for more files in server working dir.
server response format in the buffer
file/dir [tab] file_name [tab] file_change_time
for each 1000 files to client.
Server Sending Code:
#define BUFSIZE 1400
void lsfun(node_t *pclient)
{
DIR *directory;
int status;
int cpylen = 0;
int msglen = 0;
unsigned int tt_count = 0;
unsigned int no_files = 0;
unsigned int no_sends = 0;
int clientfd = *(pclient->client_socket);
char *filectime;
char *buffer = malloc(BUFSIZE * sizeof(char));
char *tmp = malloc(BUFSIZE * sizeof(char));
char ending[] = "#####";
struct dirent *dir;
struct stat type;
pthread_mutex_lock(&lock);
chdir(pclient->pwd);
directory = opendir(".");
pthread_mutex_unlock(&lock);
if(tmp == NULL || buffer == NULL)
printf("malloc error for client conn:%d\n", clientfd);
if(directory)
{
while((dir = readdir(directory)) != NULL)
{
if(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))
continue;
status = stat(dir->d_name, &type);
if(status == 0)
{
filectime = ctime(&type.st_ctime);
if(dir->d_type != DT_REG)
cpylen = snprintf(tmp, BUFSIZE, "dir\t%s\t%s", dir->d_name, filectime);
else
cpylen = snprintf(tmp, BUFSIZE, "file\t%s\t%s", dir->d_name, filectime);
tmp[cpylen] = 0;
if((cpylen + msglen) < BUFSIZE)
{
strlcpy(buffer + msglen, tmp, cpylen);
msglen += cpylen;
no_files += 1;
}
else
{
tt_count += msglen;
printf("%s", buffer);
fflush(stdout);
send(clientfd, buffer, strlen(buffer), 0);
memset(buffer, 0, BUFSIZE + 5);
snprintf(buffer, cpylen, "%s", tmp);
msglen = cpylen;
cpylen = 0;
no_files += 1;
no_sends += 1;
}
}
else
{
cpylen = snprintf(buffer + msglen, BUFSIZE, "%s%s\n", "file stat error:", dir->d_name);
msglen += cpylen;
}
memset(tmp, 0, BUFSIZE);
}
}
cpylen = strlen(buffer);
if(msglen == cpylen)
send(clientfd, buffer, strlen(buffer), 0);
send(clientfd, ending, strlen(ending), 0); //sending msg ending for client read to close
printf("\nlssize :%d\tnofile:%d, msglen:%d\tcpylen:%d\tno_sends:%d\n", tt_count + msglen, no_files, msglen, cpylen, no_sends);
free(tmp);
free(buffer);
closedir(directory);
}
Client receiving Code:
#define BUFSIZE 1400
while(true)
{
msgsize = read(socketfd, buffer, BUFSIZE);
buffer[msgsize] = 0;
snprintf(ending, 6, "%s", buffer + (strlen(buffer) - 5));
if(strcmp(ending, "#####") == 0)
{
buffer[strlen(buffer) - 5] = 0;
if(buffer[strlen(buffer) - 1] == '\n')
printf("%s", buffer);
else
printf("%s\n", buffer);
fflush(stdout);
break;
}
else
{
printf("%s", buffer);
memset(buffer, 0, BUFSIZE);
}
}
Server replay debug print:
lssize :19931 nofile:501, msglen:437 cpylen:39 no_sends:14
why am I only receiving two packet instead of 14 packets from
server packets of around 1400 bytes each ?
where is the mistake ?
also welcome any code improvement suggestions.
In addition to the bugs pointed out in the comments, there are more fundamental issues with your code that are too extensive to merely comment on.
This code implies multithreaded use:
pthread_mutex_lock(&lock);
chdir(pclient->pwd);
directory = opendir(".");
pthread_mutex_unlock(&lock);
BUT, this code assumes the current working directory is always the current function's pclient->pwd:
status = stat(dir->d_name, &type);
It won't be if another thread calls chdir() to another directory while the loop is running, so your results
stat() always checks a relative path from the current working directory of the entire process. Which, in your posted code, can change.
A good rule to follow is to NEVER do things that change any global property of a process if you're writing multithreaded code.
Your use of snprintf() is also bug-prone. For example:
cpylen = snprintf(buffer + msglen, BUFSIZE, "%s%s\n", "file stat error:", dir->d_name);
Per 7.21.6.5 The snprintf function, paragraph 2 of the C11 standard(bolding mine):
The snprintf function returns the number of characters that would have been written had n been sufficiently large, not counting the terminating null character, or a negative value if an encoding error occurred. Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less than n.
You are blindly assuming that every one of your calls to snprintf() work. If any one call to snprintf() fails, your message contents are wildly indeterminate and your value for msglen will not accurately reflect the contents of your buffer.
That means this code won't send anything:
cpylen = strlen(buffer);
if(msglen == cpylen)
send(clientfd, buffer, strlen(buffer), 0);
I have a client that need to read on a socket a sequence of char sent by server.
Client read with system-call read() a socket SOCK_DGRAM.
Here the complete function with system-call read(..) inside.
ssize_t readLine(int sockd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *buffer;
buffer = vptr;
for ( n = 1; n < maxlen; n++ )
{
rc = read(sockd, &c, 1);
if ( rc == 1 )
{
*buffer++ = c;
if (c == '\0') break;
}
else
{
if (errno == EINTR) continue;
return -1;
}
}
*buffer = 0;
return n;
}
The problem is that if Server send a sequence of char like this ABCDEF'\0', this client read only A and then the system-call read() go in blocking mode.
I have used Wireshark to see if server work well and it send correctly ABCDEF'\0' in a UDP packet. All ok from this point of view.
Thanks to all in advance.
With datagram sockets, you need to read and write the whole datagram at once.
If you don't give read enough space to read the entire datagram, the rest of the datagram simply disappears.
int datagram_length = read(sockd, vptr, maxlen - 1);
if (datagram_length < 0) {
// complain about the error
} else {
vptr[datagram_length] = 0;
}
I want to implement a program that transfer any files from the server to the client. I must use read/write functions to read and write data(its for school assignment). Here is the code for server and client.
server.c
char buffer[512];
if( (file = open(strTable[1], O_RDONLY)) == -1 ) { perror("Open"); }
while( read(file, buffer, sizeof(buffer)) != 0 )
{
size = strlen(buffer)+1;
if( write(newsock, &size, sizeof(size)) < 0 ) { perror("Write"); exit(1); }
write_all(newsock, buffer, strlen(buffer)+1);
}
size = 4;
if( write(newsock, &size, sizeof(size)) < 0 ) { perror("Write"); exit(1); }
write_all(newsock, "end", 4);
It opens strTable[1] (which contains the file that i want to read) it reads sizeof(buffer) bytes and then i send to client how much bytes i will write to socket and after that i send the bytes. Here is write_all function.
int write_all(int sock, char* buffer, int size)
{
int nwrite, sent = 0;
while( sent < size )
{
if( (nwrite = write(sock, buffer + sent, size - sent)) < 0 )
{ perror("Write"); exit(1); }
sent += nwrite;
}
return sent;
}
client.c
if( (file = open(absolute, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1 )
{ perror("Open"); }
while( true )
{
received = 0;
/* Read the desired readable size */
if( read(sock, &size, sizeof(size)) < 0 )
{ perror("Read"); pthread_exit(NULL); }
/* Read all data */
while( received < size )
{
if( (nread = read(sock, buffer + received, size - received)) < 0 )
{ perror("Read"); pthread_exit(NULL); }
received += nread;
}
if( strncmp(buffer, "end", 4) == 0 ) { break; }
write_all(file, buffer, strlen(buffer)+1);
}
The client opens a file (absolute) and writes in it what it reads. It first reads how much size it has to read and then it doesnt stop to read until it reaches that size. If the client reads "end" then server has stop the writing to the socket so the client stops reading.
The problem is that i cant open the files after the transfer. I read some images that i cant open. I read also and one file.txt which i write some random words, this seems to be copied right but it has some garbage too(its the photo number 2). Why i getting this and how can i transfer files from sockets correctly?
enter image description here
enter image description here
Usual problems. read() doesn't null-terminate the buffer, so using strlen() on it as the count isn't valid. A read/write loop should look like this:
int count;
while ((count = read(inFD, buffer, sizeof buffer)) > 0)
{
if (write(outFD, buffer, count) < 0)
{
perrror("write"); // at least
break;
}
}
followed by error-handling as follows:
if (count < 0)
{
perror("read"); // at least
}
Your second problem comes with assuming that "end" will be received by itself as a separate message. There is no guarantee of this. You will either have to use end of stream by closing the socket after each file, or else send the length ahead of each file and only read exactly that many bytes from the stream for each file. But as you are already sending the length, sending "end" is pointless anyway.
I wrote function for read and write for a tcp program . I output in server side but I can't get read on client side . my code
read function :
int read_data (int sd , char **data_buf)
{
int in_length,length,size,bytesread;
char *temp_buf;
size = read(sd,&in_length,sizeof(in_length));/*send entire length of data*/
if( 0 > size )
{
printf("Error on reading from socket\n");
exit(1);
}
length = ntohl(in_length);
printf("Total length coming : %d\n",length);
*data_buf =(char *)malloc((length+1)*sizeof(char));
temp_buf =(char *)malloc((length+1)*sizeof(char));
while(length> 0)
{
bytesread = read(sd,temp_buf,4);
strcat(*data_buf,temp_buf);
temp_buf = temp_buf + bytesread;
length = length - bytesread;
}
return 1;
}
and my write functions as :
int write_data (int sd , char *buffer)
{
int length,len_buff,bytesread,size;
len_buff = strlen(buffer);/*total length of string*/
printf("string == %s\n",buffer);
length = htonl(len_buff);/*convert to host to n/w*/
printf("Total length send =%d\n",len_buff);
size = write(sd,&length,sizeof(length));/*write total size to server */
if( 0 > size)
{
printf("error\n");
exit(0);
}
while(length > 0)
{
bytesread = write(sd,buffer,4);/*write 4 bytes to server*/
buffer = buffer + bytesread;
length = length - bytesread;
}
return 1;
}
client program :
///.............code for socket and connections.................//
ret = write_data(sd,user_string);/*write entire datas to server*/
value_from_server = read_data(sd,&data_buf);
server side program :
value_from_client = read_data(connfd,&data_buf);
printf("the value from client : %s\n",data_buf);
index = string_function(data_buf,&store_buf);
printf("after string process : %s\n",store_buf);
write_data(connfd,store_buf);
printf("i am waiting for next string\n");
connfd is the new socket for communication with client . reading and writing function work perfectly on server side . writing function work on client side . but reading from server not work in client program . ant mistake on my code ?
bytesread = read(sd,temp_buf,4);
Why read 4 bytes always inside the loop? You should be reading the remaining number of bytes to be read. The socket is blocking and hence will be stuck if the server is done sending but client still tries reading 4 bytes to arrive in the last iteration.
Have print statements inside the loop to know the bytes read in each iteration and see if client is blocked with read
Your code has several logic errors.
size = read(sd,&in_length,sizeof(in_length));/*send entire length of data*/
if( 0 > size )
{
printf("Error on reading from socket\n");
exit(1);
}
length = ntohl(in_length);
Here you are assuming you read four bytes, rather than fewer, or end of stream. You must check for end of stream (zero return value), and you must loop until you get the four bytes.
while(length> 0)
{
bytesread = read(sd,temp_buf,4);
strcat(*data_buf,temp_buf);
temp_buf = temp_buf + bytesread;
length = length - bytesread;
}
Here again you are ignoring the possibility of end of stream or an error. It should be:
while ((bytesread = read(sd,temp_buf, length)) > 0)
{
temp_buf += bytes_read;
length -= bytesread;
}
if (bytesread < 0)
{
perror("read 2");
}
else if (length > 0)
{
// end of stream before all expected bytes were received ...
}
else
{
// The OK case
}
Your sending code is suboptimal:
while(length > 0)
{
bytesread = write(sd,buffer,4);/*write 4 bytes to server*/
buffer = buffer + bytesread;
length = length - bytesread;
}
There's no point in chunking into 4-byte writes. It should be:
while (length > 0)
{
bytesread = write(sd, buffer, length);
buffer = buffer + bytesread;
length = length - bytesread;
}
and of course the misnamed bytesread variable should be called byteswritten. In fact you can rely on this loop only executing once. Again it should be followed by a test of byteswritten == -1 to check for errors.
Your functions have logic errors in them.
The reading loop is reading exactly 4 bytes on each iteration. If the length of the data being read is not an even multiple of 4, read() will block on the last iteration waiting for data that does not arrive. The reading loop is also assuming that read() returns a null-terminated buffer, but that is not the case, so strcat() will attempt to copy data from surrounding memory and will either copy garbage or crash with a segfault. Also, the reading function is not null-terminating the data buffer it returns to the caller, but the caller assumes it is null-terminated.
The writing loop is writing exactly 4 bytes on each iteration. If the length of the data is not an even multiple of 4, write() will attempt to write data from surrounding memory on the last iteration, and will either send garbage or crash with a segfault.
You are also not doing adequate error handling in either function.
Try something more like this instead:
void read_raw_bytes (int sd, void *data, int length)
{
int bytes_read;
char *data_ptr;
data_ptr = (char*) data;
while( length > 0 )
{
bytes_read = read(sd, data_ptr, length);
if( bytes_read < 0 )
{
printf("Error on reading from socket\n");
exit(1);
}
if( bytes_read == 0 )
{
printf("Disconnected while reading from socket\n");
exit(1);
}
data_ptr += bytes_read;
length -= bytes_read;
}
}
void write_raw_bytes (int sd, void *data, int length)
{
int bytes_sent;
char *data_ptr;
data_ptr = (char*) data;
while( length > 0 )
{
bytes_sent = write(sd, data_ptr, length);
if( bytes_sent < 0 )
{
printf("Error on writing to socket\n");
exit(0);
}
data_ptr += bytes_sent;
length -= bytes_sent;
}
}
int read_data (int sd, char **data_buf)
{
int length;
read_raw_bytes (sd, &length, sizeof(length)); /*send entire length of data*/
length = ntohl(length);
printf("Total length coming : %d\n", length);
*data_buf = (char *) malloc((length+1)*sizeof(char));
if (*data_buf == NULL)
{
printf("Error on allocating memory\n");
exit(1);
}
read_raw_bytes (sd, *data_buf, length);
(*data_buf)[length] = 0;
return 1;
}
int write_data (int sd, char *buffer)
{
int length, len_buff;
len_buff = strlen(buffer); /*total length of string*/
printf("string == %s\n", buffer);
printf("Total length send =%d\n", len_buff);
length = htonl(len_buff); /*convert to host to n/w*/
write_raw_bytes (sd, &length, sizeof(length)); /*write total size to server */
write_raw_bytes (sd, buffer, len_buff);
return 1;
}
My server code is as follows:
while(bytes_written < filesize){
//Send from send_ptr
bw = send(child_socket, send_ptr, newLength, 0);
printf("%d bytes written\n", (int)bw);
//Increment bytes written
bytes_written += bw;
//Move send pointer
send_ptr = send_ptr + bw;
}
And my client code is as follows:
while((num_bytes_recv = read(sd, jpgBufferPointer, BUFFER_LENGTH))>0){
total_bytes_recv += num_bytes_recv;
printf("Read %d bytes\n",num_bytes_recv);
//Check for error
if(jpgError == NULL)
jpgError = strstr(jpgBufferPointer, "404 Not Found");
if(jpgError != NULL){
//Forwarding error response
if(send(sd, jpgBuffer, num_bytes_recv, 0) == -1){
error("Failed to send response message to client");
}
}
else{
//Find content size
contentSizeBuffer = strstr(jpgBufferPointer,"Content-Length");
if(contentSizeBuffer != NULL){
contentSizeBuffer=contentSizeBuffer+16;
contentSize=atoi(contentSizeBuffer);
jpgBuffer=(char*)realloc(jpgBuffer,(contentSize+FILE_NAME_LENGTH*2)*sizeof(char));
jpgBufferPointer=jpgBuffer;
}
jpgBufferPointer+=num_bytes_recv;
}
}
The server is saying it has sent all 43000 bytes, but client says it has received only 32768 bytes.
Appreciate any help! Thanks
You have a bug in the sending part, you should update newLength, because if you have 1 byte left to send from the file, it will send more, going out of the memory area where the content you want to send is stored. You should fix in this way:
bw = send(child_socket, send_ptr, newLength<(filesize-bytes_written)?newLength:(filesize-bytes_written), 0);
In this way the last send will have the correct size.
Also, use write instead of send if you are not using any flags.
You need to have the similar loop as you have on the writing side (bytes_written < filesize) on the reading side (i.e., while you can read more bytes, you should read them and append them).
The network doesn't guarantee that one read() call will return all available data.
The best way of writing client-server socket programming is to have a header before your data. The header should state the amount of data that it is going to transfer.
For example, To send data "Hello World", then send it as "0011+HELLO WORLD"
Here 11 stands for the size of the data the sender is planning to send now. The receiver on reading the first 4 bytes can understand that he should be ready to read next 11 bytes of data from the sender.
So reader will do two read:
hRead = 5 /* With 5 you are saying it can read upto max of 9999 bytes from data".
read(sd, buff, hRead);
dRead = atoi(buff);
readn(sd, buff, dRead);
For Example : Server
size_t sendn(int fd, const void *vptr, size_t n) {
size_t nleft;
size_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = send(fd, vptr, nleft, 0)) <= 0) {
if (errno == EINTR)
nwritten = 0;
else {
fprintf(stderr, "send failed %d - %s\n", fd, strerror(errno));
return (-1);
}
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
To send message:
sprintf(buff, "%d + %d + %s\r\n", MSG_LOGIN, strlen("Hello World"), Hello World);
sendn(sd, buff, strlen(buff));
Client:
size_t readn(int fd, void *vptr, size_t n) {
size_t nleft;
size_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nread = recv(fd, ptr, nleft, 0)) < 0) {
if (errno == EINTR)
nread = 0;
else {
fprintf(stderr, "read failed %d - %s\n", fd, strerror(errno));
return (-1);
}
} else if (nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return (n - nleft);
}