1, Overview of network programming
Network:
- Address (including IP address and port number)
- Data exchange (involving protocols such as http, tcp and UDP, in other words, data format)
- MCU will use port number protocol (uart)
Introduction to network programming all talk about sockets (sockets are divided into two types: tcp and UDP):
- TCP: connection oriented (want to be on the phone, high reliability)
- UDP: Message Oriented (equivalent to sending SMS. If the data volume is large, the memory response is fast, and the reliability is not so high, use it)
TCP/UDP comparison:
- TCP is connection oriented (for example, dial up to establish a connection before making a call); UDP is connectionless, that is, there is no need to establish a connection before sending data
- TCP provides reliable services. In other words, the data transmitted through TCP connection is error free, not lost, not repeated, and arrives in sequence; UDP does its best to deliver, that is, reliable delivery is not guaranteed
- TCP is byte stream oriented. In fact, TCP regards data as a series of unstructured byte streams. UDP is message oriented UDP without congestion control. Therefore, network congestion will not reduce the transmission rate of the source host (very useful for real-time applications, such as IP telephony, real-time video conference, etc.)
- Each TCP connection can only be point-to-point; UDP supports one-to-one, one to many, many to one and many to many interactive communication
- TCP header overhead 20 bytes; The header overhead of UDP is small, only 8 bytes
- The logical communication channel of TCP is full duplex reliable channel, while UDP is unreliable channel
Function of port number:
- A host with an IP address can provide many services, such as Web services, FTP services, SMTP services, etc
- These services can be realized through one IP address. So, how do hosts distinguish between different network services? Obviously, you can't rely on IP addresses alone, because the relationship between IP addresses and network services is a one to many relationship.
- In fact, different services are distinguished by "IP address + port number".
- Port provides an access channel,
- Servers are generally identified by well-known port numbers. For example, for each TCP/IP implementation, the TCP port number of the FTP server is 21, the TCP port number of each Telnet server is 23, and the UDP port number of each TFTP (simple file transfer protocol) server is 69
- We usually choose the port number between 5000 and 10000 for the Linux application layer
2, Byte order
x86 series and cpu are byte order of Little endian; Network byte order = large end byte order
Byte order overview:
Byte order refers to the storage order of each byte when multi byte data is stored in computer memory or transmitted through network.
Common sequence:
- Little endian: lower bytes are stored at the starting address.
- Big endian: store the high byte at the starting address.
Byte order unit:
(Byte) 1 Byte = 8bit (DOT)
One hexadecimal is represented by four binary.
Example: storage method of double word (32 bits) 0x01020304(DWORD) in memory
Memory address:
4000&4001&4002&4003
LE 04 03 02 01
BE 01 02 03 04
Byte order conversion api:
Header file: #include < netinet / in. H >
- uint16 _t htons(uint16 _t host16bitvalue); // Returns the value of the network byte order
- uint32_ t htonl(uint32 _t host32bitvalue); // Returns the value of the network byte order
- uint16 _t ntohs(uint16 _t net16bitvalue); // Returns the value uint32 of the host byte order_ t
- ntohl(uint32 _t net32bitvalue); // Returns the value of the host byte order
- h stands for host, n stands for net, s stands for short (two bytes) and s stands for long (four bytes). The conversion between host byte order and network byte order can be realized through the above four functions. Sometimes, you can specify the address with INADDR ANY and INADDR_ANY to let the operating system obtain it by itself
3, socket programming steps
Development steps of socket server:
- Socket: creates a socket. The return value is a socket descriptor, which provides a docking interface for subsequent network operations
- Bind: add information (IP address and port number) to the socket and bind the IP and port number to the channel created by the socket
- listen(): listen for network connections
- accept(): listen for client access and accept a connection
- read, write, etc.: data interaction
- Close: close the socket and disconnect
Development steps of client:
- socket(): give me a channel. I know your IP address and port number
- connect(): call this function to connect
- read, write, etc.: data interaction
4, Brief analysis of API provided by Linux
1. Create socket
int socket(int domain, int type, int protocol);
domain:
Function: indicate the protocol family used, usually AF_INET, indicating internet protocol family (TCP/IP protocol family)
- AF_UNSPEC not specified
- AF_ROUTE socket
- AF_KEY socket
- Af_unix domain
- AF_INET6 IPv6 Internet domain
- AF_INET IPv4 Internet domain
The type parameter specifies the type of socket
- SOCK_RAW: allows programs to use low-level protocols. The original socket allows direct access to low-level protocols such as IP or ICMP. It is powerful but inconvenient to use. It is mainly used for the development of some protocols.
- SOCK_DGRAM: datagram socket defines a connectionless service. Data is transmitted through independent messages. It is disordered, and is not guaranteed to be reliable and error free. It uses datagram protocol UDP
- SOCK_STREAM: streaming socket provides reliable, connection oriented communication flow; it uses TCP protocol to ensure the correctness and sequencing of data transmission (we usually make it)
protocol:
- Normally assigned a value of 0
0 select the default protocol corresponding to the type:
- Ipproto _udptransport protocol
- Ipproto _sctptransport protocol
- Ipproto_tcp transmission protocol
- Ippotor_tipc transport protocol
2. The address is ready
bind() function: IP number, port number and corresponding description word assignment function:
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
Function:
- Used to bind IP address and port number to socketfd
Parameters:
- sockfd: is a socket descriptor
- addr: is a pointer to the sockaddr type containing organic IP address, port number and other information. It points to the protocol address structure to be bound to sockfd. This address structure varies according to the address protocol family when creating a socket.
- addrlen: length of the second parameter
Structure for the second parameter:
//IPV4 corresponds to struct sockaddr { sa_family_t sa_family;//protocol family char sa_data[14];//IP + port number };//This is generally not used. We use the following one, so we need to do a forced pointer conversion when using it Equivalent replacement: struct sockaddr_in { sa_family_t sin_family;//protocol family int_port_t sin_port;//Port number struct in_addr sin_addr;//IP address structure unsigned char sin_zero[8];/*Filling, no practical significance, just for Align with the sockaddr structure in memory so that they can be converted to each other*/ Find the instructions for this structure: cd /usr/include/ :/*Enter the include directory*/ grep "struct sockaddr_in {" * -nir :/*Recursive search in quotation marks under the current directory Things, n: display line number; r: recursive; i: case insensitive*/ vi linux/in.h +184 :/*Go to and find line 184 of the header file*/ };
We define a struct SOCKADDR_ After the structure of in, use the memset function to clear the data, such as memset (& s_addr, 0, sizeof (struct sockaddr_in));
IP address translation API:
int inet_aton(const char *straddr,struct in_addr *addrp); //Straddr: IP string addrp: memory address of IP //Convert "192.168.1.123" in string form to a format recognized by the network char *inet_ntoa(atruct in_addr inaddr); //Convert the IP address in network format to string form
3. Monitor
listen() function: listening setting function:
int listen(int sockfd,int backlog);
Function:
- Set the maximum number of connections that can be processed. listen() does not start to accept connections, but only sets the listen mode of sockect. The listen function is only used on the server side. The server process does not know who to connect with. Therefore, it will not actively request to connect to a process, but will always listen to whether other client processes are connected to it, and then respond to the connection request, A service process can handle the connection of multiple client processes at the same time. There are two main functions; Convert an unconnected socket to a passive socket (listening), and specify the maximum number of connections queued by the kernel for the corresponding socket.
- The kernel maintains two queues for any given listening socket:
(1) . if the connection queue is not completed, each SYN message segment corresponds to one of them: it has been sent by a client and arrived at the server, and the server is waiting to complete the corresponding TCP three-time handshake process. These sockets are in SYN_REVD status
(2) . the connection queue has been completed, and each client that has completed the TCP handshake process three times corresponds to one of them. These sockets are in the ESTABLISHED state
Parameters:
- sockfd: is the server-side socket descriptor returned by the socket system call
- backlog: Specifies the maximum number of requests allowed in the request queue
4. Connect
accept() function:
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
Function:
The accept function is called by the TCP server to return the next completed connection from the head of the completed connection queue. If the completed connection queue is empty, the process is put into sleep.
Parameters:
- sockfd: is the server-side socket descriptor returned by the socket system call
- addr: used to return the protocol address of the connected peer (client). It doesn't care to set NULL
- addrled: the length of the client address. It doesn't care to set NULL
The second parameter of accept is also a struct sockaddr_in structure. The usage is the same as that of bind
Return value:
The return value of this function is a new socket descriptor. The return value represents the connected socket descriptor. The first parameter is the server listening socket descriptor. A service super usually only creates a listening socket, which always exists in the life cycle of the server. The kernel creates a connected socket for each client connection accepted by the server process (indicating that the TCP three handshakes have been completed). When the server completes the service to a given client, the corresponding connected socket will be closed.
5. Data transceiver
(1) . byte stream reading function:
The byte reading functions in socket communication: read(), write(). They are slightly different from the reading functions in I/O because they may input or output fewer bytes than requested.
ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count);
There are also some functions in network I/O, such as recv()/send(); readv()/writev()
recvmsg()/sendmsg(); recvfrom()/sendto() {the latter two groups are generally used for UDP connections}
A set of API s commonly used for data sending and receiving:
(2) . send data function on TCP socket:
ssize_t send(int s,const void *msg,size_t len,int flags);
- It contains three elements: socket s, pending data msg, and data length len
- The function can only be used for sockets in the connected state. The parameter s is the socket descriptor for which the connection has been established, that is, the return value of the accept function
- The parameter msg points to the buffer in which the data to be sent is stored
- The parameter len is the length of the data to be sent, and the parameter flags is the control option, which is generally set to 0
(3) . functions for receiving data on TCP socket:
ssize_t recv(int s,void *buf,size_t len,int flags);
- It contains three elements: socket s, receive buffer buff and length len
- The recv function receives from the socket descriptor specified by the parameter s (which must be a connection oriented socket)
- And save the data to the buffer specified by the parameter buff
- The parameter len is the buffer length, and the parameter flags is the control option, which is generally set to 0
6. connect function of client
connect() function: client connects to host:
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
Function:
This function is used to establish a connection between the bound client (client) and the server
Parameters:
- sockfd: is the socket descriptor of the current server
- addr: is the address structure pointer of the server's IP address and port number
- addrlen: the address length is often set to sizeof(struct sockaddr)
Return value:
0 is returned successfully, and - 1 is returned when an error is encountered, and the errno contains the corresponding error code
5, socket server code implementation
#include<stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include<stdlib.h> #include<string.h> // int socket(int domain, int type, int protocol); //int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); int main(int argc,char **argv) { int s_fd; int c_fd; char readbuf[128]; int n_read; // char *msg = "I get your message"; char msg[128] = {0}; int mark = 0; struct sockaddr_in s_addr;/*The structure to be used in bind, It is defined in the kernel and can be declared*/ struct sockaddr_in c_addr; if(argc != 3){ printf("param is not good\n"); exit(-1); } memset(&s_addr,0,sizeof(struct sockaddr_in)); memset(&c_addr,0,sizeof(struct sockaddr_in)); //1. socket s_fd = socket(AF_INET,SOCK_STREAM,0); if(s_fd == -1){ perror("socket"); exit(-1); } s_addr.sin_family = AF_INET; s_addr.sin_port = htons(atoi(argv[2]));/*This involves the port number, We generally use 5000 ~ 9000, and the following 5000 is the operating system, which also involves the problem of byte order ,Because x86 is the small end and the network is the large end, the byte order conversion api is used here*/ inet_aton(argv[1],&s_addr.sin_addr); //2. bind bind(s_fd,(struct sockaddr *)&s_addr, sizeof(struct sockaddr_in)); //3.listen listen(s_fd,10);//There are two queues. The two queues add up to 10 //4.accept int clen = sizeof(struct sockaddr_in); while(1){ c_fd = accept(s_fd,(struct sockaddr*)&c_addr,&clen);/* This function will detect the client to be connected and return a socket descriptor of successful connection ,Follow up operation depends on it*/ if(c_fd == -1){ perror("accept"); } mark++; printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr)); if(fork() == 0){ if(fork() == 0){ while(1){ sprintf(msg,"get:welcom No.%d client",mark); write(c_fd,msg,strlen(msg)); sleep(3); } } while(1){ //5.read memset(readbuf,0,sizeof(readbuf)); n_read = read(c_fd,readbuf,128); if(n_read == -1){ perror("read"); }else{ printf("getmessage:%d,%s\n",n_read,readbuf); } } break; } } return 0; }