Network programming (API)
1, tcp
1. Server side
1) Create socket
int socket(int domain, int type, int protocol); parameter: domain:protocol family,AF_INET type:Communication type,tcp The agreement is SOCK_STREAM protocal:Specific agreement,Generally 0, default protocol Return value: File descriptor
2) Bind the file descriptor returned by the socket to the local address and port
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); Parameters: sockfd: socket Returned file descriptor addr: Address structure pointer, storage ip Address and port number addrlen Length of address structure Return value 0 Note: the address structure is: struct sockaddr { sa_family_t sa_family; //Address protocol family char sa_data[14]; //Address port number } This is written to apply to various protocols, but 32-bit binary cannot be used IP Address and short integer The port number of type data is stored in a character array, so another structure is used Replace it: struct sockaddr_in { __SOCKADDR_COMMON (sin_family); //Address protocol family in_port_t sin_port; //Port number, 2byte struct in_addr sin_addr; //IPV4 address }; Note: the structure is in/usr/include/netinet/in.h Has been defined, Header file is<netinet/in.h> ;
Note: htons converts the port number from host byte order to network byte order (big end); inet_pton press the IP address AF_INET protocol family is converted into network address structure and stored in the third parameter.
3) Listen socket: listen to whether a client initiates a connection request
int listen(int sockfd, int backlog); parameter sockfd File descriptor backlog Number of waiting queue elements When the client initiates a connection to the server, the listening descriptor of the server is received on the port After the connection request of the client, the connection is not established immediately, but the request is placed on the server first In the queue to be, and then take out the requests from the queue in order to establish a connection. A return value of 0 indicates success
4) Receive tcp connection: the server receives the request and establishes a connection
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); Parameters: sockfd: File descriptor for listening Addr: The input parameter is the address structure of the connected client addrlen Length pointer of address structure Return value: file descriptor the descriptor used for communication
5) IO: use read/write or recv/send
6) close socket (file descriptor)
2. Client
1) socket: used for communication
2) connect: initiate connection actively
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); Parameters: sockfd Communication descriptor addr Pointer to the address structure of the target server addrlen Length of address structure Return value: success or failure 0/Not 0
3 ) IO
2, udp
1 System call used ssize_t sendto(int socket, void *message, size_t length, int flags, struct sockaddr *dest_addr, socklen_t dest_len); ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len); Note: this function blocks when no data is received 1) Server recvfrom socket Listener descriptor buffer Accepted data buf length buf size flags 0 address When recvfrom When receiving data, save the client address information, sendto Use this structure when address_len sockaddr Structure size sendto socket Listener descriptor message Data sent buf length Send data size flags 0 dest_addr client scokaddr_in Structure, recvfrom Filled structure dest_len sockaddr Structure size 2) client sendto socket descriptor message Data sent buf length Send data size flags 0 dest_addr Server scokaddr_in Structure, fill in the server address, port and other information dest_len sockaddr Structure size recvfrom socket descriptor buffer Accepted data buf length buf size flags 0 address Server scokaddr_in Structure that receives data from the server NULL address_len sockaddr Structure size NULL
Question: when the server communicates with the client, recv/send, read/write and recvfrom/sendto are blocking functions. If one client is connected and keeps opening, other clients will not be connected? Solving this problem requires server concurrency;
3, Concurrent server model
1. Multi process model: fock a child process for each connected client to perform I/O, but this will waste resources and is not recommended.
Code Description: if a client connects, it is its fork sub process. If it fails, close the file descriptor for communication and reconnect.
Code Description: if successful, I/O is performed in the child process
characteristic:
① fork is expensive. When forking, you need to copy all the resources of the parent process, including memory images;
② After fork ing child processes, the communication between parent-child processes and brother processes needs IPC mechanism, which brings difficulties to the communication;
③ Multi process still can't make effective use of system resources to a certain extent;
④ The number of processes in the system is also limited
2. Multithreading model: create a sub thread for each connected client for I/O, which will not consume so many resources. However, if there are common global variables, it is necessary to complete the mutual exclusion or synchronization of multithreads, which will be very complex.
Code Description: the communication descriptor needs to be passed into the sub thread for I/O, but before that, there may be a new client to connect, and the iClient will change, affecting the sub thread. Therefore, put the iClient in the heap application space to avoid this problem.
Code Description: create a thread. The following are thread functions:
3.I/O multiplexing model: call the function to create a table for monitoring the status of file descriptors, and then rotate all elements in the table. When a descriptor has a real IO request, the function returns to judge which file descriptor has an IO request, and then perform IO operations on the file descriptor. Then restore the table to its original state and continue monitoring
Function prototype: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); Parameters: nfds Maximum file descriptor+1 To monitor all file descriptors readfds Read the address of the file descriptor set. This set mainly stores the file descriptor, which may cause read operations writefds Write file descriptor set address exceptfds Exception file descriptor collection address timeout Time overflow NULL: Blocks until a file descriptor is ready or an error occurs The time value is 0: only the state of the file descriptor set is detected, and then it is returned immediately The time value is not 0: if no event occurs within the specified time, the timeout will be returned. Return value:>0 Really have IO Number of file descriptors for the operation =0 Time overflow but no IO <0 error doubt: select I don't know which descriptor state in the monitoring has changed, and how to find that descriptor? void FD_ZERO(fd_set *fdset) Clear descriptor set void FD_SET(int fd,fd_set *fdset) Add descriptor to collection void FD_CLR(int fd,fd_set *fdset) Clear descriptor from collection int FD_ISSET(int fd,fd_set *fdset) Determine whether the descriptor is in the collection
Note: the kernel allows the select function to monitor up to 1024 descriptors:
advantage:
- Avoid the resources and time spent creating multiple threads.
- The polling of socket is completed in kernel mode, which does not need to switch like multithreading and consumes resources.
The implementation of epoll can ensure that the performance is almost not affected by the number of connections (only connections and no other operations).