Reputation: 13453
EDIT1: Per request of John Bollinger, I've included the full client and server code below.
I am sending 4-digit penny prices over a socket connection; e.g., 43.75, 29.43, 94.75, etc. Buffer size is set to 1024 bytes. At the moment, I am converting float
prices to c-strings for transmission--not optimal, but I'm working on it. By my calculations, price size is 6 bytes: 4 digits, a decimal point, and the c-string terminating character '\0'
.
The problem is that, on the client side, prices are not printed until the 1024-byte buffer is full. I would like each price tick sent and handled as it comes in, and force a buffer flush, and have each tick to be handled separately. In other words, I'd like each price to be sent in a separate packet, and not buffer the 1024 bytes.
How can I force each price tick to be handled separately? Thanks for your help. Sincerely, Keith :^)
The socket connection code is taken from the following url: http://www.programminglogic.com/example-of-client-server-program-in-c-using-sockets-and-tcp/
Server-side:
char buffer[1024]; // buffer set to 1024
char res[1024] // res contains the a float rounded and converted to a string.
// res is copied into buffer, and then sent with size 6:
// 22.49\0 = 6 characters.
strcpy(buffer, res);
send(newSocket,buffer,6,0);
Client-side:
while(1) {
recv(clientSocket, buffer, 1024, 0);
printf("%s ",buffer);
}
I would expect the prices to print as they arrive, like so:
pickledEgg$ 49.61
pickledEgg$ 50.20
pickledEgg$ 49.97
pickledEgg$ etc..
but 1024 bytes worth of prices are being buffered:
pickledEgg$ 49.61 50.20 49.97 49.86 49.52 50.24 49.79 49.52 49.84 50.29 49.83 49.97 50.34 49.81 49.84 49.50 50.08 50.06 49.54 50.04 50.09 50.08 49.54 50.43 49.97 50.33 50.29 50.08 50.43 50.02 49.86 50.06 50.24 50.33 50.43 50.25 49.58 50.25 49.79 50.43 50.04 49.63 49.88 49.86 49.93 50.22 50.38 50.02 49.79 50.41 49.56 49.88 49.52 49.59 50.34 49.97 49.93 49.63 50.06 50.38 50.15 50.43 49.95 50.40 49.77 50.40 49.68 50.36 50.13 49.95 50.29 50.18 50.09 49.66 50.06 50.04 50.38 49.95 49.56 50.18 49.86 50.13 50.09 49.88 49.74 49.91 49.88 49.70 49.56 50.43 49.58 49.74 49.88 49.54 49.63 50.15 49.97 49.79 49.52 49.59 49.77 50.31 49.81 49.88 50.47 50.36 50.40 49.86 49.81 49.97 49.54 50.18 50.11 50.13 50.08 50.36 50.06 50.45 50.06 50.13 50.38 49.65 49.88 50.29 49.70 50.00 50.45 49.68 50.29 50.47 50.29 50.09 50.27 49.59 50.45 50.24 50.47 49.88 50.11 49.77 49.86 50.16 49.97 50.47 50.31 49.56 49.84 50.38 50.02 50.40 49.52 49.90 50.09 49.90 50.20 49.81 50.38 50.15 49.99 49.70 50.11 49.77 49.79 49.88 49.88 49.75 50.13 50.36 49.63 49.74 50.1
EDIT1: Server-side code:
/****************** SERVER CODE ****************/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <math.h>
void reverse(char *str, int len)
{
int i=0, j=len-1, temp;
while (i<j)
{
temp = str[i];
str[i] = str[j];
str[j] = temp;
i++; j--;
}
}
int intToStr(int x, char str[], int d)
{
int i = 0;
while (x)
{
str[i++] = (x%10) + '0';
x = x/10;
}
// If number of digits required is more, then
// add 0s at the beginning
while (i < d)
str[i++] = '0';
reverse(str, i);
str[i] = '\0';
return i;
}
void ftoa(float n, char *res, int afterpoint)
{
// Extract integer part
int ipart = (int)n;
// Extract floating part
float fpart = n - (float)ipart;
// convert integer part to string
int i = intToStr(ipart, res, 0);
// check for display option after point
if (afterpoint != 0)
{
res[i] = '.'; // add dot
// Get the value of fraction part upto given no.
// of points after dot. The third parameter is needed
// to handle cases like 233.007
// fpart = fpart * pow(10, afterpoint);
fpart = fpart * 100;
intToStr((int)fpart, res + i + 1, afterpoint);
}
}
float randPrice() {
int b;
float d;
b = 4950 + rand() % 100 + 1;
d = (float)b/100;
return d;
}
void wait() {
int i, j, k;
for (i=0; i<10000; ++i) {
for (j=0; j<10000; ++j) {
k = i + j + i * j;
}
}
}
int main(){
int welcomeSocket, newSocket;
char buffer[1024];
struct sockaddr_in serverAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
char res[1024];
float n;
srand(time(NULL));
/*---- Create the socket. The three arguments are: ----*/
/* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
welcomeSocket = socket(PF_INET, SOCK_STREAM, 0);
/*---- Configure settings of the server address struct ----*/
/* Address family = Internet */
serverAddr.sin_family = AF_INET;
/* Set port number, using htons function to use proper byte order */
serverAddr.sin_port = htons(7891);
/* Set IP address to localhost */
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/* Set all bits of the padding field to 0 */
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*---- Bind the address struct to the socket ----*/
bind(welcomeSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
/*---- Listen on the socket, with 5 max connection requests queued ----*/
if(listen(welcomeSocket,5)==0)
printf("Listening\n");
else
printf("Error\n");
/*---- Accept call creates a new socket for the incoming connection ----*/
addr_size = sizeof serverStorage;
newSocket = accept(welcomeSocket, (struct sockaddr *) &serverStorage, &addr_size);
/*---- Send prices to the socket of the incoming connection ----*/
while(1) {
n = randPrice(); // Get a random, float price
ftoa(n, res, 2); // Convert price to string
strcpy(buffer, res); // copy to buffer
send(newSocket,buffer,6,0); // send buffer
wait();
}
return 0;
}
Client-side code:
/****************** CLIENT CODE ****************/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
void wait() {
int i, j, k;
for (i=0; i<10000; ++i) {
for (j=0; j<10000; ++j) {
k = i + j + i * j;
}
}
}
int main(){
int clientSocket;
char buffer[1024];
struct sockaddr_in serverAddr;
socklen_t addr_size;
/*---- Create the socket. The three arguments are: ----*/
/* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
clientSocket = socket(PF_INET, SOCK_STREAM, 0);
/*---- Configure settings of the server address struct ----*/
/* Address family = Internet */
serverAddr.sin_family = AF_INET;
/* Set port number, using htons function to use proper byte order */
serverAddr.sin_port = htons(7891);
/* Set IP address to localhost */
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/* Set all bits of the padding field to 0 */
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*---- Connect the socket to the server using the address struct ----*/
addr_size = sizeof serverAddr;
connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);
/*---- Read the message from the server into the buffer ----*/
int r = 0;
while(1) {
r = recv(clientSocket, buffer, 1024, 0);
printf("recv value: %i\n", r);
printf("%s ",buffer);
wait();
}
return 0;
}
Upvotes: 1
Views: 3623
Reputation: 33982
My apologies for lack of clarity in my comment.
It is impossible to reliably separate data based solely on the packet in which it arrived. Disabling Nagle's Algorithm with TCP_NODELAY may greatly improve the likelihood of getting the desired behaviour but nothing can guarantee it.
For example:
Message A is written and sent immediately
Message B is written and sent immediately
Message A is delayed on the network (too many possible reasons to list)
Message B arrives at receiver
Message A arrives at receiver
Receiver makes Messages A and B available
recv
will read everything from the buffer, Message A and Message B, up to the maximum number of bytes requested. Without some method of telling Message A from Message B, you cannot proceed.
OK, but you know the length of Message A and Message B, 6 bytes, so you simply recv
6 bytes. Right?
Almost. For a number of reasons, the sender may not be able to send the whole message in one packet and a recv
for 6 bytes only returns, say, 2.
The only way to be sure, other than nuking the site from orbit, is to loop on recv until all 6 bytes have been read.
bool receiveAll(int sock,
char * bufp,
size_t len)
{
int result;
size_t offset = 0;
while (len > 0)
{ // loop until we have all of our data
result = recv(sock, &bufp[offset], len, 0);
if (result < 0)
{ // Socket is in a bad state
// handle error
return false;
}
else if (result == 0)
{ // socket closed
return false;
}
len -= result;
offset += result;
}
return true;
}
Usage:
while(receiveAll(clientSocket, buffer 6)) {
printf("%s ",buffer);
}
This will keep looping until the socket is closed or an error forces the loop to exit. No waiting is required, recv
waits for you.
What it doesn't have is a good mechanism for a polite shutdown of the client while the server is still running.
This allows the sender to remain stupid. Otherwise the sender needs to do something similar making sure that it only ever sends full messages, and no messages ever straddle multiple packets. This is significantly more effort to get right than the loop in the receiveAll
function. See Akash Rawal's answer for hints on how to do this.
Upvotes: 3
Reputation: 144
It is recv()
that is buffering 1024 bytes.
You have 2 options:
Read character-by-character (buffer size = 1). Inefficient but simple.
Set O_NONBLOCK
using fcntl()
on client side and use select()
to wait till there is data to read and then call recv()
. Complex, you could get any number of data or even partial data, but it is going to be efficient.
Upvotes: 3