How to receive long array of integers with TCP socket in C? - arrays

How can I send and receive a long array of integers using TCP sockets?
In the case of short array, the reception is possible using the function recv(.., 4*size of array) one time, however when the size of array is too long, I can't receive data correctly.
int main(void)
{
int listenfd = 0, connfd = 0;
int i,j,x;
int fd;
unsigned int *pic;
struct sockaddr_in serv_addr;
char *recvBuff;
clock_t t;
//Allocate memory for a 24-bit 640x480 rgb image
pic = (int*)malloc(10*sizeof(int));
recvBuff = (char*)malloc(1*sizeof(char));
for(i = 0; i < 10 ; i++){
pic[i] = 20;
}
//Create the TCP socket
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
memset(pic, '0', sizeof(pic));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(7); // la valeur du port
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listenfd, 10);
//fprintf(stdout,"End Creating Socket4\n");
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
while(1)
{
recv(connfd,recvBuff, sizeof(char),MSG_WAITALL);
//printf("BUFF: %s\n",recvBuff);
//Wait for client request
if(strcmp(recvBuff,"A")){
printf("Error in input\n");
}else
write(connfd, pic, 921600);
}
close(connfd);
}

In the case of short array, the reception is possible using the function recv(.., 4*size of array) one time
That is not guaranteed. Any call to recv() can return fewer bytes than requested, even as few as just 1 byte. So you always need to call recv() in a loop until you have actually received as many bytes as are you expecting, eg:
ssize_t recv_all(int skt, void *buf, size_t bufsize)
{
char *ptr = (char*) buf;
while (bufsize > 0)
{
ssize_t recvd = recv(skt, ptr, bufsize, 0);
if (recvd <= 0) return recvd;
ptr += recvd;
bufsize -= recvd;
}
return 1;
}
And then you can call it like this:
int32_t *arr = (int32_t*) malloc(sizeof(int32_t) * count);
...
if (recv_all(..., arr, sizeof(int32_t) * count) <= 0)
{
// error, or peer disconnected...
}
else
{
// use arr as needed...
}
You should do the same thing for send() too, eg:
ssize_t send_all(int skt, const void *buf, size_t bufsize)
{
const char *ptr = (const char*) buf;
while (bufsize > 0)
{
ssize_t sent = send(skt, ptr, bufsize, 0);
if (sent < 0) return sent;
ptr += sent;
bufsize -= sent;
}
return 0;
}
int32_t *arr = (int32_t*) malloc(sizeof(int32_t) * count);
...
if (send_all(..., arr, sizeof(int32_t) * count) < 0)
{
// error...
}
else
{
// arr sent in full...
}

First of all, it's worth noting that recv() doesn't care at all about the type of data it receives, only about its size.
So all you need is a function that calls recv() in a loop until it receives all of the requested data
// Returns the number of bytes read, or -1 in the case of an error
ssize_t recv_all(const int sock, void * const buf, const size_t n)
{
ssize_t len = 0;
ssize_t total_read = 0;
while (n > (size_t)total_read)
{
len = recv(sock, (char *)buf + total_read, n - total_read, MSG_WAITALL);
switch (len)
{
case -1:
return -1;
case 0:
break;
default:
total_read += len;
}
}
return total_read;
}

Related

Send a string with sendto() function in C

I have to get the numbers in a buffer with recvfrom() function and compute the sum. Then I have to use sprintf() function to get the result as a string and then I have to send this string with sendto() function. Everything is fine is my code except the result that I send with sendto(). Could you help me please ?
int recv_and_handle_message(const struct sockaddr *src_addr, socklen_t addrlen) {
// TODO: Create a IPv6 socket supporting datagrams
// TODO: Bind it to the source
// TODO: Receive a message through the socket
// TODO: Perform the computation
// TODO: Send back the result
int result = 0;
int sock = socket(AF_INET6, SOCK_DGRAM, 0);
if(sock == -1) return sock;
int err = bind(sock,src_addr,addrlen);
if (err == - 1) return err;
struct sockaddr_storage recever;
socklen_t len = sizeof(recever);
char buffer[1024];
int rec = recvfrom(sock, buffer, sizeof(char)*1024, 0, (struct sockaddr*)&recever, &len);
if(rec == -1) return rec;
int*current = (int*)&buffer;
for (int i = 0; i < rec/4; ++i){
result += current[i];
}
char res[1000];
int b = sprintf(res, "%d", result);
if(b == -1){
return -1;
}
char rese[b];
memcpy(rese, res, b);
//for (int i = 0; i < b; ++i){
// rese[i] = res[i];
//}
int r = sendto(sock, &rese, 1, 0, (struct sockaddr *)&recever, len);
if(r == -1){
return -1;
}
return 0;
}

