Sending huge array through socket - c

I am trying to send a char array of 100.000.000 bytes (maybe more) from a server to a client through a TCP socket in C.
I am doing this like that:
char *array; // global array malloc'd (SIZE)
//#######################
// server code
//#######################
int i;
int SIZE = 100000000
for (i = 0; i < SIZE; i = i + 4){
write(id, &array[i], 4); // write 4 bytes every time
}
//#######################
// client code
//#######################
int i;
int SIZE = 100.000.000
for (i = 0; i < SIZE; i = i + 4)
read(id, array + i, 4); // read 4 bytes
Problems:
1) When I try to send more bytes something goes wrong with the transfer. For example if I change 4 to 100, it says "broken pipe". Why does that happen?
2) I know this is not a "safe" way for reading/writing since I am not checking read() and write() return values. How can I do that?
3) Do I have to use htonl() and ntohl() functions?

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
//in #param
//#param fd the socket file descriptor
//#param array an array of data source to write to send to the connected client
//#param SIZE the size of data source to send to the client
//#param sz_emit the size of data to send in one loop step
//out #param
//total length of data emited to the client
int write_to_client(int fd, char* array, int SIZE, int sz_emit)
{
//#######################
// server code
//#######################
int i=0, sz=0;
for(i = 0; i < SIZE; i += sz_emit )
{
while(sz_emit-sz)
{
sz+=write(id, array+i+sz, sz_emit-sz);
}
sz = 0;
}
return i;
}
//#######################
// client code
//#######################
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
//in #param
//#param fd is the file descriptor of the socket to read from
//#param SIZE the size of datas you want to read from the socket
//#param sz_received the size of byte to read in one loop step
//#param length, the length of data received
//#param read_err if 0 no error if -1 an error occurs use errno from #include <errno.h> to know more about that error
//out #param
//a pointer to an array of size SIZE containing the data readed
char* receive_from_server(int fd, int SIZE, int sz_received, int* length, int* read_err)
{
*read_err = 0;
int i = 0, sz = 0, rt = 0, count=0;
char *array = (char *)malloc(SIZE);
memset(array, 0, SIZE);
for (i = 0; i < SIZE; i += sz_received)
{
while(sz_received-sz)
{
rt = read(id, array + i + sz, sz_received-sz);
if(rt==-1)
{
*read_err=rt;
printf("an error occurs\n");
goto l;
}
if(!rt)goto l;
sz+=rt;
count += sz;
}
sz = 0;
}
l: *length = count;
return array;
}
usage:
//server side
int SIZE = 100000000;
char array_to_send[SIZE]={'r'};
int sz_data_emited = write_to_client(sock, array_to_send, SIZE, 4);
printf("how many byte data emited:%d\n", sz_data_emited);
//client side
int SIZE = 100000000, length = 0, read_err=0;
char*array_received = NULL;
array_received = receive_from_server(sock, SIZE, 4, &length, &read_err);
if(!read_err)printf("get some datas\n");
// free array_received when finished...free(array_received)
some notes:
you need to pay attention on endianess when you want to transfert a multi-byte entity for example a short, int, long, utf-16 etc but if your datas are utf-8 or ascii text you don't need it.

