Sockets in C: recv fails after one byte - c

I'm currently doing an assignment in which we are defined to use send()/recv() with two parts:
first send an int that indicates the length of the string (4 bytes) and then send the string itself.
When I try to run the client/server on my PC, only the first recv() returns success, while the next one fails.
When I run the same code on my partner's PC, it works well.
The error I'm receiving for recv() is 10054.
I am totally clueless, and have no idea how to approach this.
The receiving code is:
TransferResult_t ReceiveString(char** OutputStrPtr, SOCKET sd){
/* Recv the the request to the server on socket sd */
int TotalStringSizeInBytes;
TransferResult_t RecvRes;
char* StrBuffer = NULL;
if ((OutputStrPtr == NULL) || (*OutputStrPtr != NULL))
{
printf("The first input to ReceiveString() must be "
"a pointer to a char pointer that is initialized to NULL. For example:\n"
"\tchar* Buffer = NULL;\n"
"\tReceiveString( &Buffer, ___ )\n");
return TRNS_FAILED;
}
/* The request is received in two parts. First the Length of the string (stored in
an int variable ), then the string itself. */
RecvRes = ReceiveBuffer(
(char *)(&TotalStringSizeInBytes),
(int)(sizeof(TotalStringSizeInBytes)), // 4 bytes
sd);
if (RecvRes != TRNS_SUCCEEDED) return RecvRes;
StrBuffer = (char*)malloc(TotalStringSizeInBytes * sizeof(char));
if (StrBuffer == NULL)
return TRNS_FAILED;
RecvRes = ReceiveBuffer(
(char *)(StrBuffer),
(int)(TotalStringSizeInBytes),
sd);
if (RecvRes == TRNS_SUCCEEDED)
{
*OutputStrPtr = StrBuffer;
}
else
{
free(StrBuffer);
}
return RecvRes;
}
The ReceiveBuffer function is:
TransferResult_t ReceiveBuffer(char* OutputBuffer, int BytesToReceive, SOCKET sd){
char* CurPlacePtr = OutputBuffer;
int BytesJustTransferred;
int RemainingBytesToReceive = BytesToReceive;
while (RemainingBytesToReceive > 0)
{
/* send does not guarantee that the entire message is sent */
BytesJustTransferred = recv(sd, CurPlacePtr, RemainingBytesToReceive, 0);
if (BytesJustTransferred == SOCKET_ERROR)
{
printf("recv() failed, error %d\n", WSAGetLastError());
return TRNS_FAILED;
}
else if (BytesJustTransferred == 0)
return TRNS_DISCONNECTED; // recv() returns zero if connection was gracefully disconnected.
RemainingBytesToReceive -= BytesJustTransferred;
CurPlacePtr += BytesJustTransferred; // <ISP> pointer arithmetic
}
return TRNS_SUCCEEDED;
}
ADDED: the sending function:
TransferResult_t SendString( const char *Str, SOCKET sd ){
/* Send the the request to the server on socket sd */
int TotalStringSizeInBytes;
TransferResult_t SendRes;
/* The request is sent in two parts. First the Length of the string (stored in
an int variable ), then the string itself. */
TotalStringSizeInBytes = (int)( strlen(Str) + 1 ); // terminating zero also sent
SendRes = SendBuffer(
(const char *)( &TotalStringSizeInBytes ),
(int)( sizeof(TotalStringSizeInBytes) ), // sizeof(int)
sd );
if ( SendRes != TRNS_SUCCEEDED ) return SendRes ;
SendRes = SendBuffer(
(const char *)( Str ),
(int)( TotalStringSizeInBytes ),
sd );
return SendRes;
}
And:
TransferResult_t SendBuffer( const char* Buffer, int BytesToSend, SOCKET sd ){
const char* CurPlacePtr = Buffer;
int BytesTransferred;
int RemainingBytesToSend = BytesToSend;
while ( RemainingBytesToSend > 0 )
{
/* send does not guarantee that the entire message is sent */
BytesTransferred = send (sd, CurPlacePtr, RemainingBytesToSend, 0);
if ( BytesTransferred == SOCKET_ERROR )
{
printf("send() failed, error %d\n", WSAGetLastError() );
return TRNS_FAILED;
}
RemainingBytesToSend -= BytesTransferred;
CurPlacePtr += BytesTransferred; // <ISP> pointer arithmetic
}
return TRNS_SUCCEEDED;
}

Related

Writing a Struct to Text file in C

