function

Telnet connects to the server. The server sends hi to Telnet and disconnects.

Server:

[hqinglau@centos 01-015]$./ a.ut 172.21.16.6 12347 connect 42.193.121.128:47282 now exit...Copy the code

The IP address here is 172.21.16.6 because I use the cloud server, and ifconfig found that it is this IP address. However, 42.193.121.128 is available for external IP addresses.

Linux test:

[hqinglau@centos ~]$Telnet 42.193.121.128 12347 Trying 42.193.121.128... Connected to 42.193.121.128. Escape character is '^]'. Hi Connection closed by foreign host.Copy the code

Windows Extranet test:

Microsoft Telnet> o 42.193.121.128 12347Connecting to42.193.121.128. Hi lost the connection to the mainframe. Press any key to continue...Copy the code

The code analysis

For parameter Settings, we need the IP address of the host, and the port number. You can set it to give the user a hint.

if (argc <= 2)
{
    printf("usage: %s ip_address port_number\n".basename(argv[0]));
    return EXIT_FAILURE;
}
Copy the code

Here are the tips:

[hqinglau@centos 01-015]$ ./a.out 
usage: a.out ip_address port_number
Copy the code

Get IP and port:

const char *ip = argv[1];
int port = atoi(argv[2]);
Copy the code

Sockaddr_in is defined as follows:

/* Structure describing an Internet socket address. */
struct sockaddr_in
{
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;			/* Port number. */
    struct in_addr sin_addr;		/* Internet address. */

    /* Pad to size of `struct sockaddr'. */
    unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];
};
Copy the code

Sin_family indicates the protocol, sin_port indicates the port, and sin_addr indicates the address.

struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET,ip,&address.sin_addr);
address.sin_port = htons(port);
Copy the code

AF_INET (also called PF_INET) is an IPv4 socket, while AF_INET6 is an IPv6 socket. AF_UNIX is local communication for Unix systems.

The purpose of AF_INET is to communicate using IPv4. Because IPv4 uses 32-bit addresses, calculations are faster than IPv6’s 128-bit addresses, making it easier to use for LAN communications.

And AF_INET is more general than AF_UNIX because Windows has AF_INET but not AF_UNIX.

Int_pton converts the address to a number, presentation to numeric. Such as 1.2.3.4 or localhost can be used.

Then create the socket.

int sock = socket(AF_INET,SOCK_STREAM,0);
assert(sock>=0); / / assertions
Copy the code

The socket function only uses the protocol type and socket type (e.g., SOCK_STREAM, SOCK_DGRAM message). The third parameter is left blank.

A socket is a connected node. After it is created, it needs to bind address ports to communicate with other nodes.

int ret = bind(sock,(struct sockaddr *)&address,sizeof(address));
if(ret==- 1)
{
    perror("bind error: ");
    return EXIT_FAILURE;
}
Copy the code

At this point, the socket is ready.

The three-way handshake is as follows:

The server needs to listen first, that is, listen to the port to accept the client request.

ret = listen(sock,5);
assert(ret! =- 1);
Copy the code

Also create the address structure of the client.

struct sockaddr_in client;
socklen_t client_addrlen = sizeof(client);
int connfd = accept(sock,(struct sockaddr*)&client,&client_addrlen);
if(connfd<0)
{
    perror("accept error: ");
    return EXIT_FAILURE;
}
Copy the code

The server then blocks in the Accept function, waiting for the connection to arrive. After the three-way handshake connection is established (more on that later), data can be transferred.

For convenience, you can display the client information on the server.

char client_ip[32];
inet_ntop(AF_INET,&client.sin_addr,client_ip,sizeof(client_ip));
printf("connect %s: %d\n",client_ip,ntohs(client.sin_port));
printf("now exit... \n");
Copy the code

The following information is displayed:

[hqinglau@centos 01-015]$./ a.ut 172.21.16.6 12347 connect 202.107.195.199:6790 now exit...Copy the code

Try sending a piece of data:

send(connfd,"hi\n".2.0);
Copy the code

Code connection: tinyWebServer

Tcpdump analyzes captured packets one by one

Server:

Connect 42.193.121.128:51132 now exit...Copy the code

Tcpdump packet capture command:

[hqinglau@centos ~]$ sudo tcpdump -nn -X tcp port 12347
Copy the code

The result:

6 packets captured
6 packets received by filter
0 packets dropped by kernel
Copy the code

6 packets are captured.

Packet capture result:

listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:31:41.718544 IP 202.107.195.199.5705 > 172.21.16.6.12347: Flags [S], seq 2459730644, win 64240, options [mss 1380,nop,wscale 8,nop,nop,sackOK], length 0
Copy the code

Client port 5705 corresponds to server port 12347. Here we are focusing only on Flags, SEQ and ACK. Win is the window size, so don’t worry about it for now.

IP address 202.107.195.199.5705 is the client and 172.21.16.6.12347 is the server. The cloud server I use has a public IP address 42.193.121.128, which means the same host.

The values for Tcpflags are explained in the manual as follows:

Tcpflags are some combination of S (SYN), F (FIN), P (PUSH), R (RST), U (URG), W (ECN CWR), E (ECN-Echo) or . (ACK), or none if no flags areset.

Review the TCP connection three-way handshake:

Flags [S] indicates that the client sends the SYN to the server for the first handshake. Seq is 2459730644, so the next ACK should be 2459730645.

