I'm just learning how to handle sockets and TCP connections in C. I've got an application (a long one) which basically sends and receives char arrays with the system call write from server to client and vice versa (two separate C applications of course). As long as I use it with a local connection, on the same PC, running the server on a terminal and the client on an another, everything just works fine and the data arrives at the destination. But if I try it with the server on one computer and the client on another but on the same internet line, passing to the client an address like 192.168.1.X (took from the machine on which the server is running), after the connection is established, I've got an error that tells me that the number of expected bytes (which I pass before sending the real char[]) isn't arrived. Same thing if I try the server on my PC, and the client on another one with a different line on a different provider.
There's something I'm missing, are there any limitations in sending a bunch of bytes in sequence?
The code where the error pops up.
SERVER SIDE:
r=htonl(lghstr);
w=write(myFd,&r,sizeof(int));//writes the number of incoming bytes
if(w<0) perror("writeServer4"),exit(-1);
w=write(myFd,tmp->string,lghstr);
if(w<0) perror("writeServer5"),exit(-1);
if(w!=lghstr) perror("ERROR");
CLIENT SIDE
rC=read(fdc,&cod,sizeof(int));//read incoming number of bytes
lghstr=ntohl(cod);
if(rC<0) perror("readClient3"),exit(-1);
rC=read(fdc,dest,lghstr);
if(rC<0) perror("readClient4"),exit(-1);
if(rC!=lghstr) perror("error : "), printf("didn't read the right number of bytes"),exit(-1);
Now this is basically repeated a lot of times, let's even say 300 times, and it's with big numbers that the program doesn't work.
This is the problem:
rC=read(fdc,dest,lghstr);
...
if(rC!=lghstr) perror("error : ")
The #1 fallacy with socket programming is expecting that recv() and read() will return exactly the same number of bytes corresponding to the write/send call made by the other side.
In reality, partial data is extremely likely and expected. The simple workaround is to loop on read/recv until you get the exact number of bytes expected:
size_t count = 0;
while (count < lghstr)
{
ssize_t readresult = read(fdc, dest+count, lghstr-count);
if (readresult == -1)
{
// socket error - handle appropriately (typically, just close the connection)
}
else if (readresult == 0)
{
// The other side closed the connection - handle appropriately (close the connection)
}
else
{
count += readresult;
}
}
The other alternative to looping is to the use the MSG_WAITALL flag with the socket. This means, using recv() instead of read(). You'll still need to handle the error cases.
rc = recv(fdc, dest, lghstr, MSG_WAITALL);
if (rc == -1)
{
// socket error
}
else if (rc == 0)
{
// socket closed by remote
}
else if (rc < lghstr)
{
// the other side likely closed the connection and this is residual data (next recv will return 0)
}
You do ntohl() on one side and not the other. That might be interpreting the bytes with the wrong value.
You should printf() the bytes on both sides and see what the int is being evaluated to.
Edit: I'm convinced this is a programming bug for the record.
If I had to guess, I'd say that you are not synchronous with the other side for some reason. You say this runs 'about 300 times'.
Try adding a magic integer to the protocol.
Heres an example of a client that sends in this order.
A magic integer which is always constant.
A lengh of bytes about to be sent.
The bytes to be sent.
This uses scatter gather mechanics (its nicer for serialization) but other than that it effectively is doing the same thing yours is doing, as a client, just adding a magic value.
When the receiver receives the data, it can validate that the data is coming in the right order, by checking what the magic number was that came in. If the magic is wrong it means the client or server has lost themselves positionally in the stream.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <err.h>
#include <time.h>
#define MAGIC 0xDEADBEEFLU
#define GARBAGE_MAX 65536
const int iterations = 3000;
char * create_garbage_buf(
void)
{
int rc = -1;
int fd = -1;
char *buf = NULL;
buf = malloc(GARBAGE_MAX);
if (!buf)
err(1, "Cannot allocate buf");
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
err(1, "Cannot open urandom");
rc = read(fd, buf, GARBAGE_MAX);
if (rc < 0)
err(1, "Cannot read from urandom");
else if (rc != GARBAGE_MAX)
errx(1, "Expected %d bytes, but got %d reading from urandom",
GARBAGE_MAX, rc);
close(fd);
return buf;
}
int main() {
int fd, offset, i, rc;
uint32_t magic = MAGIC;
uint32_t blen = 0;
char *buf = NULL;
struct iovec vecs[3];
/* Seed poor random number generator */
srand(time(NULL));
/* Use a file for demonstration, but a socket will do just fine */
fd = open("/dev/null", O_WRONLY);
/* Create some garbage to send */
buf = create_garbage_buf();
if (fd < 0)
err(1, "Cannot open file");
/* The first vector, is always the magic */
vecs[0].iov_len = sizeof(uint32_t);
vecs[0].iov_base = &magic;
for (i=0; i < iterations; i++) {
/* The second vector represents lengh of what we send
* in this demonstration it is a number between 0 and
* GARBAGE_MAX/2.
*/
blen = rand() % (GARBAGE_MAX / 2);
vecs[1].iov_len = sizeof(uint32_t);
vecs[1].iov_base = &blen;
/* The last record is the data to send. Its another random
* number between 0 and GARBAGE_MAX which represents the offset
* in our garbage data to send */
offset = rand() % (GARBAGE_MAX / 2);
vecs[2].iov_len = blen;
vecs[2].iov_base = &buf[offset];
rc = writev(fd, vecs, 3);
if (rc < 0)
err(1, "Could not write data");
if (rc != (sizeof(uint32_t)*2 + blen))
errx(1, "Did not write proper number of bytes to handle");
printf("Wrote %u bytes from offset %u in garbage\n", blen, offset);
}
free(buf);
printf("Done!\n");
return 0;
}
Closely read the documentation for read()/write() and learn that those two functions do not necessarily read()/write() as much bytes as they were told to, but few. So looping around such calls counting until all data expected had been read/written is a good idea, not to say an essential necessity.
For examples how this could be done for writing you might like to have look at this answer: https://stackoverflow.com/a/24260280/694576 and for reading on this answer: https://stackoverflow.com/a/20149925/694576
Related
I'm constantly sending structs of int64 via Pyserial with:
with serial.Serial(port='COM4', baudrate=115200, timeout=.1) as arduino:
value = write_read(struct.pack(">q", int_array[1][i])) #this sends signed int.64 in bytes
print(value)
the struct.pack has this shape, for example:
b'\xff\xff\xff\xff\xff\xff\xff\xef'
and the function write_read consists of:
def write_read(x):
arduino.write((x))
data = arduino.readline()
#the idea is to receive an ACK from the Arduino after 8 bytes (the full
#number)
return data
The code I'm trying to develop in arduino is the following:
void loop() {
// send data only when you receive data:
if (Serial.available() \> 0) {
// read the incoming byte:
incomingByte = Serial.read();
//read 8 bytes and create the result
r= function_to_read_8_last_bytes // or similar
// say what you got:
Serial.print("I received: ");
Serial.printlesultn(r, DEC);
Serial.write("ACK");
}
}
I'm very curious how I could do a robust "read 8 bytes" function.
Should I add some especial character in the Python part to indentify when it ends one value?
Thanks! I'll appreciate any help :)
Given the discussion in the comments, it's hard to receive a stream of bytes and be sure that the receiver is completely synchronized. However let's make some assumptions to ease the problem:
The serial buffer is empty when you connect your laptop to Arduino. This ensures you won't receive spurious data with no meaning. I had this problem happens a lot when the serial connection was ended abruptly by any cause.
You are not constantly sending bytes, Arduino has time to process them until the start of the new sequence.
You only send this data, so there is no need to create a higher level protocol on top of it. Bare in mind that the serial communication is almost just an hardware stack, you receive bytes with no headers.
For assumption 1 you can write a simple piece of code to consume all the spurious bytes in the serial buffer as soon as your main starts from Arudino, so this will be done everytime you connect the serial (as this is also where the power supply comes from). Something like this:
void serialFlush(){
while(Serial.available() > 0) {
char t = Serial.read();
}
}
You can send a "READY" signal back to the Python interface, so that the program knows you are ready to receive data.
Going on with the solution you can implement an easy CRC in python, an additional byte which contains a XOR of all the previous bytes, and you check that in Arduino upon reception complete.
def xor_reduce_long_int(li):
res = 0;
for i in range(8):
mask = (0xFF)<<(i*8)
print(hex(mask))
masked = (li&mask)>>(i*8)
res ^= masked
return res
with serial.Serial(port='COM4', baudrate=115200, timeout=.1) as arduino:
crc=xor_reduce_long_int(int_array[1][i])
value = write_read(struct.pack(">qc", int_array[1][i],crc)) #this sends signed int.64 in bytes
print(value)
And with Arduino I would read 8 bytes when they are available and put them into an unsigned char buffer. I would then define a union that alias such buffer to interpret it as long long int.
typedef struct long_int_CRC
{
union
{
unsigned char bytes[8];
long int data;
};
unsigned char CRC;
}data_T;
// .. Later in main
data_T = received_data;
int received_bytes=0
unsigned char my_CRC = 0;
unsigned char rec_byte= 0;
while( received_bytes < 8 )
{
if(Serial.available() )
{
// Get the byte
rec_byte = Serial.read()
// Store the byte and calc CRC
received_data.bytes[received_bytes] = rec_byte;
my_CRC ^= rec_byte;
// Increment counter for next byte
received_bytes++;
}
}
// Reception complete, check CRC
unsigned char rec_CRC;
if(Serial.available() )
{
rec_CRC = Serial.read()
}
if( my_CRC != rec_CRC )
{
// Something was wrong!
}
// Now access your data as a long int
Serial.print("I received: ");
Serial.printlesultn(received_data.data, DEC);
Serial.write("ACK");
We have two Qt applications. App1 accepts a connection from App2 through QTcpServer and stores it in an instance of QTcpSocket* tcpSocket. App1 runs a simulation with 30 Hz. For each simulation run, a QByteArray consisting of a few kilobytes is sent using the following code (from the main/GUI thread):
QByteArray block;
/* lines omitted which write data into block */
tcpSocket->write(block, block.size());
tcpSocket->waitForBytesWritten(1);
The receiver socket listens to the QTcpSocket::readDataBlock signal (in main/GUI thread) and prints the corresponding time stamp to the GUI.
When both App1 and App2 run on the same system, the packages are perfectly in sync. However when App1 and App2 are run on different systems connected through a network, App2 is no longer in sync with the simulation in App2. The packages come in much slower. Even more surprising (and indicating our implementation is wrong) is the fact that when we stop the simulation loop, no more packages are received. This surprises us, because we expect from the TCP protocol that all packages will arrive eventually.
We built the TCP logic based on Qt's fortune example. The fortune server, however, is different, because it only sends one package per incoming client. Could someone identify what we have done wrong?
Note: we use MSVC2012 (App1), MSVC2010 (App2) and Qt 5.2.
Edit: With a package I mean the result of a single simulation experiment, which is a bunch of numbers, written into QByteArray block. The first bits, however, contain the length of the QByteArray, so that the client can check whether all data has been received. This is the code which is called when the signal QTcpSocket::readDataBlock is emitted:
QDataStream in(tcpSocket);
in.setVersion(QDataStream::Qt_5_2);
if (blockSize == 0) {
if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
return; // cannot yet read size from data block
in >> blockSize; // read data size for data block
}
// if the whole data block is not yet received, ignore it
if (tcpSocket->bytesAvailable() < blockSize)
return;
// if we get here, the whole object is available to parse
QByteArray object;
in >> object;
blockSize = 0; // reset blockSize for handling the next package
return;
The problem in our implementation was caused by data packages being piled up and incorrect handling of packages which had only arrived partially.
The answer goes in the direction of Tcp packets using QTcpSocket. However this answer could not be applied in a straightforward manner, because we rely on QDataStream instead of plain QByteArray.
The following code (run each time QTcpSocket::readDataBlock is emitted) works for us and shows how a raw series of bytes can be read from QDataStream. Unfortunately it seems that it is not possible to process the data in a clearer way (using operator>>).
QDataStream in(tcpSocket);
in.setVersion(QDataStream::Qt_5_2);
while (tcpSocket->bytesAvailable())
{
if (tcpSocket->bytesAvailable() < (int)(sizeof(quint16) + sizeof(quint8)+ sizeof(quint32)))
return; // cannot yet read size and type info from data block
in >> blockSize;
in >> dataType;
char* temp = new char[4]; // read and ignore quint32 value for serialization of QByteArray in QDataStream
int bufferSize = in.readRawData(temp, 4);
delete temp;
temp = NULL;
QByteArray buffer;
int objectSize = blockSize - (sizeof(quint16) + sizeof(quint8)+ sizeof(quint32));
temp = new char[objectSize];
bufferSize = in.readRawData(temp, objectSize);
buffer.append(temp, bufferSize);
delete temp;
temp = NULL;
if (buffer.size() == objectSize)
{
//ready for parsing
}
else if (buffer.size() > objectSize)
{
//buffer size larger than expected object size, but still ready for parsing
}
else
{
// buffer size smaller than expected object size
while (buffer.size() < objectSize)
{
tcpSocket->waitForReadyRead();
char* temp = new char[objectSize - buffer.size()];
int bufferSize = in.readRawData(temp, objectSize - buffer.size());
buffer.append(temp, bufferSize);
delete temp;
temp = NULL;
}
// now ready for parsing
}
if (dataType == 0)
{
// deserialize object
}
}
Please not that the first three bytes of the expected QDataStream are part of our own procotol: blockSize indicates the number of bytes for a complete single package, dataType helps deserializing the binary chunk.
Edit
For reducing the latency of sending objects through the TCP connection, disabling packet bunching was very usefull:
// disable Nagle's algorithm to avoid delay and bunching of small packages
tcpSocketPosData->setSocketOption(QAbstractSocket::LowDelayOption,1);
I am currently in the process of making a Client and Server in the Unix/Windows environment but right now I am just working on the Unix side of it. One of the function we have to create for the program is similar to the list function in Unix which shows all files within a dir but we also have to show more information about the file such as its owner and creation date. Right now I am able to get all this information and print it to the client however we have to also add that once the program has printing 40 lines it waits for the client to push any key before it continues to print.
I have gotta the program to sort of do this but it will cause my client and server to become out of sync or at least the std out to become out of sync. This means that if i enter the command 'asdad' it should print invalid command but it won't print that message until i enter another command. I have added my list functions code below. I am open to suggestions how how to complete this requirement as the method I have chosen does not seem to be working out.
Thank-you in advance.
Server - Fork Function: This is called when the list command is enter. eg
fork_request(newsockfd, "list", buf);
int fork_request(int fd, char req[], char buf[])
{
#ifndef WIN
int pid = fork();
if (pid ==-1)
{
printf("Failed To Fork...\n");
return-1;
}
if (pid !=0)
{
wait(NULL);
return 10;
}
dup2(fd,1); //redirect standard output to the clients std output.
close(fd); //close the socket
execl(req, req, buf, NULL); //run the program
exit(1);
#else
#endif
}
Here is the function used to get all the info about a file in a dir
void longOutput(char str[])
{
char cwd[1024];
DIR *dip;
struct dirent *dit;
int total;
char temp[100];
struct stat FileAttrib;
struct tm *pTm;
int fileSize;
int lineTotal;
if(strcmp(str, "") == 0)
{
getcwd(cwd, sizeof(cwd));
}
else
{
strcpy (cwd, str);
}
if (cwd != NULL)
{
printf("\n Using Dir: %s\n", cwd);
dip = opendir(cwd);
if(dip != NULL)
{
while ((dit = readdir(dip)) != NULL)
{
printf("\n%s",dit->d_name);
stat(dit->d_name, &FileAttrib);
pTm = gmtime(&FileAttrib.st_ctime);
fileSize = FileAttrib.st_size;
printf("\nFile Size: %d Bytes", fileSize);
printf("\nFile created on: %.2i/%.2i/%.2i at %.2i:%.2i:%.2i GMT \n", (pTm->tm_mon + 1), pTm->tm_mday,(pTm->tm_year % 100),pTm->tm_hour,pTm->tm_min, pTm->tm_sec);;
lineTotal = lineTotal + 4;
if(lineTotal == 40)
{
printf("40 Lines: Waiting For Input!");
fflush(stdout);
gets(&temp);
}
}
printf("\n %d \n", lineTotal);
}
else
{
perror ("");
}
}
}
At here is the section of the client where i check that a ! was not found in the returned message. If there is it means that there were more lines to print.
if(strchr(command,'!') != NULL)
{
char temp[1000];
gets(&temp);
}
Sorry for the long post but if you need anything please just ask.
Although, I didn't see any TCP/IP code, I once had a similar problem when I wrote a server-client chat program in C++. In my case, the problem was that I didn't clearly define how messages were structured in my application. Once, I defined how my protocol was suppose to work--it was a lot easier to debug communication problems.
Maybe you should check how your program determines if a message is complete. In TCP, packets are guaranteed to arrive in order with no data loss, etc. Much like a conversation over a telephone. The only thing you have to be careful of is that it's possible to receive a message partially when you read the buffer for the socket. The only way you know to stop reading is when you determine a message is complete. This could be as simple as two '\n' characters or "\n\r".
If you are using UDP, then that is a completely different beast all together (i.e. messages can arrive out of order and can be lost in transit, et cetera).
Also, it looks like you are sending across strings and no binary data. If this is the case, then you don't have to worry about endianess.
I use the following function to read from a file descriptor...
int cread(int fd, char *buf, int n){
int nread;
if((nread=read(fd, buf, n))<0){
perror("Reading data");
exit(1);
}
return nread;
}
Following is the function that uses the above function
if(FD_ISSET(tap_fd, &rd_set)){
/* data from tun/tap: just read it and write it to the network */
nread = cread(tap_fd, buffer, BUFSIZE);
tap2net++;
do_debug("TAP2NET %lu: Read %d bytes from the tap interface\n", tap2net, nread);
/* write length + packet */
plength = htons(nread);
nwrite = cwrite(net_fd, (char *)&plength, sizeof(plength));
nwrite = cwrite(net_fd, buffer, nread);
do_debug("TAP2NET %lu: Written %d bytes to the network\n", tap2net, nwrite);
}
They both work fine with TCP siocket but not with udp socket.. Any help would be appreciated
It's not clear exactly what your problem is, but if net_fd is a UDP socket, then the two cwrite() calls will create two UDP datagrams.
There isn't a great deal of point in prepending the size with UDP - UDP maintains the message boundaries for you. So in the UDP case, just remove the plength part entirely.
I've setup a simple TCP file transfer. Everything appears to work OK, except for the received file size is sporadically a smaller size than the file that was sent. There doesn't appear to be any pattern to the size of the received file.
(in the code below, note that the typical client/server rolls are reversed)
My client code is like:
#define kMaxBacklog (5)
// fill out the sockadd_in for the server
struct sockaddr_in servAdddress;
//memcpy() to fill in the sockaddr
//setup the socket
int sockd, returnStatus;
sockd = socket(AF_INET, SOCK_STREAM, 0);
if (sockd == -1)
NSLog(#"could not create client socket");
else
NSLog(#"created client socket");
returnStatus = connect(sockd, (struct sockaddr*)&servAdddress, sizeof(servAdddress));
if (returnStatus == -1)
NSLog(#"could not connect to server - errno:%i", errno);
else
NSLog(#"connected to server");
NSData *dataWithHeader = [self getDataToSend];
returnStatus = send(sockd, [dataWithHeader bytes], [dataWithHeader length], 0);
if (returnStatus == -1)
NSLog(#"could not send file to server");
else if( returnStatus < [dataWithHeader length])
NSLog(#"ONLY PARTIAL FILE SENT");
else
NSLog(#"file sent of size: %i", returnStatus);
shutdown(sockd, SHUT_WR);
close(sockd);
The client method ALWAYS reports that it sent the entire file.
For the server:
#define MAXBUF (10000)
int _socket;
_socket = socket(AF_INET, SOCK_STREAM, 0); // set up the socket
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(0);
int retval = bind(_socket, (struct sockaddr *)&addr, sizeof(addr));
if (retval == -1)
NSLog(#"server could not bind to socket");
else
NSLog(#"server socket bound");
socklen_t len = sizeof(addr);
retval = getsockname(_socket, (struct sockaddr *)&addr, &len);
if (retval == -1)
NSLog(#"server could not get sock name");
else
NSLog(#"server socket name got");
int socket1, socket2, clientAddrLen, returnStatus;
struct sockaddr_in servAdddress, clientAddress;
clientAddrLen = sizeof(servAdddress);
socket1 = _socket;
returnStatus = listen(socket1, kMaxBacklog);
if (returnStatus == -1)
NSLog(#"server could not listen on socket");
else
NSLog(#"server socket listening");
while(1){
FILE *fd;
int i, readCounter;
char file[MAXBUF];
NSLog(#"server blocking on accept()");
socket2 = accept(socket1, (struct sockaddr*)&clientAddress, (socklen_t*)&clientAddrLen);
if (socket2 == -1)
NSLog(#"server could not accpet the connection");
else
NSLog(#"server connection accepted");
i = 0;
readCounter = recv(socket2, file, MAXBUF, 0);
if(!readCounter)
NSLog(#"server connection cancelled, readCount = 0");
else if (readCounter == -1){
NSLog(#"server could not read filename from socket");
close(socket2);
continue;
}
else
NSLog(#"server reading file of size: %i", readCounter);
fd = fopen([myfilePathObject cStringUsingEncoding:NSASCIIStringEncoding], "wb");
if(!fd){
NSLog(#"server could not open the file for creating");
close(socket2);
continue;
}
else
NSLog(#"server file open for creating");
returnStatus = fwrite([myData bytes], 1, [myData length], fd);
if (returnStatus == -1)
NSLog(#"Error writing data to server side file: %i", errno);
else
NSLog(#"file written to disk);
readCounter = 0;
//close (fd);
returnStatus = fclose(fd);
if(returnStatus)
NSLog(#"server error closing file");
So sporadically, the readCounter variable will not contain the same size as the file that was sent, but some times it does.
If it matters the file transfer is occurring between an iPhone and an iPhone simulator, both over WIFI. This happens regardless of if the phone is the server or if the simulator is the server.
If anyone can help me understand why this is occurring I'd appreciate it. I thought the whole purpose of TCP was to avoid this kind of problem.
(to give credit where it's due, for my server and client code I borrowed heavily from the book: The Definitive Guide to Linux Network Programming, by Davis, Turner and Yocom from Apress)
The recv function can receive as little as 1 byte, you may have to call it multiple times to get your entire payload. Because of this, you need to know how much data you're expecting. Although you can signal completion by closing the connection, that's not really a good idea.
Update:
I should also mention that the send function has the same conventions as recv: you have to call it in a loop because you cannot assume that it will send all your data. While it might always work in your development environment, that's the kind of assumption that will bite you later.
Tim Sylvester and gnibbler both have very good answers, but I think the most clear and complete is a combination of the two.
The recv() function return immediately with whatever is the in the buffer. This will be somewhere between 1 byte and MAXBUF. If the buffer is being written to while recv returns, you wont have the entire data that was sent in the buffer yet.
So you need to call recv() multiple times, and concatenate the data, to get everything that was sent.
A convenient way to do this (since we are working in cocoa) is to use NSMutableData like:
NSMutableData *fileData = [[NSMutableData alloc] init]; //Don't forget to release
while ((readCounter = recv(socket2, file, MAXBUF, 0)) > 0){
if (readCounter == -1){
NSLog(#"server could not read filename from socket");
close(socket2);
continue;
}
else{
NSLog(#"server reading file of size: %i", readCounter);
[fileData appendData:[NSData dataWithBytes:file length:readCounter]];
}
bzero(file, MAXBUF);
readCounter = 0;
}
You should probably have some kind of sequence of characters to signal termination of the file transfer, and only when you read those at the end of a block do you break out of your recv loop.
Of course, you will have to find a sequence that won't occur in your files, or that can be easily escaped. If you're working with text files this is pretty easy, but if not you'll have to be clever.
Alternatively, the client could first send the file size (in a separate send call), so the server knows how many bytes to expect in the file transfer.
recv returns right away with whatever is in the buffer (upto MAXBUF). If the buffer is being written to at the same time you might not get all the data
What TCP ensures is that your message will get to the remote peer correctly. As long as it fits in the send buffer, it will be automatically split into smaller chunks and sent by the local peer, and reordered and reassembled by the remote peer. It is not uncommon for a route to dynamically change while you are sending a message, which you would have to reorder manually (the smaller chunks) before delivering to your application.
As for your actual data transfer, your application needs to agree on a custom protocol. For instance, if you are only sending one message (the file), the sender could signal the receiver that it does not intend to write anymore to the socket (with shutdown(sock, SHUT_WR)), this way recv() returns with 0 and you know the transfer is complete (this is how a HTTP/1.0 server signals the client the transfer is complete). If you intend to send more data, then this alternative is not appropriate.
Another way would be to let the receiver know how much data the sender is going to transmit by including a header, for instance. It does not need to be overly elaborate, you could simply reserve the first 8 bytes to send the length as a 64-bit unsigned integer. In this case, you still need to be careful about byte ordering (big-endian / little-endian).
There is a very useful tutorial on network programming for UNIX environments:
Beej's Guide to Network Programming
You could refer to it to get a quick start, then refer back to the book for completeness, if you need. Even though you did not ask for additional references, TCP/IP Illustrated Vol. 1 and UNIX Network Programming Vol. 1 (both by W. Richard Stevens, the latter with a recent third edition) are excellent references.