What I am trying to do is to parse a file (WireShark dissector into a little descriptor file) in C.
I managed to parse successfully (the values in the struct are correct), but when I write to the text file, it adds a row of null values at the end (the number of nulls added are always equal to the number of structs I wrote to the file). For example, I wrote 6 lines, so 6 nulls were added at the end.
I would love to know if someone could identify what the problem is.
This is the write macro I use:
#define WRITE_F(file_name,modifier,value) fprintf(file_name,"%"#modifier,value);
//the used function - **space_val = " "**
void write_to_file(FILE* lua_descriptor, lua_line *line)
{
WRITE_F(lua_descriptor,s, line->name);
WRITE_F(lua_descriptor,s, line->str_size);
WRITE_F(lua_descriptor,c, SPACE_VAL);
WRITE_F(lua_descriptor,d, line->opcode);
WRITE_F(lua_descriptor, s, "\n");
}
//the lua_line struct:
typedef struct lua_line{
char * name;
char * str_size;
int opcode;
}lua_line;
This is handled by a client-server solution. This is the client side and I am sending it to the server like so:
/*
============================================
General : function is responsible for sending the length of the file to the server
Parameters : sock - socket connection between client and server
*filesize - holds a pointer to the size that needs to be sent
filesize_len - the length of the file size pointer
Return Value : returns TRUE when the length of the data was sent correctly.
returns FALSE when there was a socket error.
============================================
*/
bool send_file_length(SOCKET sock, long* filesize, int filesize_len)
{
bool retval = true;
unsigned char* pbuf = (unsigned char*)filesize;
int num = send(sock, pbuf, filesize_len, 0);
if (num == SOCKET_ERROR){retval = false;}
return retval;
}
/*
============================================
General : transfers the size to network byte order
and sends data to the server
Parameters : sock - socket for the client - server connection
filesize - the value of the file size
Return Value : returns TRUE when the length of the data was sent correctly.
returns FALSE when there was a socket error.
============================================
*/
bool convert_size(SOCKET sock, long filesize)
{
printf("file size %d\n", filesize);
filesize = htonl(filesize);
return send_file_length(sock, &filesize, sizeof(filesize));
}
/*
============================================
General : function is responsible of sending the new lua
file to the server
Parameters : sock - socket between the client and the server
f - file that needs to be sent to the server
Return Value : returns TRUE when the file was sent correctly
returns FALSE when the file is empty or when there was a socket error
============================================
*/
bool send_file(SOCKET sock, FILE* f)
{
bool retval = true;
fseek(f, 0, SEEK_END);
long filesize = ftell(f);
char buffer[BUFFER_SIZE];
rewind(f);
if (filesize == EOF) { retval = false; }
if (retval && !convert_size(sock, filesize)) { retval = false; }
if (filesize > 0 && retval){
while (filesize > 0 && retval){
size_t num = filesize;
num = fread(buffer, 1, num, f);
if (num < 1) {
retval = false;
}
if (!send(sock, buffer, num, 0)){
retval = false;
}
filesize -= num;
}
}
return retval;
}
And on the server side, I receive and write it to the file (which adds an extra null line) like so:
/*
===================================================
General : receives the length of the file and updates it
Parameters : sock - client socket to receive the data from
*filesize - holds a pointer to the size of the buffer that needs to update
filesize_len - the length of the file size pointer
Return Value : returns TRUE when the size is read correctly
else, FALSE when there was a socket error or no bytes are received.
===================================================
*/
bool recv_file_len(SOCKET sock, long* filesize, int filesize_len)
{
unsigned char* psize = (unsigned char*)filesize;//changes the pointer type so we can receive the data to it from recv
bool retval = true;
int num = recv(sock, psize, filesize_len, 0);//receive to size
if (num == SOCKET_ERROR)
{
retval = false;
}
else if (num == 0)
{
retval = false;
}
return retval;
}
/*
===================================================
General : writes to the lua file the data from the file
that was received in the socket
Parameters : sock - the socket between the client and server
*f - the file to write the data received to
Return Value : returns TRUE when everything was written to the file.
returns FALSE if there's no data received or detected a socket problem.
===================================================
*/
bool write_to_lua(SOCKET sock, FILE *f)
{
long filesize;//size of address
char buffer[BUFFER_SIZE];
ZeroMemory(buffer, BUFFER_SIZE);
bool retval = recv_file_len(sock, &filesize, sizeof(filesize));
if (retval)//if the size of the file didn't fail to update
{
filesize = ntohl(filesize);
printf("file size (From C client) : %ld\n", filesize);
while (filesize > 0 && retval)
{
int num = filesize;
if (!recv(sock, buffer, sizeof(buffer), 0))//reads the data
{
retval = false;
}
int offset = 0;
while (offset < num && retval)//writes to the file
{
size_t written = fwrite(&buffer[offset], 1, num - offset, f);
//size_t written = fprintf(f,&buffer[offset]);
if (written < 1)
{
retval = false;
}
offset += written;
}
filesize -= num;
}
}
return retval;
}
Your write_to_file() looks fine to me, provided the lua_line is being setup correctly (which you did not show).
However, your handling of send() and recv() is all wrong. The return value is the number of bytes sent/received, not a bool like you are treating it. And, the return value may be smaller than the number of bytes requested, which you are not checking for to know if you have to call send()/recv() again to send/receive more bytes (similar to what you are doing for fwrite()).
You also have a potential buffer overflow in the send_file() loop, if the file is larger than your buffer. And the write_to_lua() loop is also potentially receiving too many bytes into its buffer and ignoring the actual filesize when writing that buffer to the output file. Which is likely where your extra nuls are coming from.
Try something more like this instead:
Client:
/*
============================================
General : function is responsible for sending the length of data to the server
Parameters : sock - socket connection between client and server
*buf - holds a pointer to the data that needs to be sent
bufsize - the length of the data pointer
Return Value : returns TRUE when the length of the data was sent correctly.
returns FALSE when there was a socket error.
============================================
*/
bool send_raw(SOCKET sock, void* buf, int bufsize)
{
unsigned char* pbuf = (unsigned char*)buf;
while (bufsize > 0) {
int num = send(sock, pbuf, bufsize, 0);
if (num == SOCKET_ERROR){ return false; }
pbuf += sent;
bufsize -= sent;
}
return true;
}
/*
============================================
General : function is responsible for sending the length of the file
to the server in network byte order
Parameters : sock - socket connection between client and server
filesize - the value of the file size
Return Value : returns TRUE when the length of the data was sent correctly.
returns FALSE when there was a socket error.
============================================
*/
bool send_file_length(SOCKET sock, long filesize)
{
filesize = htonl(filesize);
return send_raw(sock, &filesize, sizeof(filesize));
}
/*
============================================
General : function is responsible of sending the new lua
file to the server
Parameters : sock - socket between the client and the server
f - file that needs to be sent to the server
Return Value : returns TRUE when the file was sent correctly
returns FALSE when the file is empty or when there was a socket error
============================================
*/
bool send_file(SOCKET sock, FILE* f)
{
if (fseek(f, 0, SEEK_END) != 0) { return false; }
long filesize = ftell(f);
rewind(f);
if (filesize == -1L) { return false; }
if (!send_file_length(sock, filesize)) { return false; }
if (filesize > 0) {
char buffer[BUFFER_SIZE];
do {
size_t num = fread(buffer, 1, min(filesize, BUFFER_SIZE), f);
if (num < 1) { return false; }
if (!send_raw(sock, buffer, num)) { return false; }
filesize -= num;
}
while (filesize > 0);
}
return true;
}
Server:
/*
============================================
General : function is responsible for receiving a length of data from the client
Parameters : sock - client socket to receive the data from
*buf - holds a pointer to the buffer that needs to update
bufsize - the length of the buffer
Return Value : returns TRUE when the data is read correctly
else, FALSE when there was a socket error or no bytes are received.
============================================
*/
bool recv_raw(SOCKET sock, void* buf, int bufsize)
{
unsigned char* pbuf = (unsigned char*)buf;
while (bufsize > 0) {
int num = recv(sock, pbuf, bufsize, 0);
if (num <= 0) { return false; }
pbuf += num;
bufsize -= num;
}
return true;
}
/*
===================================================
General : receives the length of the file and updates it
Parameters : sock - client socket to receive the data from
*filesize - holds a pointer to the size of the buffer that needs to update
filesize_len - the length of the file size pointer
Return Value : returns TRUE when the size is read correctly
else, FALSE when there was a socket error or no bytes are received.
===================================================
*/
bool recv_file_len(SOCKET sock, long* filesize)
{
if (!recv_raw(sock, filesize, sizeof(*filesize)) { return false; }
*filesize = ntohl(*filesize);
return true;
}
/*
===================================================
General : writes to the lua file the data from the file
that was received in the socket
Parameters : sock - the socket between the client and server
*f - the file to write the data received to
Return Value : returns TRUE when everything was written to the file.
returns FALSE if there's no data received or detected a socket problem.
===================================================
*/
bool write_to_lua(SOCKET sock, FILE *f)
{
long filesize;//size of address
if (!recv_file_len(sock, &filesize)) { return false; }
printf("file size (From C client) : %ld\n", filesize);
if (filesize > 0)
{
char buffer[BUFFER_SIZE];
do {
int num = min(filesize, BUFFER_SIZE);
if (!recv_raw(sock, buffer, num)) { return false; }
int offset = 0;
do
{
size_t written = fwrite(&buffer[offset], 1, num - offset, f);
if (written < 1) { return false; }
offset += written;
}
while (offset < num);
/* alternatively, no loop is needed:
if (fwrite(buffer, num, 1, f) < 1) { return false; }
*/
filesize -= num;
}
while (filesize > 0);
}
return true;
}

Is there any way to know the amount of bytes send from the client to the server and process the recv() in networks

I am trying to build a chat application between the server and the client. My doubt is for sending information from the client or from the server I was able to handle the partial send with the help of the loop, but I am unable to find out the length of the send data bytes from the client to the server or from the server to the client, thereby having problem in creating the memory for the received bytes and printing.
My chat function code for the client:
int chat_function(int sockfd)
{
char ch;
char *buf;
char *newp;
int ret_send = 0;
int ret_recv = 0;
int buf_size = 0;
while(1) {
printf("From client, enter the message : ");
buf = (char *)malloc(sizeof(char));
if (buf == NULL)
return -1;
while ((ch = getchar()) != '\n') {
buf[buf_size++] = ch;
newp = (char *)realloc(buf, (buf_size + 1) * sizeof(char));
if ( newp == NULL) {
free(buf);
return -1;
}
buf = newp;
}
buf[buf_size] = '\0';
ret_send = send_all(sockfd, buf, buf_size);
if (ret_send == -1)
error(1, errno, "error in send() function call\n");
memset(buf, 0, buf_size);
ret_recv = recv_all(sockfd, buf, buf_size);
if (ret_recv == -1) {
error(1, errno, "error in recv() function call\n");
} else if (ret_recv == -2) {
printf("Oops the server has closed the connection\n");
free(buf);
break;
}
printf("From Server : %s", buf);
if ((strncmp(buf, "exit", 4)) == 0) {
printf("Client Exit...\n");
free(buf);
break;
}
free(buf);
}
}
For handling partial send:
int send_all(int sockfd, char *buf, int buf_size)
{
int bytes_left = 0;
size_t send_bytes = 0;
bytes_left = buf_size
while (1) {
send_bytes = send(fd, buf, bytes_left, 0);
if (send_bytes == -1)
return -1;
buf = buf + send_bytes;
bytes_left = bytes_left - send_bytes;
if (bytes_left == 0)
break;
}
return 0;
}
TCP is a stream protocol, meaning there are no message boundaries: it is just a full-duplex (meaning data flows in both directions at the same time, as if there were two separate lanes) more or less continuous stream of data.
UDP is a datagram protocol, and does have message boundaries. There is an ioctl (FIONREAD/SIOCINQ) that provides the length of the next datagram, but because it involves a syscall, doing that for every message you receive is going to be slow and inefficient. Instead, you normally use a buffer large enough to hold the largest acceptable message, and copy it if/when necessary. However, UDP also has no reliability guarantees, and often UDP datagrams are completely lost without any trace or discernible reason; that's just what happens.
For a chat client-server connection, you'll want to use TCP.
Since the underlying connection is just a stream of data, you need to design a protocol for the communications, so that the stream can be split into messages, with each message processed separately.
The simplest case would be to use the nul character, \0, as a message separator.
The "send" function would then look something like this:
/* Returns 0 if message successfully sent,
nonzero errno code otherwise. */
int send_message(int descriptor, const char *message)
{
/* If message is NULL, we cannot use strlen(); use zero for that. */
const size_t message_len = (message) ? strlen(message) : 0;
/* Temporary variables for the sending part. */
const char *ptr = message;
const char *const end = message + message_len + 1; /* Include '\0' at end */
ssize_t bytes;
/* Check valid descriptor and message length. */
if (descriptor == -1 || message_len < 1)
return errno = EINVAL;
/* Write loop for sending the entire message. */
while (ptr < end) {
bytes = write(descriptor, ptr, (size_t)(end - ptr));
if (bytes > 0) {
ptr += bytes;
} else
if (bytes != -1) {
/* This should never happen. */
return errno = EIO;
} else
if (errno != EINTR) {
/* We do not consider EINTR an actual error; others we do. */
return errno;
}
}
return 0;
}
The above send_message() function writes the specified string, including the string terminating nul character \0, to the specified descriptor.
On the read end, we need a buffer large enough to hold at least one full message. Instead of always waiting for incoming data, we need to check if the buffer already contains a full message, and if it does, return that. Also, you do not necessarily want to always wait for an incoming message, because that would mean you cannot send two messages in a row.
So, here's my suggestion:
static int incoming_desc = -1;
static char *incoming_data = NULL;
static size_t incoming_size = 0;
static char *incoming_next = NULL; /* First received but not handled */
static char *incoming_ends = NULL; /* Last received but not handled */
#define INCOMING_CHUNK 4096
/* Receive a new message into dynamically allocated buffer,
and return the length. Returns 0 when no message, with errno set.
Waits at most ms milliseconds for a new message to arrive.
errno == EAGAIN: no message, timeout elapsed.
errno == ECONNABORTED: other end closed the connection.
*/
size_t get_message(char **message, size_t *size, long ms)
{
struct timeval timeout;
/* Make sure the parameters are sane. */
if (!message || !size || ms < 0) {
errno = EINVAL;
return 0;
}
/* For this function to work like getline() and getdelim() do,
we need to treat *message as NULL if *size == 0. */
if (!*size)
*message = NULL;
timeout.tv_sec = ms / 1000;
timeout.tv_usec = (ms % 1000) * 1000;
/* Timeout loop. */
while (1) {
fd_set readfds;
ssize_t bytes;
size_t used;
int result;
/* Is there a pending complete message in the buffer? */
if (incoming_ends > incoming_next) {
char *endmark = memchr(incoming_next, '\0', (size_t)(incoming_ends - incoming_next));
if (endmark) {
const size_t len = (size_t)(endmark - incoming_next) + 1;
/* Reallocate the message buffer, if necessary. */
if (len > *size) {
char *temp = realloc(*message, len);
if (!temp) {
errno = ENOMEM;
return 0;
}
*message = temp;
*size = len;
}
/* Copy message, */
memcpy(*message, incoming_next, len);
/* and remove it from the buffer. */
incoming_next += len;
/* In case the other end sent just the separator, clear errno. */
errno = 0;
/* We return the length sans the separator. */
return len - 1;
}
}
/* Do we have time left to check for input? */
if (timeout.tv_sec <= 0 && timeout.tv_usec <= 0)
break; /* Nope. */
/* Is incoming_desc one we can select() for? */
if (incoming_desc < 0 || incoming_desc >= FD_SETSIZE)
break; /* Nope. */
FD_ZERO(&readfds);
FD_SET(incoming_desc, &readfds);
result = select(incoming_desc + 1, &readfds, NULL, NULL, &timeout);
if (result < 1)
break; /* Nothing interesting happened (we ignore error here). */
if (!FD_ISSET(incoming_fd, &readfds))
break;
/* Number of bytes used in the buffer right now. */
used = (size_t)(incoming_ends - incoming_data);
/* Do we have at least INCOMING_CHUNK bytes available? */
if (used + INCOMING_CHUNK >= incoming_size) {
/* Nope. Repack the incoming buffer first. */
if (incoming_next > incoming_data) {
const size_t len = (size_t)(incoming_ends - incoming_next);
if (len > 0)
memmove(incoming_data, incoming_next, len);
incoming_next = incoming_data;
incoming_ends = incoming_data + len;
}
/* Recalculate the number of bytes we have free now. Enough? */
used = (size_t)(incoming_ends - incoming_data);
if (used + INCOMING_CHUNK > incoming_size) {
/* Grow incoming buffer. */
const size_t newsize = used + INCOMING_CHUNK;
char *temp = realloc(incoming_data, newsize);
if (!temp) {
errno = ENOMEM;
return 0;
}
incoming_next = temp + (size_t)(incoming_next - incoming_data);
incoming_ends = temp + used;
incoming_data = temp;
incoming_size = newsize;
}
}
/* Read more data into the buffer; up to a full buffer. */
bytes = read(incoming_fd, incoming_ends, incoming_size - used);
if (bytes > 0) {
incoming_ends += bytes;
} else
if (bytes == 0) {
/* Other end closed the connection. We may have a partial message
in the buffer, and should handle that too, but for now, we
just error out. */
errno = ECONNABORTED;
return 0;
} else
if (bytes != -1) {
/* Should never happen. */
errno = EIO;
return 0;
} else
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
/* No data yet, interrupted by signal delivery, etc. */
continue;
} else {
/* errno is set to indicate which error happened. */
return 0;
}
}
/* Timeout. */
errno = EAGAIN;
return 0;
}
Note that get_message() works like getline(): you do e.g.
char *msg = NULL;
size_t size = 0;
size_t len;
len = get_message(&msg, &size, 100); /* 100 ms = 0.1 seconds */
if (len) {
/* msg contains a full message of len characters */
} else
if (errno == ECONNABORTED) {
/* Other end closed the connection */
} else
if (errno != EAGAIN) {
fprintf(stderr, "Error receiving data: %s.\n", strerror(errno));
}
Then, you can reuse the same dynamically allocated buffer by just calling e.g.
len = get_message(&msg, &size, 100); /* 100 ms = 0.1 seconds */
again.
There is no such mechanism built into TCP or UDP. You need to implement your own protocol on top of it. One of the possible solutions is:
If the content delivered is static.
If the sending end knows the size of the data that is being delivered prior, your client and server can agree on specific terms. For example, the first four bytes sent by the server is the size of the remaining message represented in network byte order.
Server code
uint32_t n_size = htonl(size); // Convert the data size into network byte order.
write(sockfd, &n_size, sizeof(n_size)); // Send to the client.
Client code
uint32_t n_size;
int n_read = 0;
for ( ; ; ) {
int rd_status = read(sockfd, (void*) &n_size + n_read, sizeof(n_size) - n_read);
if (rd_status <= 0)
goto handle_this_case;
n_read = n_read + rd_status;
if (n_read == sizeof(n_size))
break;
}
uint32_t size = ntohl(n_size);
If the content delivered is generated on the fly.
In this case, even the server is not aware of the size of the message. You need to build your functions for handling this case. Below I have shown a bare minimal implementation:
Client-Side:
struct data_unit
{
void* data;
int size;
};
struct data_storage
{
struct data_unit unit;
struct data_storage* next;
};
void append_data(struct data_storage* storage, struct data_unit* unit);
struct data_unit* dump_data(struct data_storage* storage);
int main()
{
struct data_storage storage;
struct data_unit unit;
unit.data = malloc(MAX_SIZE);
for ( ; ; ) {
int rd_status = read(sockfd, unit.data, MAX_SIZE);
if (rd_status < 0)
goto handle_this_case;
else if (rd_status == 0)
break;
unit.size = rd_status;
append_data(&storage, &unit);
}
struct data_unit* t_data = dump_data(&storage);
}