I think that Narcisse Doudieu Siewe answer has some errors. I suppose it fails when SIZE isn't multiplicity of sz_emit.
Example there is 20 bytes that we sent in 8 bytes chunkes than the last data chunk (or packet) will be 4 byte long. Than if we try to send the last 8 bytes chunk and there is only 4 bytes left while loop will be infinite as while(8 - 4), and it never reaches sz = 8, as the next send will just increment by 0. So I write modification like this (not tested, I will test soon and write also second method taking into account this boundary condition).
/**
* #param sock_fd - the file descriptor of the socket to write (send) data to
* #param packetLength - the size of data to send in one packet
* #param data - binary data to send (unsigned char array)
* #param dataLength - the size of all binary data to send
* #return - status code SUCCESS or FAILURE
*/
result_t send_binary(const sock_fd_t sock_fd, const size_t packetLength, const unsigned char *data, const size_t dataLength) {
ssize_t leftPacketLength = 0;
ssize_t offset = 0;
ssize_t sentPacketLength = 0;
// send each packet of data in the loop
for(int leftDataLength=dataLength; leftDataLength>0; leftDataLength -= packetLength) {
leftPacketLength = (leftDataLength > packetLength) ? packetLength : leftDataLength;
while(leftPacketLength > 0) {
sentPacketLength = send(sock_fd, data + offset, leftPacketLength, 0);
if(sentPacketLength < 0) {
fprintf(stderr, "%s: Error while sending data to the socket.\n", __func__);
perror(errno);
return FAILURE;
}
offset += sentPacketLength;
leftPacketLength -= sentPacketLength;
}
}
if(offset != dataLength)
return FAILURE;
return SUCCESS;
}
/**
* #param sock_fd - the file descriptor of the socket to read (recieve) data from
* #param packetLength - the size of data to recieve in one packet
* #param data - binary data received (unsigned char array) - previously allocated
* #param dataLength - the size of all binary data received - previously defined
* #return - status code SUCCESS or FAILURE
*/
result_t recv_binary(const sock_fd_t sock_fd, const size_t packetLength, unsigned char *data, const size_t dataLength) {
ssize_t leftPacketLength = 0;
ssize_t offset = 0;
ssize_t recvedPacketLength = 0;
for(int leftDataLength=dataLength; leftDataLength > 0; leftDataLength -= packetLength) {
leftPacketLength = (leftDataLength > packetLength) ? packetLength : leftDataLength;
while(leftPacketLength > 0) {
recvedPacketLength = recv(sock_fd, data + offset, leftPacketLength, 0);
if(recvedPacketLength < 0) {
fprintf(stderr, "%s: Error while receiving data from the socket.\n", __func__);
perror(errno);
return FAILURE;
}
offset += recvedPacketLength;
leftPacketLength -= recvedPacketLength;
}
}
if(offset != dataLength)
return FAILURE;
return SUCCESS;
}
There is also need to send size of binary data send/recv via socket before transfering actual binary data. It's needed to know how many bytes we need to read.

Related

Pipe gets stuck during write

