I want to send() body and header which has body size from the Side A.
On the Side B, in the first recv() header to determine the size of payload. The second, recv() body.
I tried to use recv() twice but could not through the second recv() function.
Side A
struct CommandHeader_t {
int BodySizeByte;
};
void Send()
{
const int BODY_SIZE_BYTE = 10;
CommandHeader_t* header_ptr = (CommandHeader_t*) malloc(sizeof(CommandHeader_t) + BODY_SIZE_BYTE);
header_ptr->BodySizeByte = BODY_SIZE_BYTE;
char* body_ptr = (char*) (header_ptr + sizeof(CommandHeader_t));
snprintf(body_ptr, BODY_SIZE_BYTE, "Hello World");
int sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
struct sockaddr_un addr = {0};
addr.sun_family = AF_LOCAL;
strcpy(addr.sun_path, SENDING_SOCKET);
sendto(sock, header_ptr, sizeof(CommandHeader_t) + BODY_SIZE_BYTE, 0, (const struct sockaddr*)&addr, sizeof(addr));
close(sock);
}
Side B
void Reveice()
{
// something
CommandHeader_t header;
char* body_ptr;
recv(sockfd, &header, sizeof(CommandHeader_t), 0);
body_ptr = (char*) malloc(header.BodySizeByte);
recv(sockfd, body_ptr, header.BodySizeByte, 0); // CANNOT recv anything
}
There are some mistakes in your code:
On the sending side, you are not doing pointer arithmetic correctly when assigning the body_ptr pointer. As header_ptr is declared as CommandHeader_t*, doing (header_ptr + sizeof(CommandHeader_t)) will calculate a memory address that is sizeof(CommandHeader_t) number of CommandHeader_t elements past the address that header_ptr is pointing at. That is not what you want. You need to instead calculate an address that is sizeof(CommandHeader_t) number of chars past the header_ptr address.
So, you need to change this:
char* body_ptr = (char*) (header_ptr + sizeof(CommandHeader_t));
To this instead:
char* body_ptr = ((char*) header_ptr) + sizeof(CommandHeader_t);
A simpler way to handle this is to add a char[1] member to CommandHeader_t, and then subtract 1 when malloc()'ing it, eg:
#pragma pack(push, 1) // or equivalent
struct CommandHeader_t {
int BodySizeByte;
char data[1];
};
#pragma pack(pop) // or equivalent
CommandHeader_t* header_ptr = (CommandHeader_t*) malloc(offsetof(CommandHeader_t, data) + BODY_SIZE_BYTE);
header_ptr->BodySizeByte = BODY_SIZE_BYTE;
char* body_ptr = header_ptr->data;
snprintf(body_ptr, BODY_SIZE_BYTE, "Hello World");
Also, since you are using SOCK_DGRAM on the sending side, the sendto() is message-oriented, sending the entire data in 1 message. But your receiving code is written as if it were using a stream-oriented socket instead. Don't mismatch socket types like that. If you want to call recv() multiple times, use a stream-oriented socket on both sides. Otherwise, use a message-oriented socket on both sides, and then have the receiver allocate a buffer large enough to receive an entire message in 1 recvfrom() (you can use ioctl(FIONREAD) to determine the size of the next pending message), and then parse the contents of that message as needed.
Also, your receiver is leaking the body_ptr buffer it allocated with malloc().
Related
I'm working on a webserver framework in C++ mostly for my own understanding, but I want to optimize it as well.
My question is is it faster to write multiple char arrays to the TCP connection for every html response or to spend the time to concatenate up front and only write to the TCP connection once. I was thinking about benchmarking it, but I am not quite sure how to go about it.
This is my first post on stackoverflow, although I have benefitted from the website very often!
Thanks!
Here is what I am talking about for sending many char arrays individually. The alternate would be concatenate all of these char arrays into one char array then sending that.
int main() {
sockaddr_in address;
int server_handle;
int addrlen = sizeof(address);
if ((server_handle = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("cannot create socket");
exit(0);
}
memset((char *) &address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons(PORT);
if (bind(server_handle, (sockaddr *) &address, (socklen_t) addrlen) < 0)
{
perror("bind failed");
exit(0);
}
if (listen(server_handle, 3) < 0)
{
perror("In listen");
exit(EXIT_FAILURE);
}
while(1) {
std::cout << "\n+++++++ Waiting for new connection ++++++++\n\n";
int client_handle;
if ((client_handle = accept(server_handle, (struct sockaddr *)&address, (socklen_t *) &addrlen))<0)
{
perror("In accept");
exit(EXIT_FAILURE);
}
// read and respond to client request
char buffer[30000] = {0};
int bytesRead = read(client_handle, buffer, 30000);
char * httptype = "HTTP/1.1 ";
char * status = "200 \n";
char * contenttype = "Content-Type: text/html \n";
char * contentlength = "Content-Length: 21\n\n";
char * body = "<h1>hello world!</h1>";
write(client_handle, httptype, 9);
write(client_handle, status, 5);
write(client_handle, contenttype, 26);
write(client_handle, contentlength, 20);
write(client_handle, body, 21);
std::cout << "------------------Response sent-------------------\n";
close(client_handle);
}
}
If you want to send multiple buffers with a single write call you can use vectored IO (aka scatter/gather IO) as the manual suggests:
char *str0 = "hello ";
char *str1 = "world\n";
struct iovec iov[2];
ssize_t nwritten;
iov[0].iov_base = str0;
iov[0].iov_len = strlen(str0);
iov[1].iov_base = str1;
iov[1].iov_len = strlen(str1);
nwritten = writev(STDOUT_FILENO, iov, 2);
In fact it writing to a socket is not really different from writing to a file descriptor. And the fwrite function was introduced to the C library for a reason: write (be it to a TCP connection or to a file descriptor) involve a system call on common OS and a context change user/kernel. That context change has some overhead, mainly if you write small chunks of data.
On the other hand, if you write larger chunks of data in sizes that are close to the physical size for the underlying system call (disk buffer for a file descriptor, or max packet size for a network socket), the fwrite call or in your example the code concatenating char arrays will not really lower the system overhead and will just add some user code processing.
TL/DR: this depends on the average size of what you write. The smaller it is, the higher benefit of concatenating the date in larger chunks before writing. And remember: this is a low level optimization that should only be considered if you have identified a performance bottleneck or if the code could be used in a broadly distributed library.
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;
}
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.
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.
Let me give the general overview first. I'm recieving data thru three ports. I have a socket, a completion port and a worker thread for each. I call WSARecv and the worker thread process calls GetQueuedCompletionStatus followed by my parsing routine ReadMsgs. It sometimes happens that the buffer is unchanged when ReadMsgs is called and the buffer is updated while ReadMsgs is processing the buffer. The number of bytes processed, as returned by GetQueuedCompletionStatus is correct for the update when it occurs.
Does anyone know why this might occur and what I am doing wrong. Let me show you the code that seems most relevant. If you need to see more code, please be specific. My base socket class looks like this (I have omitted details that seem to me to be irrelevant. I have also omitted all error checking.)
class Socket_Base : public OVERLAPPED
{
public:
Socket_Base()
{
// Initialize base OVERLAPPED object
Internal = 0;
InternalHigh = 0;
Offset = 0;
OffsetHigh = 0;
hEvent = WSACreateEvent();
// Initialize addr structure
ZeroMemory( &addr, sizeof(struct sockaddr_in));
// Create the completion port
hCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 1);
// Create the worker thread and bind it to the callback function and the completion port
hThread = (HANDLE)_beginthreadex( NULL, 0, Callback_Socket, hCP, 0, NULL);
// Create the socket
Sock = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
// Bind the socket to the completion port
CreateIoCompletionPort( (HANDLE)Sock, hCP, 0, 0);
}
void Connect() { WSAConnect( Sock, (SOCKADDR*)(&addr), sizeof(addr), NULL, NULL, NULL, NULL);}
void StartRecv()
{
DWORD Flags = 0;
DWORD numBytes = 0;
if (WSARecv( Sock, &wsaBuf, 1, &numBytes, &Flags, (OVERLAPPED*)this, NULL) == 0) ReadMsgs( numBytes);
}
int ReadMsgs( int NumBytes);
protected:
virtual ~Socket_Base() {}
virtual void ProcessMsg() = 0;
struct sockaddr_in addr;
SOCKET Sock;
HANDLE hCP;
WSABUF wsaBuf;
HANDLE hThread;
char *readBuf;
int bufsize;
};
Each port has its own derived socket class distinguised by port number and the virtual ProcessMsg function (which is called by ReadMsgs as each message is parsed). Here is one such class:
class Socket_Admin : public Socket_Base
{
public:
static const int bufcap = 1024;
Socket_Admin::Socket_Admin() : Socket_Base()
{
// Buffer
readBuf = new char[ bufcap];
// The socket
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(9300);
wsaBuf.buf = readBuf;
wsaBuf.len = bufcap;
}
~Socket_Admin();
void ProcessMsg();
};
The worker thread process is
unsigned int Callback_Socket( void *lpParameter)
{
HANDLE hCP = (HANDLE)lpParameter;
DWORD NumBytes = 0;
ULONGLONG CompletionKey;
WSAOVERLAPPED *pOverlapped;
while (GetQueuedCompletionStatus( hCP, &NumBytes, &CompletionKey, &pOverlapped, INFINITE) && CompletionKey == 0)
{
Socket_Base *pTCP = (Socket_Base*)pOverlapped;
if (NumBytes > 0) pTCP->ReadMsgs( NumBytes);
NumBytes = 0;
}
return 0;
}
There is one other thing I should explain. ReadMsgs does its parsing in place. The server delimits messages with a final line feed character and it delimits fields within a message with commas. ReadMsgs replaces commas and line feeds with null characters as it finds them and notes where each field begins in a separate array of pointers to locations in the buffer. Now when ReadMsgs gets to the end of the region of the buffer that was last filled it sometimes finds an incomplete message. This is copied to the beginning of the buffer, expecting the next read to complete the message, and wsaBuf is modified accordingly. Thus the end of ReadMsgs looks like this:
wsaBuf.buf = pchar;
wsaBuf.len = remsize;
StartRecv();
where pchar points to the character just beyond the partial message and remsize is the size of the remaining buffer.
I know what messages have been sent to my application from detailed server logs. The replacement of delimiters with null characters also makes it easy to see what part of the buffer has been processed. By saving the buffer to file and examining it, I can tell that it was updated after ReadMsgs was called. Also, by log messages not shown in the code above, I know that in these cases ReadMsgs was called by the worker thread. It doesnt happen every time, but it does happen.
If anyone can tell me what my mistake is, I would be grateful.
I may have an answer. I have calls to ReadMsgs in both StartRecv and in Callback_Socket. In StartRecv, I call ReadMsgs when WSARecv returns 0, indicating that the transfer was completed by WSARecv. I was thinking that GetQueuedCompletion status would not get involved if the transfer was completed in WSARecv. However if GetQueuedCompletionStatus did return in response to the completed read, then I would have a duplicate call to ReadMsgs that would explain the logged data I collected, just as well as my previous hypothesis.
I've removed the call to ReadMsgs in StartRecv. The code is working properly now.
Thanks to the gentleman who gave me the negative vote. That suggested to me that the behavior I described has not been observed before and so is extremely unlikely. That sent me thinking in a new direction. Sometimes just a grunt from someone more experienced van speak volumes.
Struct I want to send:
struct Client
{
SOCKET socket = 0;
float m_fX = 0;
float m_fY = 0;
float m_fRot = 0;
bool m_bIsAlive = false;
Powerups m_ePowerup = Powerups::lazer;
};
Client Sending Struct to Server:
char* buffer = new char[sizeof(Client)];
memcpy(buffer, &dataToServer, sizeof(Client));
send(sock, (char*)dataToServer, sizeof(Client), NULL);
Server Receiving Struct from Client:
char* buffer = new char[sizeof(Client)];
recv((SOCKET)socketData, buffer, sizeof(Client), NULL);
memcpy(m_pClients[i], (void*)buffer, sizeof(Client));
std::cout << "\tSocket: " << (int)(SOCKET)socketData << "\t" << m_pClients[i]->m_bIsAlive << std::endl;
Why does this print out as though m_pClients[i]->m_bIsAlive = 205?
char* buffer = new char[sizeof(Client)];
memcpy(buffer, &dataToServer, sizeof(Client));
send(sock, (char*)dataToServer, sizeof(Client), NULL);
Looks to me like you meant for the second parameter to send() to be buffer, here. Additionally, if dataToServer is a pointer to a Client structure, the second parameter to memcpy() should not be a pointer to this pointer.
Additionally, your code fails to check the return value from both send() and recv() also. Just because you asked to send sizeof(Client) bytes, you have absolutely no guarantee whatsoever that this request will succeed, or that many bytes will be written.
The same thing goes for recv() too. You must always check the return value from every system call, be prepared to handle errors and every possible return value, unless external circumstances (there are few cases like that) guarantee that the system call will never fail.
I am trying to send packets via UDP, and I am not allowed to use SOCK_RAW (school project).
The packet I am sending has a header struct ip and a string data part. I put them into one char array (the packet itself is configured correctly).
Here is how I send:
sendPacket(packet);
where packet is a char[] and
where sendPacket is defined as:
void IPNode::sendPacket(char* packet){
//define socket, destSockAddr
int success = sendto(socket, packet, sizeof(packet), 0,
(struct sockaddr *) &destSockAddr, sizeof(destSockAddr));
}
}
The packet seems to be correct.
However, this is how I read it in.
while (true) {
struct sockaddr_in remoteAddr;
socklen_t remoteAddrLen = sizeof(remoteAddr);
char buffer[BUF_SIZE];
int recvlen = recvfrom(myListenSocket, buffer, BUF_SIZE, 0, 0, 0);
onReceive(buffer);
// other stuff
}
where onReceive is:
void onReceive(char* packet) {
ip* ptr = (ip*)packet; //the struct ip is the beginning of the packet
struct ip ipCpy = *ptr;
struct in_addr inAddrCpy = ipCpy.ip_src;
char* ipString = inet_ntoa(inAddrCpy);
cout << ipString << endl;
return;
}
However, the ipString that is printed is different from the ipString that was in the packet before being sent. Am I accessing the received packets wrongly?
You are sending sizeof(packet) bytes. But sizeof(packet) is 4, because packet is a pointer. You need a better way to keep track of the actual size you want to send.