read() not reading the remaining bytes on a socket buffer

I have created two programs, a client and a server. They communicate via sockets sending char arrays of a fixed size of 115 bytes.
The data I want to transfer is stored in the following struct:
typedef struct {
char origin[14];
char type;
char data[100];
} socket_data;
But in order to send the data serialized, I want to send that information in a single string concatenating al the fields in the struct so I send a 115 bytes string. If any of those fields does not reach it's max size, I will manually fill the extra array positions with \0.
I have created two functions implemented in both client and server that send data through the socket or receive data from the socket.
The two functions are the following:
void socket_send(int socket, char *origin, char type, char *data) {
char info[115]; //data to be sent
socket_data aux;
strcpy(aux.origin, origin);
aux.type = type;
strcpy(aux.data, data);
//Filling up the remaining positions of origin and data variables
for (int i = (int) strlen(aux.origin); i<14; i++) aux.origin[i] = '\0';
for (int i = (int) strlen(aux.data); i<100; i++) aux.data[i] = '\0';
//Building up the 115 byte string I want to send via socket
for (int i=0; i<14; i++) info[i] = aux.origin[i];
info[14] = type;
for (int i=0; i<100; i++) info[i+15] = aux.data[i];
ssize_t total_bytes = 115;
ssize_t bytes_written = 0;
//Here I send all the bytes through the socket
do {
bytes_written = write(socket, info + (115 - total_bytes), total_bytes);
total_bytes -= bytes_written;
} while (total_bytes > 0);
}
socket_data socket_rcv(int socket) {
socket_data info;
char sequence[115];
ssize_t total_bytes = 115;
ssize_t bytes_read = 0;
//Here I receive all the bytes from the socket (till I fill up the 115 byte string called sequence)
do {
bytes_read = read(socket, sequence + (115 - total_bytes), total_bytes);
total_bytes -= bytes_read;
} while (total_bytes > 0);
//Then I return a stuct
for (int i=0; i<14; i++) info.origin[i] = sequence[i];
info.type = sequence[14];
for (int i=0; i<100; i++) info.data[i] = sequence[i+15];
return info;
}
As you can see, I loop both read() and write() to make sure all bytes are sent as I'm aware sometimes those functions read or write less bytes than demanded.
The issue is that, testing the functionality of the program, I have seen that in the case that less bytes are read (it loops), the program blocks (maybe waiting for another write() from the server side) instead of reading the remaining bytes in the socket buffer (because all 115 bytes where sent and only 111 received, so there should be still 4 bytes in the socket buffer). Sometimes also, instead of blocking waiting for a possible write(), the program terminates when it shouldn't...
I can't find the issue here and I'd appreciate some help
EDIT
I created this functions to set up the sockets...
Server:
int socketConfig (connection_info cinfo) {
int socketfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socketfd < 0) {
write(1, "Socket error\n", strlen("Socket error\n"));
return -1;
}
struct sockaddr_in s_addr;
memset (&s_addr, 0, sizeof (s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(cinfo.port);
s_addr.sin_addr.s_addr = INADDR_ANY;
if (bind (socketfd, (void *) &s_addr, sizeof (s_addr)) < 0) {
write(1, "Bind error\n", strlen("Bind error\n"));
return -1;
}
listen(socketfd, 3);
return socketfd;
}
int receiveClient(int serverfd) {
struct sockaddr_in client;
socklen_t len = sizeof(client);
return accept(serverfd, (void *) &client, &len);
}
Client:
int connect_to_server(Config config) {
struct sockaddr_in client;
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
write(1, "Connecting Jack...\n", strlen("Connecting Jack...\n"));
if (sockfd < 0) {
write(1, "Error creating the socket\n", strlen("Error creating the socket\n"));
return -1;
}
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(config.port_jack);
if (inet_aton(config.ip_jack, &client.sin_addr) == 0) {
write(1, "Invalid IP address\n", strlen("Invalid IP address\n"));
return -1;
}
if (connect(sockfd, (void *) &client, sizeof(client)) < 0) {
write(1, "Error connecting to Jack\n", strlen("Error connecting to Jack\n"));
return -1;
}
return sockfd;
}
I can guarantee the connection works
You are not checking the return values of write() and read() for failures.
Try something more like this:
int socket_send_all(int socket, const void *data, size_t size) {
const char *pdata = (const char*) data;
ssize_t bytes_written;
while (size > 0) {
bytes_written = write(socket, pdata, size);
if (bytes_written < 0) return bytes_written;
pdata += bytes_written;
size -= bytes_written;
}
return 0;
}
int socket_rcv_all(int socket, void *data, size_t size) {
char *pdata = (char*) data;
ssize_t bytes_read;
while (size > 0) {
bytes_read = read(socket, pdata, size);
if (bytes_read <= 0) return bytes_read;
pdata += bytes_read;
size -= bytes_read;
}
return 1;
}
int socket_send2(int socket, const socket_data *sd) {
char bytes[115];
memcpy(bytes, sd->origin, 14);
bytes[14] = sd->type;
memcpy(bytes+15, sd->data, 100);
return socket_send_all(socket, bytes, 115);
/* alternatively:
int ret = socket_send_all(socket, sd->origin, 14);
if (ret == 0) ret = socket_send_all(socket, &(sd->type), 1);
if (ret == 0) ret = socket_send_all(socket, sd->data, 100);
return ret;
*/
}
int socket_send(int socket, char *origin, char type, char *data) {
socket_data aux;
strncpy(aux.origin, origin, 14);
aux.type = type;
strncpy(aux.data, data, 100);
return socket_send2(socket, &aux);
}
int socket_rcv2(int socket, socket_data *sd) {
char bytes[115];
int ret = socket_rcv_all(socket, bytes, 115);
if (ret > 0) {
memcpy(sd->origin, bytes, 14);
sd->type = bytes[14];
memcpy(sd->data, bytes+15, 100);
}
return ret;
/* alternatively:
int ret = socket_rcv_all(socket, sd->origin, 14);
if (ret > 0) ret = socket_rcv_all(socket, &(sd->type), 1);
if (ret > 0) ret = socket_rcv_all(socket, sd->data, 100);
return ret;
*/
}
socket_data socket_rcv(int socket) {
socket_data aux;
int ret = socket_rcv2(socket, &aux);
if (ret <= 0) {
// error handling ...
}
return aux;
}
read() returns as many bytes as it wants. Perchance the sent output went out in two packets. Perhaps something else (memory alignment comes to mind). Always handle short reads by trying to read more or have a headache. In addition, write() only writes as many byte as it wants. A short write is usually a full buffer or split by a signal, but stranger things have been observed.
You need to check for errors every time around the loop. Your program as written will trash memory otherwise.

How to reset the value of a C string pointer?

I am trying to use the same pointer twice, like this:
void* pointer = (char*) malloc(15);
pointer = "Patricia";
printf("%s", pointer);
pointer = "John";
printf("%s", pointer);
but the output I'm receiving is this:
Patricia
Johnicia
Here is the full code (the client script is a python script so I don't find it appropriate to post here)(the John and Patricia are examples of usernames the client might enter):
#define MAXCLIENTS 256
#define MAXMSG 269
void forward(int clientslist[MAXCLIENTS], char* msg) {
int x;
for (x=0; x < MAXCLIENTS; x++){
send(clientslist[x], msg, MAXMSG, 0);
}
return;
}
int main(){
#define PORT 5943
int s = socket(AF_INET, SOCK_STREAM, 0);
int clients[MAXCLIENTS];
int clientcounter = 0;
fd_set socketlist, readlist;
FD_ZERO(&socketlist);
FD_SET(s, &socketlist);
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = INADDR_ANY;
bind(s, (struct sockaddr*) &server, sizeof(server));
listen(s, MAXCLIENTS);
int clientsocket;
int i;
void* msg = (char *) malloc(MAXMSG);
void* usr = (char*) malloc(10);
while (1){
readlist = socketlist;
select(FD_SETSIZE, &readlist, NULL, NULL, NULL);
for (i=0; i<FD_SETSIZE; i++){
if(FD_ISSET(i, &readlist)){
if (i == s){
clientsocket = accept(s, NULL, NULL);
FD_SET(clientsocket, &socketlist);
clients[clientcounter] = clientsocket;
clientcounter++;
recv(clientsocket, usr, 10, 0);
printf("Connection received from %s\n", usr);
} else {
recv(i, msg, MAXMSG, 0);
forward(clients, msg);
}
}
}
}
return 0;
}
How do I fix this??
Thanks
recv returns the number of bytes read, and you just need to add a null terminator. eg:
ssize_t rc;
rc = recv(clientsocket, usr, 9, 0);
if( rc >= 0 )
usr[rc] = '\0';
Note that I've reduced the length argument in the call to recv to ensure that there is space for the terminator. If you expect to receive messages of length 10, you would want to allocate at least 11 bytes for usr. However, with buffers that small, it would probably be cleaner to use an array and do:
char usr[11];
ssize_t rc;
rc = recv(clientsocket, usr, sizeof usr - 1, 0);
if( rc >= 0 )
usr[rc] = '\0';

Sending multiple times and recv multiple times

I am very very very very new to TCP/IP socket programming.
there is 2 argv value from client.c
and I need to send this two into server.c
I thought if I send() two times from client.c and recv two times in server.c, it would be fine but actually it is not. can anyone let me know how to send two buffers into server.c?
in client.c
ssize_t byteNumSentAccName = send(clientSock, accountName, strlen(accountName), 0);
ssize_t byteNumSentCmd = send(clientSock, command, strlen(accountName), 0);
where both accountName and command are char buffer.
in server.c
ssize_t byteNumRecvAccName = recv(clientSock, nameBuf, BUFSIZE-1, 0);
ssize_t byteNumRecvCmd = recv(clientSock, cmdBuf, BUFSIZE-1, 0);
TCP is a byte stream, there is no 1:1 relationship between send() and recv(), like there is in UDP.
In TCP, send() can send fewer bytes than requested, and recv() can return fewer bytes than requested. So you need to call them in loops until all bytes have been sent/received. And you need to frame your data in such a way that the receiver knows where one value ends and the next begins. For instance, when sending a variable-length string, you can either
send the string length as a fixed-width integer before sending the actual string characters. The receiver can then read the length first, and then read until the specified number of characters have been received.
bool sendRaw(int sock, const void *data, int size)
{
const char *buffer = (const char*) data;
while (size > 0)
{
ssize_t sent = send(sock, buffer, size, 0);
if (sent < 0)
return false;
buffer += sent;
size -= sent;
}
return true;
}
bool sendInt32(int sock, int32_t value)
{
value = htonl(value);
return sendRaw(sock, &value, sizeof(value));
}
bool sendString(int sock, const char *s)
{
int32_t len = strlen(s);
if (!sendInt32(sock, len))
return false;
return sendRaw(sock, s, len);
}
...
sendString(clientSock, accountName);
sendString(clientSock, command);
int readRaw(int sock, void *data, int size)
{
char *buffer = (char*) data;
while (size > 0)
{
ssize_t recvd = recv(sock, buffer, size, 0);
if (recvd < 0)
return -1;
if (recvd == 0)
return 0;
buffer += recvd;
size -= recvd;
}
return 1;
}
int readInt32(int sock, int32_t *value)
{
int ret = readRaw(sock, value, sizeof(*value));
if (ret == 1)
*value = ntohl(*value);
return ret;
}
char* readString(int sock)
{
int32_t len = 0;
if (readInt32(sock, &len) <= 0)
return NULL;
char *ret = (char*) malloc(len+1);
if (!ret)
return NULL;
if (readRaw(sock, ret, len) <= 0)
{
free(ret);
return NULL;
}
ret[len] = '\0';
return ret;
}
...
nameBuf = readString(clientSock);
cmdBuf = readString(clientSock);
...
free(nameBuf);
free(cmdBuf);
send a unique delimiter after sending the actual string characters (a null terminator, a line break, whatever you want, as long as it never appears in strings). The receiver can then read until that delimiter is received.
bool sendRaw(int sock, const void *data, int size)
{
const char *buffer = (const char*) data;
while (size > 0)
{
ssize_t sent = send(sock, buffer, size, 0);
if (sent < 0)
return false;
buffer += sent;
size -= sent;
}
return true;
}
bool sendString(int sock, const char *s)
{
int32_t len = strlen(s) + 1;
return sendRaw(s, len);
}
sendString(clientSock, accountName);
sendString(clientSock, command);
int readRaw(int sock, void *data, int size)
{
char *buffer = (char*) data;
while (size > 0)
{
ssize_t recvd = recv(sock, buffer, size, 0);
if (recvd < 0)
return -1;
if (recvd == 0)
return 0;
buffer += recvd;
size -= recvd;
}
return 1;
}
char* readString(int sock)
{
char *ret = NULL, *tmp;
size_t len = 0, cap = 0;
char ch;
do
{
if (readRaw(sock, &ch, 1) <= 0)
{
free(ret);
return NULL;
}
if (ch == '\0')
break;
if (len == cap)
{
cap += 100;
tmp = (char*) realloc(ret, cap);
if (!tmp)
{
free(ret);
return NULL;
}
ret = tmp;
}
ret[len] = ch;
++len;
}
while (true);
if (len == cap)
{
tmp = (char*) realloc(ret, cap+1);
if (!tmp)
{
free(ret);
return NULL;
}
ret = tmp;
}
ret[len] = '\0';
return ret;
}
...
nameBuf = readString(clientSock);
cmdBuf = sendString(clientSock);
...
free(nameBuf);
free(cmdBuf);

Losing data when sending by UDP

I've written a UDP send/receive function to send a struct and listen for another struct back. The bytes have to be sent in a particular order, but this is working OK as I'm using #pragma pack(1). The only problem that I'm having now is that if any Null values (0x00) appear in the struct, the rest of the data after the Null disappears.
I guess there's something fairly simple that I'm doing wrong, but here is my code:
typedef u_int8_t NN;
typedef u_int8_t X;
typedef int32_t S;
typedef u_int32_t U;
typedef char C;
typedef struct{
X test;
NN test2[2];
C test3[4];
S test4;
} Test;
int main(int argc, char** argv)
{
Test t;
memset( &t, 0, sizeof(t));
t.test = 0xde;
t.test2[0]=0xad; t.test2[1]=0x00;
t.test3[0]=0xbe; t.test3[1]=0xef; t.test3[2]=0xde; t.test3[3]=0xca;
t.test4=0xde;
LogOnResponse response;
udp_send_receive(&t, &response);
return 0;
}
And here is my send/receive function:
int send_and_receive(void* message, void* reply, int do_send, int expect_reply)
{
struct sockaddr_in serv_addr;
int sockfd, i, slen=sizeof(serv_addr);
int buflen = BUFLEN;
void* buf = NULL;
struct timeval tv;
int n_timeouts=1;
int recv_retval;
// printf("Message Size: %d\n", strlen(message));
if ( (strlen(message)) >= BUFLEN)
err("Message too big");
buf = malloc(buflen);
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
err("socket");
tv.tv_sec = timeout_seconds;
tv.tv_usec = timeout_microseconds;
if( setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,&tv,sizeof(tv)) < 0 ){
err("Setting Timout");
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_aton(IP_ADDRESS, &serv_addr.sin_addr)==0)
err("inet_aton() failed\n");
//---Timeout Send/Receive loop
do{
if(do_send == TRUE){
strcpy(buf, message);
if (sendto(sockfd, buf, buflen, 0, (struct sockaddr*)&serv_addr, slen)==-1)
err("sendto()");
}
if (expect_reply == TRUE){
if( (recv_retval = recvfrom(sockfd, buf, buflen, 0, (struct sockaddr*)&serv_addr, &slen)) == -1){
itercount++;
}
}
}while ((itercount < itermax) && (recv_retval == -1));
if ( itercount != itermax ){
memcpy(reply, buf, BUFLEN);
}
else{
reply=NULL;
}
close(sockfd);
free(buf);
return 0;
}
void udp_send_receive(void* message, void* reply)
{
send_and_receive(message, reply, TRUE, TRUE);
}
Running the above code and capturing the packets with WireShark shows:
Data: DEAD000000000000000000....
I'd like it to show:
Data: DEAD00BEEFDECADE
I'd really appreciate some pointers on this.
You can't use string functions (like strlen or strcpy) for binary data. It's because strings are terminated by the value zero (character '\0').
For example, you use strcpy to copy data, but it will stop as soon as is sees the string terminator meaning it will not copy all of the data.
Rather than using strcpy use
void * memcpy ( void * destination, const void * source, size_t num );
Doing strcpy(buf, message); in send_and_receive() is incorrect. I would update code to pass size of message and use that to copy memory as
udp_send_receive(&t, sizeof(t), &response);
void udp_send_receive(void* message, int len, void* reply){
send_and_receive(message, len reply, TRUE, TRUE);
}
int send_and_receive(void* message, int len, void* reply, int do_send, int expect_reply){
...
int buflen = len;
....
memcpy(buf, message, len); //instead of strcpt(buf, message)
...
}

Resources