Two kinds of in-depth one of UNIX Programming a simple chat room realized (fcntl and select) Author: Unknown eDOC Working Group on the Internet quite popular today, chatting on the Internet for a lot of "geek" is already commonplace. The chat room program can be said to be the easiest multi-point communication program on the Internet. There are many implementation methods for chat room, but all use so-called "multi-user space" to exchange information, with typical multi-channel I / O architectures. A simple chat room, from the programmer's point of view, is a multi-to-many communication between multiple I / O endpoints. Its architecture is shown in Figure 1. Such an implementation is that other users can get this sentence after any one of the chat indoors in the user's eyes. This "multi-user space" architecture is very wide in other multipoint communication programs, and its core is multiple I / O communication. Multi-channel I / O communication is also known as I / O multi-channel (I / O multiplexing) is generally used in the following occasions: the client requires simultaneous processing of interactive inputs and network connections between the servers. Processing I / O multi-channel multiplexing problems; clients need to respond to multiple network connections (this situation is rare); TCP servers need to process Sockets in listening status and multiple connection status simultaneously; server needs to process more A network protocol's socket; server needs to handle different network services and protocols. The situation in which the chat room needs to face is the first and third cases. We will make you more understanding multiple I / O and its implementation methods through the establishment of a functional chat room on the TCP / IP protocol. We have to discuss the function of the chat room, interested friends can expand their feature, develop into a completely complete chat room, such as plus user authentication, user nickname, secret information, semote and other functions. First it is a The client of the Client / Server first starts server, then the user is connected using the client. The advantage of the Client / Server structure is fast, and the shortcoming is that the client is also updated when the server is updated. Network initialization is first initializing Server, so that Server Enter the listening status: (For the sake of simplicity, the following quoted procedures are slightly transported, the following) SOCKFD = Socket (AF_INET, SOCK_STREAM, 0); // Building a Socket, the family is AF_INET, the type is SOCK_STREAM. / / AF_INET = ARPA Internet Protocol Using the TCP / IP protocol // SOCK_STREAM type provides sequential, reliable, byte-based full-duplex connections. // Since there is only one protocol in the protocol family, the third The parameter is 0 bind (SockFD, (STRUCKADDR *) & serv_addr, sizeof (serv_addr)); // Binds this socket with an address. // Serv_addr includes SIN_FAMILY = AF_INET protocol Socket // sin_addr.s_addr = HTONL (INADDR_Any) Server Accepted connection. // sin_port = htons (serv_tcp_port) Server listened to port // In this program, Server's IP and listening ports are stored in Config Document. Listen (SOCKFD, MAX_CLIENT); // After the address binding, Server enters the listening state. // max_client is the total number of Clients that can be established simultaneously. Server enters the Listen state, waiting for the client to establish a connection.
Client End To establish a connection, you also need to initialize the connection: SOCKFD = Socket (AF_INET, SOCK_STREAM, 0)); // The same, the client also creates a socket first, the parameters are the same as Server. Connect (Sterkfd, (Struct SockAddr *) & serv_addr, sizeof (serv_addr)); // Client uses Connect to create a connection. The variables in serv_addr are set to: // sin_family = AF_INET protocol family with socket // sin_addr.s_addr = inet_addr (serv_host_addr) address is Server / / The address of the computer. // sin_port = htons (serv_tcp_port) port is the port of Server listening. When the request is sent to the server, Server uses accept to accept the connection: Accept (Sockfd, (Struct) SOCKADDR *) & cli_addr, & cli_len; // When the function returns, the information whose CLI_ADDR is kept // includes the other party's IP address and the port used by the other party. // accept returns a new file descriptor. After server enters the Listen state, since multiple users have online, the program needs to operate these users at the same time and implement information exchange between them. This is referred to as I / O multi-channel multiplexing techniques. Multiplexing generally has the following methods: Non-blocking communication method: Set the file pipe to non-block communication mode, each other is a polling, to determine if read / write operations can be performed. The disadvantage of this way is that the cost is too high, and most of the resources are wasted on the polling. Sub process method: Apply multiple sub-processes, each communication with a single work block. All child processes communicate via IPC and parent processes. Parent processes all information. The disadvantage of this approach is complex, and since IPC is not exactly consistent on the various operating system platforms, the transplantability is reduced. Signal drive (SIGIO) asynchronous I / O method: First, asynchronous I / O is based on signaling mechanisms and is not reliable. Secondly, a single signal is not sufficient to provide more source of information. It is still necessary to assist in other means to achieve high difficulty. SELECT () Method: A method for blocking a multi-channel I / O in the BSD - SELECT (). It provides a method of blocking a plurality of I / O descriptors, using it, we can easily implement multiplexing. According to the unified UNIX specification, POSIX also uses this method, so we can use the Select method in most operating systems. Use a special I / O multiplexer: The method of constructing and using multiplexers in detail in the book "Unix® System V Programmer's Guide: Streams". This is no longer detailed here. We discuss two implementations of multiple I / O below: 1. Non-blocking communication methods have two files or devices specified for a file descriptor, there are two ways of working: blocking and non-blocking. The so-called blocked method means that when the file descriptor is read or written, if there is no thing readable, or if it is not writable, the program will enter the waiting state until there is something readable or writable. For non-blocking states, if there is no thing readable, or if you don't have to be written, the read and write function will return immediately without waiting. By default, the file descriptor is in a blocking state. When the chat room is implemented, Server needs to take the Socket established with each client. Once readable, the characters in the socket are read and sent to all other clients.
Also, Server also views if there is a new client attempt to establish a connection, so that if Server is blocking, the content sent by other clients will affect the server's timely response. The new client attempts to establish a connection will also be affected. So we can't use the default blocked file work, and we need to turn the files in a non-blocking method. Under UNIX, the function fcntl () can be used to change the mode of operation of the file I / O operation, the function describes the following: Fcntl (SOCKFD, F_SETFL, O_NONBLOCK); // sockfd is the file descriptor to change the status. // f_setfl indicates To change the status // o_nonblock representation of the file descriptor to change the file descriptor to non-blocking. In order to save space we use the natural language description chat room Server: While (1) {if there is a new connection life to establish and record the new connection; For (all valid connections) Begin if this connection is character readable by reading the BEGIN reading string; for (all other valid connections) Begin sends the string to the connection; end; end; end; end. Because of the END; End; end; Decisive whether there is a new connection, whether it is readable, so each judgment, no matter whether there is still no, it will immediately return. This way, any client sends characters to Server or trying to establish a new connection, will not for other clients. The activity has affected. For Client, after establishing a connection, only two file descriptors are required, one is a connected Socket descriptor and the other is the standard input. Like Server, if you use the blocked method, it is easy because of one temporary There is no input to affect another reading. Therefore, it will become non-blocking, then the client performs the following: while (do not want to exit) Begin if (connected to Server) Begin read from this connection And output to standard output. End; if (standard input readable) BeGin reads from the standard input, and outputs it to the server connection. End; end. Top read and write respectively call this two functions: read (Userfd [i], line, max_line); // userfd [i] refers to the file descriptor for the i-th Client connection. // Line means the location of the character stored in the read. // max_line is one most read. The number of characters. // The return value is the number of characters actually read. Write (Userfd [J], Line, Strlen (Line)); // Userfd [J] is the file descriptor of the JO JO Client. // Line It is the string to send. // Strlen (line) is the string length to send. Analyze the above program you can know, whether it is a server or a client, they keep inquiry each file descriptor, once readable Enterging and processes. Such procedures are not stopped, as long as there is a CPU resource, it will not let go. Therefore, the consumption of system resources is very large. When Server or Client is executed separately, 98% of the CPU resource is occupied. Very large consumption system resources. Solect method, although we don't want to block other users when there is no response to a user, but we should stop the operation of the program without any user reactions, let the preemptive system resources, enter the blocking state.
Is there such a way? The SELECT method is provided in the current UNIX system. The specific implementation is as follows: SELECT method, all file descriptors are blocked. Use SELECT to determine if there is a readable (write) in a set of file descriptors, if not Blocking until there is a time to be awaken. We first look at the implementation of a relatively simple Client: Since the client only needs to handle two file descriptors, it is necessary to judge whether there is a readable and writable file descriptor only needs to join two items. : Fd_zero (SOCKSET); // Clear the SockSet FD_SET (SOCKFD, SOCKSET); // Add SOCKFD to the SockSet Collection FD_SET (0, Sockset); // Add 0 (Standard Input) to the Sockset Collection and then Client Processing as follows: While (I don't want to exit) {SELECT (SockFD 1, & Sockset, Null, Null, Null); // This function will block until a readable selection / / first parameter is in the standard input or SOCKFD. The maximum value of 0 and SOCKFD plus one // second parameter is a read, that is, SOCKSET // Third, four parameters are written and unusual sets, which are empty in this program // fifth parameters It is timeout, that is, there is still not readable in the specified time, then error // and return. When this parameter is null, the timeout is set to infinitely long. // When SELECT is read, the SOCKSET is included. Just readable // Those file descriptors. If (fd_isset (sockfd, & sockset)) {// fd_isset This macro sockfd is read from the readable file descriptor to read from the SOCKFD, output to the standard output.} IF (FD_ISSET (0, & SOCKSET)) {// fd_isset This macro rigid SOCKFD is a readable file descriptor to read from the standard input, output to the SOCKFD.} Reset SockSet. (即 清 清 清, 0 Add)} The following is the case: set the SOCKSET as follows: fd_zero (SOCKSET); FD_SET (SOCKFD, SOCKSET); for (all valid connection) fd_set (userfd [i ], SOCKSET;} MAXFD = maximum file description symbol 1; Server processing is as follows: While (1) {SELECT (Maxfd, & Sockset, Null, Null, Null); if (fd_isset (sockfd, & sockset) {//// There is a new connection to establish a new connection and add the connection descriptor to the socked.} For (all valid connection) {if (fd_isset (userfd [i], & sockset) {// This connection is readable in this connection Read characters from this connection and send it to other active connections.}} Reset SockSet;} Performance Comparison Due to the SELECT mechanism, the program is in a blocking state, minimally occupying CPU resources When performing a server and several client on the same machine, the system load is only about 0.1, and the original non-blocking communication method is used, only one server is run, the system load can reach 1.5. So we recommend using SELECT. Reference Document: [1] UNIX NetWork Programming Volume 1 W.Richard Stevens 1998 Prentice Hall [2] Computer Practical Network Programming Tang Yijian 1993 People's Posts Publishing House [3] Unix? System V Release 4 Programmer's Guide: Streams AT &