The first thing to say is the flow protocol and the sticky packet problem:
Before we wrote the few that are based on the TCP transport Protocol socket communication, and we know that the TCP transmission data is based on stream flow, what is the meaning of it.
Let's just say that TCP is not boundary-sensitive when transmitting data (there is no boundary between data and data), because it is based on byte stream, so the data is a lot of bytes without structure difference to TCP. What that means. This means that TCP does not differentiate the overall information of multiple data (for example: it's like you say a bunch of words without punctuation all together, others are likely to be mistaken) or to a single overall information The error distinction (for example, you want to send the whole piece of data is divided into several pieces of sending, and then the data in the transmission process may be due to network reasons, some large chunks of fragmented fragments of the fragment to, perhaps because the receiver buffer full, start reading, and they do not have boundary points, this time to explain the error ). That would make the information we're transmitting possible to stick together and be interpreted incorrectly by the computer.
This is called sticky bag, and how do we solve this problem?
1. Fixed length package
2. Add \r,\n characters at the end of the package (FTP uses this strategy, if the contents of the package also contains \r,\n, this time need to use the escape character \ Processing)
3. Baotou plus the length of the package (receive the packet header according to Baotou to calculate the length of the package body to receive the package)
4. More complex application-layer protocols
First , the first fixed-length package:
The length of each packet sent is fixed, no matter how big a message packet, to our sending area are zoned fixed length to send, assuming we this fixed length of 1024 (the general programming can take 46~1500 bytes, less than 46 will fill you to 46 bytes when sent, Over 1500 of the transfer process will be fragmented by Ethernet for you).
There are several possibilities:
1. The packet length is less than 1024, the most part of the message packet is complete with a blank byte, the length of 1024 bytes is sent.
2. The packet length is greater than 1024, when the package is divided into several parts, assuming 2049 bytes, then divided into 3 segments, number 1:0~1023, number 2:1024~2047, number 3: (2048+ padding blank bits 1023 bytes). The last byte sent is 3072 bytes of data.
Since such packets are fixed-length and do not send multiple messages with different data structures at the same time as above, it means that the accepted packets cannot be grouped together without being interpreted, because a single packet is a separate data structure.
The biggest flaw is that it can not play the TCP protocol efficiency, and greatly wasted network traffic, a lot of invalid data transmission on the network, is not recommended to use.
The second is to add \r,\n and other characters at the end of the package
Our common FTP server is this way, in distinguishing between the \ R and \ n characters in the package using the escape character \ Solve, but this limit can only do some special service, because we have to ensure the uniqueness of the Terminator, in the normal data transmission, Because the user's input is not qualified, what kind of characters have, so can not be applied casually.
The third type: Baotou plus the length of the package body
This is one of the most widely used transmission methods, which guarantees the efficiency of TCP and solves the problem of sticky packets.
The following to simulate this transmission mode: In fact, is to encapsulate a send and accept the function, and then in the acceptance of data in the packet header data analysis, to find out the actual length of the data Baotou tells us, and then two times to accept, that is the data we want
Note: This is based on the first two of the multi-user/server interaction
First Look at server.c:
//defines a packet structure with len struct packet {int len; Record Char buf[1024]; Here's 1024 to do the longest limit of the single longest packet}//simply say here ssize_t: is a signed shape, on 32-bit machine is 4 bytes long, on the 64-bit machine is a 8-byte long, this difference is not much said, Afterwards the author will write a general summary of the origins of 32 and 64-bit correlation and similarities and differences//size_t is unsigned shaping//encapsulation of a READN ssize_t readn (int fd,void* buf,size_t count) {size_t NL EFT = count;
Defines the number of bytes left in each read ssize_t nread;
char* BUFP = (char*) buf; while (Nleft > 0) {if ((Nread = Read (fd,bufp,nleft)) < 0)//read If the number of bytes read is successfully returned {if (err
No = = eintr)//If the signal is interrupted, not error, re-read continue;
return-1; } else if (nread = = 0) return count-nleft; The read data is empty and returns 0, indicating that the connection is disconnected BUFP + = nread; After reading the nread byte data, the pointer is shifted down this amount, the next &BUFD is starting from here nleft-= nread; The setting here is to read the nleft many times, as long as the Nleft and >0, repeat the cycle} return count; All execute correctly returns the read count, starting at 4, followed by Len}
Think about or separate to write, below continue to encapsulate a writen
Or a brief description of the function, it is based on the write prototype, from the point where the pointer refers to the BUF, send the size count of the data, read is the same, the same can know its role, the difference is that we add a return value on the read, Implement the functionality we need
ssize_t writen (int fd,const void* buf,size_t count)
{
size_t nleft = count;
ssize_t Nwritten;
char* BUFP = (char*) buf;
while (Nleft > 0)
{
if ((Nwritten = Write (fd,bufp,nleft)) < 0)
{
if (errno = = eintr)
continue;
return-1;
}
else if (Nwritten = = 0)
continue;
BUFP + = Nwritten;
Nleft-= Nwritten;
}
return count;
}
Next we encapsulate the process of using READN and writen as a function that is called at the time of the fork--that is, before we write the read buffer and then the write back to the client. The difference is that we are all using our own package of READN and writen, plus two read judgment.
void do_service (int conn) {struct packet recvbuf; int n; Here we may not be able to understand what this n is, first remember that this is when the client operation is to identify the length of the sending packet, that is, it is used to identify the Len in the struct packet, finish writing the client and then come back to see the while (1) {memset (
&recvbuf,0,sizeof (RECVBUF));
int ret = READN (conn,&recvbuf.len,4);
if (ret = =-1) err_exit ("Readn");
else if (Ret < 4) {printf ("client closed_1, the reason may be closed, or the connection is broken .... \ n");
Break } n = Ntohl (Recvbuf.len); Converts the network byte order to machine byte order ret = READN (conn,recvbuf.buf,n);
Here the BUF pointer has pointed to the 5th byte, passing through the first READN if (ret = =-1) err_exit ("Readn");
else if (Ret < n) {printf ("client closed_2, the reason may be closed or the connection is broken .... \ n");
Break
} fputs (Recvbuf.buf,stdout); Writen (Conn,&recvbuf,4+n); Back-up client, write to socket}}
Finally, the code in Main--first clear the idea, the front or the same, create a socket, bind address, listen to it, and then wait for the Accept event occurs, once the client connects, fork a child process to handle the read operation, the parent process continues to listen for new client connections, Some words continue to fork ....
Create, bind, listen ... Wait for the code to be omitted, the details look at the previous chapters, then directly write to the client operation of the connection
int main (void)
{
//.... Slightly
struct sockaddr_in peeraddr;
socklen_t Peerlen = sizeof (PEERADDR);
int conn;
pid_t pid;
while (1)
{
if (conn = Accept (LISTENFD, (struct sockaddr*) &peeraddr,&peerlen)) < 0)
Err_exit ( "Accept");
printf ("IP address is:%s port is:%d\n", Inet_ntoa (PEERADDR.SIN_ADDR), Ntohs (Peeraddr.sin_port));
PID = fork ();
if (PID = =-1)
err_exit ("fork");
if (PID = = 0)
{
close (LISTENFD);
Do_service (conn);
Exit (exit_success); Jumps out of the Do_service loop, indicating that the connection is broken, and exits the child process directly
}
else
close (conn);
}
return 0;
}
Tidy up the general process of the above program:
1. First package two functions Readn and writen, the function is to implement the message packet can be sent or read a certain length of time to specify the data, here we use the int type 4 bytes to record The actual size of the data to be passed , so the send and receive is specified as 4 bytes first.
2. After receiving 4 bytes, read the Len length, note that the network byte order is first converted to machine byte order, then the next read Readn, starting at the pointer read from the previous READN, specifies that the number of bytes read is Len, and this time the read will be looped to the length of the Len.
3. After reading, like the client writen a total of len+4 bytes of RECVBUF data, it can be thought that the client is also through the above 1, 22 procedures to read the message sent back by the server.
The process of 4.fork process is the old routine: Create->bind-> Monitor->accept do not block->fork child process start processing ... perform the above 3 procedures.
OK, finished, to write our client side:
First, the two Readn and writen functions are exactly the same, and can be pasted in the past (code reuse so easy->__->)
Similarly, define a packet structure struct packet {int len;
Char buf[1024]; }//directly write our main function, because is the client, we have to do is only the customer input can be int main (void) {//... Create, address or something .... Briefly//Connect successfully, start the following procedure if (Connect (sock, (struct sockaddr*) &servaddr,sizeof (servaddr)) < 0) Err_exit ("Conne
CT ");
The client has two, one send, and the other is the data that holds the server back packet sendbuf;
struct packet recvbuf;
memset (&sendbuf,0,sizeof (SENDBUF));
memset (&recvbuf,0,sizeof (RECVBUF));
int n;
while (Fgets (sendbuf.buf,sizeof (SENDBUF.BUF), stdin)! = NULL) {n = strlen (SENDBUF.BUF); Sendbuf.len = htonl (n);
Len identifies the length of the data to be sent writen (sock,&sendbuf,4+n); int ret = READN (sock,&recvbuf.len,4);
Read first 4 bytes if (ret = =-1) err_exit ("Readn");
else if (Ret < 4) {printf ("client_close_1, the reason may be closed, or the link is broken \ n");
Break
} n = Ntohl (Recvbuf.len);
ret = READN (sock,recvbuf.buf,n);
if (ret = =-1) Err_exit ("Readn");
else if (Ret < n)//Note this is the time to drop the packet, stating that there was an error {printf ("client_close_2, the cause may be closed, or the link may have been broken");
Break
} fputs (Recvbuf.buf,stdout);
memset (&sendbuf,0,sizeof (SENDBUF));
memset (&recvbuf,0,sizeof (RECVBUF));
} close (sock);
return 0; }
summarize the client-side process:
1. First, the old routine walk again, then try to ConnectServer end, connected on the block in the fgets, from the keyboard input.
2. The input data is stored in the BUF in the package SendBuf , SendBuf len identifies the length of the buf, that is, the length of the input data from the keyboard, after encapsulating the packet, execute writen, Writes the entire sendbuf to sock.
3. After sending, the process has nothing to do, then let it accept the server back to the data bar, so next, read the 4 bytes (int), if the read is successful, the read to the Len to the local byte-order n = = Ntohl ( Recvbuf.len), proceed to a readn, starting from where the previous pointer points, reading n-length characters , and then printing it to standard output.
program Run screenshot:
Summary:
Clear thinking is the most important link, need to be familiar with the man Help manual, like the above own package of READN and writen are used by the read and write prototype, the general clear function can direct the man function name, man 2 function name-2 indicates some system calls the Kernel command, Mans 3 Function name--standard C library functions. The rest will be used to learn again ~ ~ ~
Code after testing can be used, there are errors to point out, thank you!