recv() on socket by dynamically allocating space

I'm trying to get the source code of my website using c, I'm able to connect and everything but when I implement the recv() code, it only receives the last few bytes of the source code. I'd like to dynamically allocate space for the buffer to receive more using the C functions malloc and realloc.
This is the code I have so far:
char *buffer = NULL;
unsigned int i = 0;
unsigned long LEN = 200;
unsigned long cur_size = 0;
buffer = (char*)malloc(sizeof(char)*LEN);
do
{
if( status >= LEN )
{
cur_size += status;
buffer = (char*)realloc(buffer, cur_size);
}
status = recv(cSocket, buffer, LEN, 0);
if( status == 0 )
{
printf("Bye\n");
}
else if( status > 0 )
{
printf("%d\n", status);
}
else
{
printf("socket error=%d\n", WSAGetLastError());
break;
}
}while( status > 0 );
printf("%s\n", buffer);
It still doesn't print the whole source code. How should I go about this?
Pseudocode:
buffer = 'len chars';
loop:
if( status >= buffer ) buffer = 'resize to status chars';
status = recv(sock, buffer, len, 0);
end loop
As you resize the buffer in advance this needs to be reflected by its size. Which currently is not the case.
To fix this you could, for example, initialise cur_size with LEN by changing
unsigned long cur_size = 0;
to
unsigned long cur_size = LEN;
Assuming the fix above, you want to append to the buffer and not overwrite it with every call to recv().
To do so change this line
status = recv(cSocket, buffer, LEN, 0);
to be
status = recv(cSocket, buffer + cur_size - LEN, LEN, 0);
A more straight forward approach would be to not track the size of the buffer, but the number of bytes received and just always increase the buffer by a constant size.
Also the two calls to allocate memory can be replaced by one:
char *buffer = NULL;
unsigned long LEN = 200;
unsigned long bytes_received = 0;
unsigned long cur_size = 0;
int status = 0;
do
{
if (bytes_received >= cur_size)
{
char * tmp;
cur_size += LEN;
tmp = realloc(buffer, cur_size);
if (NULL == tmp)
{
fprintf(stderr, "realloc error=%d\n", WSAGetLastError());
break;
}
buffer = tmp;
}
status = recv(cSocket, buffer + bytes_received, LEN, 0);
if (status == 0)
{
printf("Bye\n");
}
else if (status > 0)
{
bytes_received += status;
printf("%d\n", status);
}
else /* < 0 */
{
fprintf(stderr, "socket error=%d\n", WSAGetLastError());
}
} while (status > 0);
printf("%s\n", buffer);
Well, after a bit of research, I came across this website and finally found what I was looking for.
Binary tides
Although it uses linux's fcntl, the windows equivalent is ioctlsocket which is used to set the socket's non-blocking mode.
To see the exact function, visit the website. I modified the version and set my socket to blocking mode.
int total_recv(SOCKET s)
{
int size_recv = 0, total_size = 0, block = 00;
char chunk[BUFLEN];
ioctlsocket(s, FIONBIO, (unsigned long*)&block); // set mode to block
// not necessary but clarification of function, mode is block by
// default
while( 1 )
{
memset(chunk, 0, BUFLEN);
if( ( size_recv = recv(s, chunk, BUFLEN, 0) ) == SOCKET_ERROR )
{
printf("Error receiving\n");
}
else if( size_recv == 0 )
{
break;
}
else
{
total_size += size_recv;
// i used file since console wouldn't show full source code
FILE *fp = NULL;
fp = fopen("source.txt", "a");
fprintf(fp, chunk);
fclose(fp);
}
}
return total_size;
}