When I'm trying to write to one of my pipes to communicate with a child process, it gets stuck. My first guess was that it was because its buffer was full, and something has to read from it, for it to continue, so I followed Increasing the maximum pipe size in linux instructions, and to my surprise, my maximum buffer size is 1048576. I'm trying to write 160000 bytes into my pipe. I don't understand why it's getting stuck.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <math.h>
#define SIZE 320000
#define FILENAME "Practical_Q_1numbers.txt"
extern long fun(long a, long b);
void write_numbers(int process_id,int* pipefds, long* numbers) {
long process_numbers[SIZE / 8];
memcpy(process_numbers, &numbers[SIZE / 8 * process_id], SIZE / 8 * sizeof(long));
printf("This gets printed\n");
// Pipe Limit
write(pipefds[1], process_numbers, SIZE / 8 * sizeof(long));
printf("This doesnt\n");
}
// Correct
long calculate(long* numbers, int left, int right) {
if(left == right){
return numbers[left];
}else if(left + 1 == right) {
return fun(numbers[left], numbers[right]);
}
int middle = (right + left) / 2;
long l_ans = calculate(numbers, left, middle);
long r_ans = calculate(numbers, middle + 1, right);
return fun(l_ans, r_ans);
}
void calculateHelper(int id, int* pipefds) {
long* ptr = (long*)malloc(SIZE / 8 * sizeof(long));
read(pipefds[0], ptr, SIZE / 8 * sizeof(long));
long res = calculate(ptr, 0, SIZE / 8 - 1);
// write(pipefds[1], &res, sizeof(res));
return;
}
int main() {
// Read the file as parent.
FILE *myFile;
myFile = fopen(FILENAME, "r");
long* ptr = (long*)malloc(SIZE * sizeof(long));
// Reads correctly
for(int i = 0; i < SIZE; i++) {
fscanf(myFile, "%ld", &ptr[i]);
}
int pipefds[8][2];
for(int i = 0; i < 8; i++) {
pipe(pipefds[i]);
}
for(int i = 0; i < 1; i++) {
write_numbers(i,pipefds[i], ptr);
pid_t a = fork();
if(a == 0) {
// Child process
calculateHelper(i,pipefds[i]);
exit(0);
}
}
// Wait for your children to terminate
while(wait(NULL) > 0);
// long* finalContenders = (long*) malloc(8 * sizeof(long));
// for(int i = 0; i < 8; i++) {
// read(pipefds[i][0], &finalContenders[i], sizeof(long));
// }
// long ans = calculate(finalContenders, 0, 7);
// printf("%ld\n",ans);
}
Fun is a function responsible for calculating the GCD of 2 numbers.
Your problem is here:
for(int i = 0; i < 1; i++) {
write_numbers(i,pipefds[i], ptr);
pid_t a = fork();
if(a == 0) {
// Child process
calculateHelper(i,pipefds[i]);
exit(0);
}
You write the data to the pipe before there's a process to read from it. That's deadlock-prone no matter how large the maximum pipe buffer size might be.
This would be better:
for(int i = 0; i < 1; i++) {
pid_t a = fork();
if(a == -1) {
// handle error
}
else if(a == 0) {
// Child process
calculateHelper(i,pipefds[i]);
exit(0);
}
else {
write_numbers(i,pipefds[i], ptr);
}
You'd also be better off writing smaller chunks to the pipe no matter what. Right now, you don't handle partial write() results at all (bolding mine):
The write() function shall attempt to write nbyte bytes from the buffer pointed to by buf to the file associated with the open file descriptor, fildes.
There's no portable guarantee on any call to write() that the entirety of your requested buffer will be written.
The easiest way to do that is to create a writeAllBytes() wrapper around write, such as:
#define CHUNK_SIZE ( 4 * 1024 )
ssize_t writeAllBytes( int fd, void *data, size_t bytes )
{
// need to do pointer arithmetic on the value so
// it can't be a void *
char *buf = data;
ssize_t totalWritten = -1;
while ( bytes > 0 )
{
size_t bytesToWrite = bytes;
if ( bytesToWrite > CHUNK_SIZE )
{
bytesToWrite = CHUNK_SIZE;
}
ssize_t bytesWritten = write( fd, buf, bytesToWrite );
if ( bytesWritten <= 0 )
{
break;
}
buf += bytesWritten;
totalWritten += bytesWritten;
bytes -= bytesWritten;
}
return( totalWritten );
}
Similarly, a corresponding readAllBytes() would also be appropriate.

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

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

is it safe to call write successively when using a sock_stream?

I need to write a small client/server application in C on Linux.
I have built a short example in order to explore a little bit more since i am new to network programming.
I am basically trying to send an array of double dynamically allocated by the client.
I found the following way to do it ( client side ) :
write(sd,&datas.size,sizeof(int)); /* send size */
write(sd,datas.yi,datas.size*sizeof(double));/* send array */
and on the server side :
read(sd_cli,&datas.size,sizeof(int)); /* receive size */
datas.yi=(double *)malloc(datas.size*sizeof(double));
read(sd_cli,datas.yi,datas.size*sizeof(double)); /* receiving datas */
At first sight my code seems to work fine.
But since the write calls are non blocking, i ask myself if the read sequence can receive , for example the array of double before its size ?
Is there any guarantee that this can never happen ?
Thanks.
Sockets of type SOCK_STREAM provide reliable, in-order data transmission, though details depend on the nature of the underlying transport. The reader will receive all the data successfully written (if it in fact chooses to read them all), byte-for-byte in the order they were written, but not necessarily in the same size chunks.
Blocking vs. non-blocking has nothing to do with it, though I don't actually see what makes you say your writes are non-blocking. Perhaps you're remarking on the fact that neither write() nor read() promises to transfer the full number of bytes requested on any given call. That in itself provides no guarantee against blocking, but you absolutely do need to account for it correctly, especially with sockets, and even more especially if you really have put one or both ends of the socket in non-blocking mode. The original version of your question seemed to claim that you do account for it.
In any case, barring some kind of kernel bug, your client will never read the array size after any part of the array, nor otherwise receive bytes in a different relative order than they were written.
To be perfectly clear, however, here are reasonable implementations for reading and writing variable-size double arrays via a stream socket. They assume that sender and receiver have identical representations of type double, which will certainly be the case for UNIX-domain sockets. They are not at all trivial, though the helper functions comprising around half the code are suitable for reuse:
/******
* helper functions
*/
/*
* Returns the number of bytes written, which may be zero, or a number
* less than zero on failure.
*/
ssize_t write_fully(int fd, const void *buf, size_t count) {
const unsigned char *next = buf;
size_t remaining = count;
while (remaining) {
ssize_t n_written = write(fd, next, remaining);
if (n_written < 0) {
/* error */
return n_written;
} else {
assert(n_written <= remaining);
next += n_written;
remaining -= n_written;
}
}
/* all bytes successfully written */
return count;
}
/*
* Returns the number of bytes read on success, or a number less
* than zero on error. It is accounted "success" if the end of the stream
* is reached before the requested number of bytes is read; that case
* can be distinguished by the return value, but no recovery is
* possible.
*/
ssize_t read_fully(int fd, void *buf, size_t count) {
unsigned char *next = buf;
size_t remaining = count;
while (remaining) {
ssize_t n_read = read(fd, next, remaining);
if (n_read < 0) {
/* error */
return n_read;
} else if (n_read) {
assert(n_read <= remaining);
next += n_read;
remaining -= n_read;
} else {
/* premature end of file */
return count - remaining;
}
}
/* all bytes successfully read */
return count;
}
/******
* Array-transfer functions
*/
/* returns 0 on success, else nonzero */
int write_double_array(int fd, unsigned n, double d[n]) {
ssize_t bytes_written;
bytes_written = write_fully(fd, &n, sizeof(n));
if (bytes_written < 0) return bytes_written;
bytes_written = write_fully(fd, d, n * sizeof(double));
return (bytes_written < 0) ? bytes_written : 0;
}
/*
* returns 0 on success, else nonzero.
* On success, the caller takes responsibility for freeing the
* dynamically-allocated result array.
*/
int read_double_array(int fd, unsigned *n, double **d) {
unsigned temp_n;
ssize_t bytes_read = read_fully(fd, &temp_n, sizeof(temp_n));
if (bytes_read < 0) {
return -1;
} else if (bytes_read != sizeof(temp_n)) {
return 1;
} else if (temp_n) {
size_t n_bytes = temp_n * sizeof(double);
double *temp = malloc(n_bytes);
if (!temp) return -1; /* allocation failure */
if (read_fully(fd, temp, n_bytes) < n_bytes) {
free(temp);
return -1;
}
/* success */
*d = temp;
}
*n = temp_n;
return 0;
}
You could implement the array-transfer protocol differently, but that approach sends the data in the same form that you claim to do. You cannot safely do it any more simply than that.

recv() until a NUL byte is received?

I'm trying to receive a single packet at a time from the server, since packets are going too fast, and each is of undefined size, calling recv() with number of bytes to read will read the first packet and maybe a part of the second packet. Since each packet is NULL terminated, I thought reading byte by byte until a NULL byte is received.
int recvLen = 0;
char TB;
char recvBuffer[1024];
while (recv(Socket, &TB, 1, 0) > 0 && TB != 0 && recvLen < 1024)
{
recvBuffer[recvLen] = TB;
recvLen++;
}
I don't think this method is efficient at all. If the server sent 1024 bytes, recv() will be called 1024 times.
Is there any other method to recv() until a NULL char is received, or some better method than this one I'm using?
EDIT:
i added the packet size infront of the data sent from the server, but now, if a false packet or even sometimes for no reason, packets gets messed up and no correct data is received. here is my code
#define UPLOAD_LEN 2755
int PacketSize, recvLen;
char Size[4];
char recvBuffer[UPLOAD_LEN+1];
while(1)
{
if(recv(Socket,Size,4,0)>0)
{
Size[4] = '\0';
PacketSize = atoi(Size);
if (PacketSize > UPLOAD_LEN || PacketSize <= 0) continue;
recvLen = recv(Socket, recvBuffer, PacketSize, 0);
} else recvLen = -1;
if (recvLen > 0)
{
recvBuffer[recvLen] = '\0';
ProcessData(recvBuffer);
}
else
{
closesocket(Socket);
}
}
I have never understood why communications protocols never support the one use case programmers expect to be able to do: exchange arbitrarily sized blobs with sends and recv's aligned on boundaries.
So theres no real shortcut here. You need to keep a persistent buffer that holds any data left over from the previous call to recv. Keep adding data to the end as you receive it, and return up to the terminating zero each time you find one. You'll probably have at least a partial following packet, so move that to the start of the buffer to serve as your initial state on the next call.
Create a buffer and extract your protocol messages from that. If the buffer does not contain a complete message, then recv() until it does. Here's a simple C implementation to buffer a socket (lightly tested, compiles on MS VS2008):
#include <winsock2.h>
#include <string.h>
typedef struct buffsock {
SOCKET s;
char* buf;
size_t maxlen;
size_t curlen;
} buffsock_t;
void buffsock_init(buffsock_t* bs,SOCKET s,size_t maxlen)
{
bs->s = s;
bs->buf = malloc(maxlen);
bs->maxlen = maxlen;
bs->curlen = 0;
}
void buffsock_free(buffsock_t* bs)
{
free(bs->buf);
bs->buf = NULL;
bs->maxlen = 0;
bs->curlen = 0;
bs->s = INVALID_SOCKET;
}
/* Attempt to fill internal buffer.
* Returns 0 if socket closed.
* Returns number of additional bytes in buffer otherwise.
*/
int buffsock_fill(buffsock_t* bs)
{
int bytes;
bytes = recv(bs->s,bs->buf + bs->curlen,bs->maxlen - bs->curlen,0);
if(bytes == SOCKET_ERROR)
return -1;
bs->curlen += bytes;
return bytes;
}
/* Return up to <bytes> from buffered socket.
* If return value 0 socket was closed.
* If return value >0 and <bytes socket received partial message.
*/
int buffsock_bytes(buffsock_t* bs,size_t bytes,void* msg)
{
while(bs->curlen < bytes)
{
int result;
result = buffsock_fill(bs);
if(result == -1)
return -1; /* error on socket */
if(result == 0)
break;
}
if(bytes > bs->curlen)
bytes = bs->curlen;
memcpy(msg,bs->buf,bytes);
bs->curlen -= bytes;
memmove(bs->buf,bs->buf + bytes,bs->curlen);
return bytes;
}
/* Implmementation of a protocol with two big-endian bytes indicating
* msg size followed by <size> bytes of message.
* Returns -1 if error on socket.
* Returns -2 if partial message recv'd (shouldn't happen as long as
* internal buffer is bigger than max message size).
* Returns -3 if user buffer not big enough to hold message.
* Returns size of message otherwise.
*/
int get_protocol_message(buffsock_t* bs,void* msg,size_t maxlen)
{
int bytes;
u_short len;
bytes = buffsock_bytes(bs,sizeof(u_short),&len);
if(bytes == 0)
return 0; /* socket closed, no more messages */
if(bytes == -1)
return -1; /* error on socket */
if(bytes < sizeof(u_short))
return -2; /* partial message */
len = ntohs(len);
if(len > maxlen)
return -3; /* message exceeds user buffer */
bytes = buffsock_bytes(bs,len,msg);
if(bytes < len)
return -2; /* partial message */
return bytes;
}
Use it like this:
int len;
char msg[256];
buffsock_t bs;
/* open a socket */
buffsock_init(&bs,sock,1024);
len = get_protocol_message(&bs,msg,sizeof(msg));
The key is TCP/IP has no concept of message boundaries, so recv() can return 1 to number of bytes requested. The received buffer could contain multiple or even partial messages.
This code just appends received data into a buffer. The protocol requests bytes from the buffer, and the buffer is filled from the socket. as bytes are removed the remaining buffered data is shifted to the beginning of the buffer.
In this case, two bytes are requested, converted to a length, then the remaining bytes are requested. If a request can't be satisfied, more data is recv'd.
Hope this helps.
There are several ways that you could do this.
Option #1: Before sending out any information, send out an int at the front of your packet which contains the size of the packet. Read this int, and then allocate a buffer which is the length of the int that you just received. Then you can recv() the entire packet at one time.
Option #2: Read in 1024 bytes at a time. recv() will give you back the number of bytes read. You can then use strlen() to figure out if you have more than one packet in your buffer. It would probably make the most sense to make this recursive(assuming that you could have several packets in 1024 bytes); so that you split the packets based on NULL bytes.

Resources