12:31:41.718601 IP 172.21.16.6.12347 > 202.107.195.199.5705: Flags [S.], 
seq 3415114482, ack 2459730645, win 16060, options [mss 1460,nop,
nop,sackOK,nop,wscale 6], length 0
Copy the code

Check out the next bag, sure enough! Flags [S.] Indicates that the server sends SYN+ACK to the client.

12:31:41.758324 IP 202.107.195.199.5705 > 172.21.16.6.12347: Flags [.], ack 1, win 1024, length 0
Copy the code

The client then sends Flags [.] (ACK) to the server. At this point, the three-way handshake ends and the client and server establish a connection.

The server then sends a hi\n to the client.

12:31:41.758487 IP 172.21.16.6.12347 > 202.107.195.199.5705: Flags [P.], SEQ 1:4, ACK 1, Win 251, Length 3 0x0000: 4500 002b e305 4000 4006 0d79 ac15 1006 E.. +.. @. @.. y.... 0x0010: ca6b c3c7 303b 1649 cb8e 82f3 929c 82d5 .k.. 0; .I........ 0x0020: 5018 00fb 479e 0000 6869 0a P... G... hi.Copy the code

The last 0a represents the newline key (which is also part of the sent data), and it is obvious that three bytes hi\n were sent.

Flags [P.] indicates P (PUSH).

After data is sent, the server stops the port and exits.

We hear about three handshakes, four waves, but there’s only two bags left.

The client thinks I’m ready to crack and sends a FIN to the server. The server replies with an ACK indicating that I know it. The client then sends an ACK indicating that I know it too.

Therefore, there are only two cases in which the server voluntarily shuts down. As follows:

12:31:41.758607 IP 172.21.16.6.12347 > 202.107.195.199.5705: Flags [F.], SEq 4, ACK 1, win 251, length 0 0x0000: 4500 0028 e306 4000 4006 0d7b ac15 1006 E.. (.. @. @.. {... 0x0010: ca6b c3c7 303b 1649 cb8e 82f6 929c 82d5 .k.. 0; .I........ 0x0020: 5011 00fb ba0e 0000 P.......Copy the code
12:31:41.797980 IP 202.107.195.199.5705 > 172.21.16.6.12347: Flags [.], ACK 5, win 1024, length 0 0x0000: 4568 0028 9562 4000 fb06 9fb6 ca6b c3c7 Eh.(.b@...... k.. 0x0010: ac15 1006 1649 303b 929c 82d5 cb8e 82f7 ..... I0; . 0x0020: 5010 0400 b709 0000 P.......Copy the code

Flags [F.] and Flags [.] are FIN and ACK.

Advanced content

sendfile

int fd = open(filename,O_RDONLY);
if(fd<0)
{
    perror("open file error: ");
    exit(EXIT_FAILURE);
}
struct stat stat_buf;
fstat(fd,&stat_buf);

sendfile(connfd,fd,NULL,stat_buf.st_size);
Copy the code

Obviously, fstat is used to get file information after opening file. In sendFile, file size is used mainly.

#include<sys/sendfile.h>
ssize_t senfile(int out_fd,int in_fd,off_t* offset,size_t count);
Copy the code

The in_fd parameter is the file descriptor of the content to be read, and the out_fd parameter is the file descriptor of the content to be written. The offset parameter specifies where the file stream is read from. If null, the default starting position for the file stream is used. The count argument specifies the number of bytes transferred between the file descriptors in_fd and out_fd.

In_fd must be a file descriptor that supports mmap-like functions, that is, it must point to real files, not sockets and pipes. Before Linux2.6.33, out_fd must be a socket, and after Linux2.6.33, out_fd can be any file.

When a file needs to be transferred, the details of the traditional Read /write socket transfer process are as follows:

2. The read function returns a copy of file data from the kernel buffer to the user buffer. 3. The write function calls a copy of file data from the user buffer to the kernel socket-related buffer. Data is copied from the socket buffer to the relevant protocol engine.

Four copy operations occurred during this process.

Disk -> Kernel -> User -> Socket Buffer (kernel) -> Protocol Engine.

How does SendFile work?

1. The system call sendFile () copies the disk data to the kernel buffer via DMA, and then the data is directly copied by the kernel to another kernel buffer related to sockets. There is no switching between user state and core state; copying from one buffer to another is done directly in the kernel. 2. DMA copies data directly from the kernel buffer to the protocol stack without switching, and does not require data from the user state and the core state, because the data is in the kernel.

Server:

[hqinglau@centos 01-015]$ ./a.out 
usage: a.out ip_address port_number filename
[hqinglau@centos 01-015]$ ./a.out localhost 12347 00_model.cc
Copy the code

Client:

[hqinglau@centos ~]$ telnet localhost 12347
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
#include <sys/socket.h>
#include <netinet/in.h>. close(connfd); close(sock); return EXIT_SUCCESS; } Connection closed by foreign host.Copy the code

writev

** The readv and writev functions are used to read and write multiple discontinuous buffers in a single function call. These two functions are sometimes called Scatter Read and Gather Write.

#include <sys/uio.h>
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
Copy the code

The return values of both functions: the number of bytes read and written on success or -1 on error.

struct iovec {
    void      *iov_base;      /* starting address of buffer */
    size_t    iov_len;        /* size of buffer */
};
Copy the code

The problem here is that the data is too large and may not be read or written completely. The next read/write will change the starting address and length of the IOV array elements. More on this in the tinyWebServer project.