Socket Programming -- recv() is not receiving data correctly

I have looked at the similar threads but can't seem to find anything that could solve my problem.
I am programming a server that could send an image(jpg) file from the path sent to it from the client. I am using send/recv functions in C for that.
I read files one chunk of data at a time and send it to the client that receives the contents and writes them at some location to build the file.
Problem is 'recv' doesn't receive the number of bytes sent by the 'send'.
As part of debugging, I have tried different buffer sizes and '128' buffer size doesn't give me any problem, and the file is successfully transferred and built.
However, for '32' and '64' bit buffers, 'recv' receives '32' bit or '64' bit data at the last chunk, even though the data sent by the server is less than either '32' bit or '64' bit. And, for '256', '512', '1024' so on, 'recv' returns ONLY '128' bits at EXACTLY one of the responses, even though the server sends complete chunk, i.e. '256' or'512', depending on the buffer size.
I'll appreciate any advise for debugging. Following code is for the relevant parts only, but I can provide more, should someone requires it.
//Client Code
#define BUFFER_SIZE 4096
#define HEADER_LEN 512
const char * const scheme = "GETFILE";
const char* const method = "GET";
const char * const end_marker = "\\r\\n\\r\\n";
struct gfcrequest_t
{
int filelen;
char cport[12];
char servIP[50];
gfstatus_t ret_status;
char spath[1024];
int tot_bytes;
char filecontent[BUFFER_SIZE];
void (*fl_handler)(void *, size_t, void *);
void * fDesc;
void (*head_handler)(void *, size_t, void *);
void * headarg;
};
static pthread_mutex_t counter_mutex;
gfcrequest_t *gfc;
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
static char *stringFromError(gfstatus_t stat)
{
static const char *strings[] = {"GF_OK", "GF_FILE_NOT_FOUND", "GF_ERROR", "GF_INVALID"};
return strings[stat];
}
int getFileRequestHeader(char * req_header)
{
return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,gfc->spath, end_marker);
// return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,"/courses/ud923/filecorpus/yellowstone.jpg", end_marker);
}
gfcrequest_t *gfc_create()
{
gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t));
return gfc;
}
void gfc_set_server(gfcrequest_t *gfr, char* server)
{
strcpy(gfr->servIP, server);
}
void gfc_set_path(gfcrequest_t *gfr, char* path)
{
strcpy(gfr->spath, path);
}
void gfc_set_port(gfcrequest_t *gfr, unsigned short port)
{
snprintf(gfr->cport,12, "%u",port);
}
void gfc_set_headerfunc(gfcrequest_t *gfr, void (*headerfunc)(void*, size_t, void *))
{
gfr->head_handler = headerfunc;
}
void gfc_set_headerarg(gfcrequest_t *gfr, void *headerarg)
{
/*have to change this...*/
gfr->headarg = headerarg;
}
int isEndMarker(char *iheader, int start)
{
char *marker = "\\r\\n\\r\\n";
int i = 0; int ind=0;
while (ind <= 7)
{
if (iheader[start++] == marker[ind++])
{
continue;
}
return 0;
}
return 1;
}
int getFileLen(char *resp_header, gfcrequest_t *gfr)
{
char scheme[8];
char status[4];
int istatus;
char filelen[12];
int contentlen = 0;
int fileindex = 0;
char end_marker[12];
int fexit=0;
int end=0;
sscanf(resp_header, "%7s%3s", scheme, status);
istatus = atoi(status);
if (istatus == 200)
{
gfr->ret_status = GF_OK;
}
else if (istatus == 400)
{
gfr->ret_status = GF_FILE_NOT_FOUND;
}
else if (istatus == 500)
{
gfr->ret_status = GF_ERROR;
}
if (!strcmp(scheme, "GETFILE") && (istatus == 200 || istatus == 400 || istatus == 500))
{
int index = 10;
while(1)
{
if (resp_header[index] == '\\')
{
end = isEndMarker(resp_header, index);
}
if (end)
break;
filelen[fileindex++] = resp_header[index++];
}
filelen[fileindex] = '\0';
}
int head_len = strlen(scheme) + strlen(status) + strlen(filelen) + 8;
return atoi(filelen);
}
void gfc_set_writefunc(gfcrequest_t *gfr, void (*writefunc)(void*, size_t, void *))
{
gfr->fl_handler = writefunc;
}
void gfc_set_writearg(gfcrequest_t *gfr, void *writearg)
{
gfr->fDesc = writearg;
}
int gfc_perform(gfcrequest_t *gfr){
struct addrinfo hints, *servinfo, *p;
int sockfd, rv, totalBytesRcvd;
char req_header[HEADER_LEN];
int bytesRcvd;
int header_len;
char s[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof(hints));
memset(req_header,0,sizeof(req_header));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; //use my IP
if ((rv = getaddrinfo(NULL, gfr->cport, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if (p == NULL)
{
fprintf(stderr, "client: failed to connect\n");
return 2;
}
//printf("connected...\n");
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof(s));
//Ahsan
// printf("Before getFileRequestHeader...\n");
header_len = getFileRequestHeader(req_header);
//printf("Header Description:%s, Header Len: %u\n", req_header, header_len);
if (send(sockfd, req_header, header_len, 0) != header_len)
perror("send() sent a different number of bytes than expected");
if ((bytesRcvd = recv(sockfd, gfr->filecontent, BUFFER_SIZE, 0)) <= 0)
perror("recv() failed or connection closed prematurely");
//printf("Header Received: %s\n", gfr->filecontent);
gfr->filelen = getFileLen(gfr->filecontent, gfr);
//printf("File Length: %d\n", gfr->filelen);
/* Receive the same string back from the server */
int req_no=1;
gfr->tot_bytes = 0;
while ( 1 )
{
printf("Request: %d ", req_no++);
ssize_t nb = recv( sockfd, gfr->filecontent, BUFFER_SIZE, 0 );
if ( nb == -1 ) err( "recv failed" );
if ( nb == 0 ) {printf("zero bytes received...breaking");break;} /* got end-of-stream */
gfr->fl_handler(gfr->filecontent, nb, gfr->fDesc);
gfr->tot_bytes += nb;
printf("Received Bytes: %zd Total Received Bytes: %d\n", nb, gfr->tot_bytes);
}
return 0;
}
/*
* Returns the string associated with the input status
*/
char* gfc_strstatus(gfstatus_t status)
{
return stringFromError(status);
}
gfstatus_t gfc_get_status(gfcrequest_t *gfr){
return gfr->ret_status;
}
size_t gfc_get_filelen(gfcrequest_t *gfr)
{
return gfr->filelen;
}
size_t gfc_get_bytesreceived(gfcrequest_t *gfr)
{
return gfr->tot_bytes;
}
void gfc_cleanup(gfcrequest_t *gfr)
{
free(gfr);
gfr=NULL;
}
void gfc_global_init()
{
;
// pthread_mutex_lock(&counter_mutex);
//
// gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t));
//
// pthread_mutex_unlock(&counter_mutex);
}
void gfc_global_cleanup()
{
;
// pthread_mutex_lock(&counter_mutex);
//
// free(gfc);
//
// pthread_mutex_unlock(&counter_mutex);
}
//Server Code
struct gfcontext_t
{
int sockfd;
int clntSock;
};
struct gfserver_t
{
char port[12];
unsigned short max_npending;
char fpath[256];
ssize_t (*fp_handler)(gfcontext_t *ctx, char *, void*);
int *handler_arg;
};
/*Variable decalation*/
static gfserver_t *gfserv;
static gfcontext_t *gfcontext;
int isEndMarker(char *iheader, int start)
{
char *marker = "\\r\\n\\r\\n";
int i = 0; int ind=0;
while (ind <= 7)
{
if (iheader[start++] == marker[ind++])
{
//printf("Header Char:%c Marker:%c\n", iheader[start], marker[ind]);
//start++;
continue;
}
return 0;
}
//printf("Its a marker!!!\n");
return 1;
}
int parseHeader(char *iheader, gfserver_t *gfs, int hlen)
{
//"GETFILEGET/courses/ud923/filecorpus/road.jpg\r\n\r\n"
char scheme[8];
char method[4];
// char path[256];
int pathindex = 0;
char end_marker[12];
int end = 0;
int fexit=0;
sscanf(iheader, "%7s%3s", scheme, method);
int i=10;
if (iheader[i] == '/')
{
// printf("Path has started...\n");
if (!strcmp(scheme, "GETFILE") && !strcmp(method, "GET"))
{
while(1)
{
if (iheader[i] == '\\')
{
end = isEndMarker(iheader, i);
}
if (end)
break;
gfs->fpath[pathindex++] = iheader[i++];
}
gfs->fpath[pathindex] = '\0';
}
}
printf("Scheme: %s Method:%s Path:%s\n", scheme, method, gfs->fpath);
return 0;
}
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
ssize_t gfs_sendheader(gfcontext_t *ctx, gfstatus_t stat, size_t file_len)
{
char resp_header[MAX_REQUEST_LEN];
char end_marker[12] = "\\r\\n\\r\\n";
sprintf(resp_header, "GETFILE%d%zd%s",stat, file_len, end_marker);
printf("Response: %s\n", resp_header);
if (send(ctx->clntSock, resp_header, MAX_REQUEST_LEN, 0) != MAX_REQUEST_LEN)
perror("send() failed");
return 0;
}
ssize_t gfs_send(gfcontext_t *ctx, void *data, size_t len)
{
size_t total = 0;
size_t bytesLeft = len;
size_t n;
int debug_req=1;
while (total < len)
{
n = send(ctx->clntSock, data+total, bytesLeft, 0);
if (n == -1) { printf("Nothing to send...\n"); break; }
fprintf(stderr, "Tries: %d Bytes Sent: %zu\n", debug_req++, n);
total += n;
bytesLeft -= n;
}
// if ( shutdown( ctx->clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
return total;
}
void gfs_abort(gfcontext_t *ctx){
close(ctx->clntSock);
close(ctx->sockfd);
free(ctx);
free(gfserv);
perror("aborting...");
exit(1);
}
gfserver_t* gfserver_create()
{
gfserv = (gfserver_t*) malloc(1 * sizeof(gfserver_t));
gfcontext = (gfcontext_t*) malloc(1*sizeof(gfcontext_t));
return gfserv;
}
void gfserver_set_port(gfserver_t *gfs, unsigned short port)
{
//set port number in gfs structure
snprintf(gfs->port,12, "%u",port);
}
void gfserver_set_maxpending(gfserver_t *gfs, int max_npending)
{
//set maxpending connections
gfs->max_npending = max_npending;
}
void gfserver_set_handler(gfserver_t *gfs, ssize_t (*handler)(gfcontext_t *, char *, void*))
{
gfs->fp_handler = handler;
}
void gfserver_set_handlerarg(gfserver_t *gfs, void* arg)
{
gfs->handler_arg = (int *)arg;
}
void gfserver_serve(gfserver_t *gfs)
{
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage clntAddr; //connectors address information
socklen_t clntSize;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int rv;
int rcvMsg = 0;
char recvBuff[MAX_REQUEST_LEN];
char *req_path;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; //using stream socket instead of datagrams
hints.ai_flags = AI_PASSIVE; //use my IP
if ((rv = getaddrinfo(NULL, gfs->port, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next)
{
if ((gfcontext->sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("server: socket");
continue;
}
//get rid of 'address already in use' error.
if (setsockopt(gfcontext->sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
if (bind(gfcontext->sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(gfcontext->sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL)
{
fprintf(stderr, "server: failed to bind.\n");
return 2;
}
freeaddrinfo(servinfo); // no need of servinfo structure anymore
if (listen(gfcontext->sockfd, gfs->max_npending) == -1)
{
perror("listen");
exit(1);
}
//printf("server: waiting for connetions...\n");
while(1)
{
clntSize = sizeof(clntAddr);
gfcontext->clntSock = accept(gfcontext->sockfd, (struct sockaddr *)&clntAddr, &clntSize);
if (gfcontext->clntSock == -1)
{
perror("accept");
continue;
}
inet_ntop(clntAddr.ss_family, get_in_addr((struct sockaddr *)&clntAddr), s, sizeof(s));
//printf("server: got connection from %s\n", s);
if (!fork())
{ // this is the child process
if ((rcvMsg = recv(gfcontext->clntSock, recvBuff, MAX_REQUEST_LEN, 0)) < 0)
{
perror("recv() failed");
exit(1);
}
/*Still to parse received request...*/
//printf("Recd Header: %s, Recd %d bytes\n",recvBuff, rcvMsg);
/*Parse the received header...*/
int len = parseHeader(recvBuff, gfs, rcvMsg);
//printf("Requested Path: %s\n", gfs->fpath);
if (gfs->fp_handler(gfcontext, gfs->fpath, NULL) < 0)
{
printf("some problem...\n");
}
if ( shutdown( gfcontext->clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
//close(gfcontext->clntSock);
}
}
}
//Server gf_send is being called from following function handler function:
Handler:
ssize_t handler_get(gfcontext_t *ctx, char *path, void* arg){
int fildes;
size_t file_len, bytes_transferred;
ssize_t read_len, write_len;
char buffer[BUFFER_SIZE];
printf("Path: %s\n", path);
if( 0 > (fildes = content_get(path)))
return gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0);
/* Calculating the file size */
file_len = lseek(fildes, 0, SEEK_END);
gfs_sendheader(ctx, GF_OK, file_len);
/* Sending the file contents chunk by chunk. */
int req=1;
bytes_transferred = 0;
while(bytes_transferred < file_len){
read_len = pread(fildes, buffer, BUFFER_SIZE, bytes_transferred);
if (read_len <= 0){
fprintf(stderr, "handle_with_file read error, %zd, %zu, %zu", read_len, bytes_transferred, file_len );
gfs_abort(ctx);
return -1;
}
printf("Request No: %d ", req++);
write_len = gfs_send(ctx, buffer, read_len);
if (write_len != read_len){
fprintf(stderr, "handle_with_file write error");
gfs_abort(ctx);
return -1;
}
bytes_transferred += write_len;
}
printf("Total Bytes sent to client: %zu\n", bytes_transferred);
return bytes_transferred;
}
You didn't specify, so I am assuming you are using TCP here (send/receive semantics is different with UDP),
You are suffering from a very common misconception that one send on one end of a TCP socket corresponds to one receive of sent number of bytes on the other end. This is wrong.
In fact, TCP socket is a bi-directional stream of bytes with no notion of messages. One write can correspond to many reads on the other end, and vise versa. Treat it as a stream.
You need to keep number of bytes sent and received as returned from sending and receiving system calls.
It is also important to let the other side know how much data you are sending, so it will know when, say, an image is fully transferred. This is the job of an application-level protocol that you have to either come up with or use an existing one.
Edit 0:
Here is what looks needed even before setting up any meaningful protocol between client and the server.
First the sending code:
size_t total = 0;
while ( total != len ) {
ssize_t nb = send( s, data + total, len - total, 0 );
if ( nb == -1 ) err( "send failed" );
total += nb;
}
if ( shutdown( s, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
/* also need to close client socket, see below */
Then the receiving code:
char buffer[BUFFER_SIZE]; /* somewhere, might be static */
size_t total = 0; /* everything received */
while ( 1 ) {
ssize_t nb = recv( s, buffer, BUFFER_SIZE, 0 );
if ( nb == -1 ) err( "recv failed" );
if ( nb == 0 ) break; /* got end-of-stream */
if ( write( file_fd, buffer, nb ) == -1 ) err( "file write failed" );
total += nb;
}
/* send an ack here */
if ( close( s ) == -1 ) err( "socket close failed" );
if ( close( file_fd )) err( "file close failed" );
printf( "received and saved total of %zu bytes\n", total );
Then your application-level protocol might be as simple as server sending, say, 64-bit file length immediately after accepting new client connection (you need to decide what endianness to use for that), then after sending that many bytes to the client and shutting down writing on the socket, waiting for the client to acknowledge successful receipt of data. That might be that same number back, or just a single byte - up to you, then finally closing the socket. That way the client knows upfront how many bytes to expect, and the server knows that transfer was successful.
After getting this simple version working you can extend it to allow multiple file transfers per connection, and/or dive into IO multiplexing with select(2)/poll(2).
Hope this helps.
First of all: recv() does not always receive the data in the chunks that it was sent by send(), as a matter of fact - it rarely does - because of buffering (for example, you send 256-bytes receive two buffers of 128-bytes each)
Now to your error: I think the problem is that you are not be calling select() with a FD_SET to reset your socket to a "ready to receive" state before calling recv() a second time.
I have a metric-ton of winsock/c-sockets code on my site if you want to dig through it.
Let me know if I can expand on this answer, I'd be happy to provide additional assistance!
gfr->fl_handler(gfr->filecontent, BUFFER_SIZE, gfr->fDesc);
Usual problem. You're assuming the read filled the buffer. It should be:
gfr->fl_handler(gfr->filecontent, bytesRcvd, gfr->fDesc);

client can't read after connection with server in tcp

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;
}

Resources