first, understand the socket
1, socket is socket, in the TCP/IP protocol, "IP address +tcp or UDP port number" Unique identification of a process in network communications, "IP address +tcp or UDP port number" for the socket.
2. In the TCP protocol, the two processes (client and server) that establish the connection each have a socket to identify, then the socket pair of the two sockets will uniquely identify a connection.
3, the socket itself has "socket" meaning, so used to describe the network connection one-to-one relationship, for TCP/IP protocol Design Application layer programming interface called the socket API. second, the network byte order
In-memory multi-byte data has the size of the point, the disk file multibyte data in relation to the file offset address also has the size of the point, the same, network data flow also has the size of the end of the point.
Network data Flow address rules: the first issue of the data is low address, the data sent after the high address. The sending host usually sends the data in the send buffer in the order of the memory address from lowest to highest, in order not to cause the data stream to be disorderly, the receiving host will also save the data received from the network in the Order of memory address from low to high in the receiving buffer.
TCP/IP protocol: Network data flow should be a big-endian byte sequence, that is, low address high byte.
PS: If you do not understand the byte sequence of the big end byte sequence of children's shoes, you can see this article for reference: http://blog.csdn.net/qq_33951180/article/details/60767876
Because the size of the two hosts at both ends is not necessarily the same, so in order to make these network data more portable, so that the same code on the big and small end of the host can be normal operation, we can call the following library functions for network byte order and host byte order of the relevant conversion:
#include <arpa/inet.h>
//Converts host byte order to network byte order
uint32_t htonl (uint32_t hostlong);//converts 32 long integers from host byte sequence to network byte order.
//If the host byte order is a small end, then the function will do the corresponding size
///End conversion return, if the host byte order is large, the letter
//number does not convert, return the parameter intact ... Hereinafter
uint16_t htons (uint16_t hostshort);
Converts the network byte order to host byte order
uint32_t Ntohl (uint32_t netlong);
uint16_t Ntohs (uint16_t netshort);
H represents a host, n represents a network (net), L represents a 32-bit long integer, and S represents a 16 short integer.
implementation of TCP protocol communication
TCP protocol Communication process:
Let's introduce a couple of functions first:
1, the creation of sockets
int socket (int domain,int type,int protocol);
Domain: This parameter is generally set to af_inet, which means that the IPV4 address is used. There are more options to use man to view this function
//type: This parameter also has many options, such as sock_stream for streaming transport protocol, sock_dgram means datagram, we implement TCP here, so choose Sock_stream, If you implement the UDP optional SOCK_DGRAM
//protocol: protocol type, generally use default, set to 0
This function is used to open a network communication interface, error return-1, a successful return of a socket (file descriptor), the application process can be like read and write files called Read/write on the network to send and receive data.
2, Binding
int bind (int sockfd,const struct sockaddr*addr,socklen_t addrlen);
SOCKFD: Server Open sock
//The latter two parameters can refer to the introduction of Part IV
The network address and port number on which the server listens is generally fixed, and the client program can initiate a connection to the server after knowing the address and port number of the server program, so the server needs to call bind to bind a fixed network address and port number. Bind successfully returns 0, error returns-1.
The role of BIND () is to bind the parameter sockfd and addr together to sockfd the address and port number described by the file descriptor that is used for network traffic to listen to addr.
3. Monitor
int listen (int sockfd,int backlog);
The meaning of SOCKFD is the same as in bind. The
//backlog parameter is interpreted as the maximum number that the kernel queues for the secondary socket interface, which is generally 5~10, not too large (to prevent SYN attacks)
This function is used only by the server side, listen () declares that the SOCKFD is in a listening state, and that a maximum of backlog clients are allowed to be in a connection waiting state, which is ignored if more connection requests are received. Listen () successfully returned 0, failure returned-1.
4, receive the connection
int accept (int sockfd,struct sockaddr* addr,socklen_t* addrlen);
Addrlen is an incoming outgoing parameter, passing in the length of the caller's buffer cliaddr to avoid a buffer overflow problem, and the actual length of the client address structure, which may not be filled with the buffer area provided by the caller. If the CLIADDR parameter is passed NULL, it indicates that the client's address is not cared for.
A typical server program can serve multiple clients at the same time, when a client initiates a connection, the server calls accept () to return and receive the connection, if there are a large number of clients to initiate the request, the server is too late to process, no accept client is in the connection waiting state.
After three handshake finishes, the server calls accept () to receive the connection, and if the server calls accept () does not have a client connection request, it blocks waiting until a client is connected.
5. Request Connection
int connect (int sockfd,const struct sockaddr* addr,socklen_t addrlen);
This function only needs to have the client program to invoke, call this function to indicate the connection server, the parameter here is each other's address. Connect () successfully returned 0, error return-1.
After understanding these functions, let's take a step through the process of establishing a connection between the client program and the server program:
Server: First call socket () to create a socket to communicate, then call bind () to bind this file descriptor, and invoke listen () to listen to the port whether there is a client request, if there is, call accept () to connect, Otherwise, continue blocking until there is a client connection. Once the connection is established, it can be communicated.
Client: Call socket () to allocate a port for communication, and then call Connect () to send a SYN request and be blocked waiting for the server to answer the state, the server answered a syn-ack segment, the client received from connect () back, while answering an ACK section, After the server receives the return from accept (), the connection is established successfully. The client generally does not call bind () to bind a port number, not bind (), nor does the server have to bind ().
Study Questions : Why the client is not recommended for bind ().
A: When the client does not bind itself, the system randomly assigns a port number to the client, and when allocated, the operating system does not conflict with the existing port number. However, if you bind yourself, the client program is prone to problems, assuming that on a PC to open multiple client processes, if the user has bound the port number, will inevitably cause port conflicts, affect communication.
With some theoretical knowledge, we can write code:
"Server.c"
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include
<unistd.h> #include <netinet/in.h> #include <arpa/inet.h> int startup (int _port,const char* _ip) {
int sock = socket (af_inet,sock_stream,0);
if (Sock < 0) {perror ("socket");
Exit (1);
} struct sockaddr_in local;
local.sin_family = af_inet;
Local.sin_port = htons (_port);
LOCAL.SIN_ADDR.S_ADDR = inet_addr (_IP);
socklen_t len = sizeof (local);
if (Bind (sock, (struct sockaddr*) &local, Len) < 0) {perror ("bind");
Exit (2);
The IF (Listen (sock, 5) < 0)//allow the maximum number of connections to be 5 {perror ("listen");
Exit (3);
Return sock; int main (int argc,const char* argv[]) {if (argc!= 3) {printf (usage:%s [LOACL_IP] [loacl_port]\n], ARG
V[0]);
return 1; int listen_sock = Startup (Atoi (argv[2)), argv[1]//Initialize//To receive client socketThe structure body struct sockaddr_in remote;
socklen_t len = sizeof (struct sockaddr_in);
while (1) {int sock = accept (Listen_sock, (struct sockaddr*) &remote, &len);
if (Sock < 0) {perror ("accept");
Continue
printf ("Get a client, ip:%s, port:%d\n", Inet_ntoa (REMOTE.SIN_ADDR), Ntohs (Remote.sin_port));
Char buf[1024];
while (1) {ssize_t _s = read (sock, buf, sizeof (BUF)-1);
if (_s > 0) {buf[_s] = 0;
printf ("client:%s", buf);
else {printf ("Client is quit!\n");
Break
}} return 0; }
Client.c
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> int main (int argc,const char* argv[]) {if (a
RGC!= 3) {printf ("usage:%s [IP] [port]\n", argv[0]);
return 0;
//Create a socket int sock = socket (af_inet,sock_stream, 0) for communication;
if (Sock < 0) {perror ("socket");
return 1;
//need Connect is to the address of the end, so here defines the server-side address structure struct SOCKADDR_IN server;
server.sin_family = af_inet;
Server.sin_port = htons (atoi (argv[2));
SERVER.SIN_ADDR.S_ADDR = inet_addr (argv[1]);
socklen_t len = sizeof (struct sockaddr_in);
if (Connect (sock, (struct sockaddr*) &server, Len) < 0) {perror ("connect");
return 2;
}//Connection succeeded in receiving data Char buf[1024];
while (1) {printf ("send###");
Fflush (stdout);
ssize_t _s = Read (0, buf, sizeof (BUF)-1); BUF[_S] = 0;
Write (sock, buf, _s);
Close (sock);
return 0; }
However, this implementation can only be a single process of communication, that is, only one client connection at a time of data communication, which obviously does not meet the basic requirements of the server. We can think of ways to modify the server-side code, each accept successful after the creation of a subprocess, let the child process to deal with read and write data, the parent process continues to monitor and accept.
Specific code: Https://github.com/lybb/Linux/tree/master/TCP_pro
The modified code adds the following code between the socket () and bind () of the server program:
int opt=1;
SetSockOpt (sock,sol_socket,so_reuseaddr,&opt,sizeof (opt));
The option to set SOCKFD is SO_REUSEADDR 1, which means that multiple//socket descriptors with the same port number but different IP are allowed to be created
But if it's a waste of resources using the method of creating a subprocess, we can modify the method of creating the thread iv. SOCKADDR data Structure
The IPV4 and IPV6 address formats are defined in "Netinet/in.h", IPv4 in sockaddr_in structure, including 16-bit port numbers and 32-bit IP addresses, IPV6 with SOCKADDR_IN6 structure, including 16-bit port numbers, 128-bit IP address and some control fields.
The address format for UNIX Domain sockets is defined in Sys/un.h and is represented by the SOCKADDR_UN structure. The beginning of each socket address structure is the same, and the first 16-bit table ⽰ the length of the entire structure (not all UNIX implementations have length fields, such as Linux), and the latter 16 bits represent the address type. The address types of IPv4, IPV6, and UNIX Domain sockets are defined as constant af_inet, Af_inet6, and Af_unix respectively. In this way, you can determine the contents of a structure based on the address Type field, as long as you obtain the first address of a SOCKADDR structure, and you do not need to know which type of SOCKADDR structure is specific. So the socket API can accept various types of sockaddr struct pointers, such as bind, accept, connect functions, which should be designed to be of type void to accept various types of pointers, but the sock API is implemented earlier than ANSI c standardization, there is no void type at that time, so the parameters of this write function are represented by the struct sockaddr* type, which is required to force type conversion (useful in the BIND function) before the argument is passed.
The member struct IN_ADDR sin_addr in sockaddr_in represents a 32-bit IP address. But we typically represent IP addresses in dotted decimal strings, and the following functions can be converted between string representations and in_addr representations.
String to in_addr function: