I have a client and a server. I have two read() in my client and two write() in my server code. The server sends data to the client on the first write(), the client reads and stores to a buffer but it doesn't stop reading, it keeps reading through the server's second write() because in my client i have it set up to read 255 in the stream(from my understanding). I put 255 because i don't know how long the data datasize for first write() is. How do i fix this?
Client:
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
Server:
n = write(newsockfd,datasize,strlen(datasize));
if (n < 0) error("ERROR writing to socket");
n = write(newsockfd,data,255);
if (n < 0) error("ERROR writing to socket");
What you are experiencing is how TCP works. If the server calls write()
multiple times before the client calls read()
, then read()
can receive everything that was previously written, up to the maximum buffer size that you specify. TCP has no concept of message boundaries, like UDP does. There is nothing wrong with that. You just need to account for it, that's all.
If you need to know where one message ends and the next begins, then you simply need to frame your messages. There are a couple of different ways you can do that.
Send the data length before sending the actual data, so the client knows how much data to read, eg:
Server:
int datalen = ...; // # of bytes in data
int tmp = htonl(datalen);
n = write(newsockfd, (char*)&tmp, sizeof(tmp));
if (n < 0) error("ERROR writing to socket");
n = write(newsockfd, data, datalen);
if (n < 0) error("ERROR writing to socket");
Client:
int buflen;
n = read(sockfd, (char*)&buflen, sizeof(buflen));
if (n < 0) error("ERROR reading from socket");
buflen = ntohl(buflen);
n = read(sockfd, buffer, buflen);
if (n < 0) error("ERROR reading from socket");
else printf("%*.*s\n", n, n, buffer);
wrap the data with delimiters that do not appear in the actual data, then the client can keep reading and look for those delimiters. Use whatever delimiters make sense for your data (STX/ETX, line breaks, special reserved characters, etc):
Server:
char delim = '\x2';
n = write(newsockfd, &delim, 1);
if (n < 0) error("ERROR writing to socket");
n = write(newsockfd, data, datalen);
if (n < 0) error("ERROR writing to socket");
delim = '\x3';
n = write(newsockfd, &delim, 1);
if (n < 0) error("ERROR writing to socket");
Client:
char tmp;
do
{
n = read(sockfd, &tmp, 1);
if (n < 0) error("ERROR reading from socket");
if (tmp != '\x2')
continue;
buflen = 0;
do
{
n = read(sockfd, &tmp, 1);
if (n < 0) error("ERROR reading from socket");
if (tmp == '\x3')
break;
// TODO: if the buffer's capacity has been reached, either reallocate the buffer with a larger size, or fail the operation...
buffer[buflen] = tmp;
++buflen;
}
while (1);
printf("%*.*s\n", buflen, buflen, buffer);
break;
}
while (1);
You've got the right idea about sending the length data
before sending the actual data
, but you're sending datasize
in the wrong format. Sending it as an ascii string means the length of datasize
will vary depending on the length of data
:
For instance:
data
is 5 bytes in length, datasize
will be "5".data
is 100 bytes in length, datasize
will be "100".Unfortunately when it comes to serializing data
, this just won't work, datasize
must always take up the same number of bytes. You need to write this into the socket as an integer, and read it again at the other end as an integer. Then write this exact number of bytes of data into the socket and read this exact number of bytes of data at the other end:
For example:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
void send(int sock)
{
const char* msg = "this is a message!";
uint16_t len = strlen(msg);
uint16_t networkLen = htons(len); // convert to network byte order
write(sock, &networkLen, sizeof(networkLen));
write(sock, msg, len);
}
void receive(int sock)
{
char msg[1024];
uint16_t networkLen;
read(sock, &networkLen, sizeof(networkLen));
uint16_t len = ntohs(networkLen); // convert back to host byte order
read(sock, msg, sizeof(msg) - 1);
msg[len] = '\0';
printf("%u %s\n", len, msg);
}
int main(int argc, char** argv)
{
int sockets[2];
pipe(sockets);
send(sockets[1]);
receive(sockets[0]);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With