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;
}
Related
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);
}
I have a checkpoint file that receives a server state. This states represents serialized commands that pass trough my network.
I'm trying to read the file but the it gets stuck on the read while loop.
My read function:
struct message_t *pmanager_readop(int fd){
if (fd < 0) return NULL;
// Variables
char *buffer = NULL;
int result, msg_size;
struct message_t *msg;
// Check if file has data
lseek (fd, 0, SEEK_END);
int size_ckp = lseek(fd, 0, SEEK_CUR);
if (size_ckp <= 0)
return NULL;
// Read message size
result = read_all(fd, (char *) &msg_size, 4);
if (result < 0) {
return NULL;
}
msg_size = ntohl(msg_size);
// ............
My read_all() function:
int read_all(int sock, char *buf, int len){
int bufsize = len;
while(len > 0){
int res = read(sock,buf,len);
if(res < 0){
if (errno == EINTR) continue;
return res;
}
buf += res;
len -= res;
}
return bufsize;
}
I use this same function to read data from my server/client connection with the same serialization and format but with a socket descriptor, and it works perfectly.
You ought to handle the case that read() returns 0, telling you that the other side shut-down the connection if reading from a socket descriptor, or EOF encountered if reading from a file descriptor.
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;
}
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;
}
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;
}