Blocking I/O programming has two features:
1. If one finds that I \ O has input, and the other has input during reading, no response will be generated at this time. this requires your program statements to use the select function to know that data is input.
2. When the program goes to select, if there is no data input, the program will wait until there is a data location, that is, there is no loop or sleep in the program.
Select is important in socket programming, but for beginners of socket, they do not like to use select to write programs. They are just used to writing blocking programs such as connect, accept, Recv, or recvfrom.
(The so-called blocking method block, as its name implies, means that when a process or thread executes these functions, it must wait for an event to occur. If the event does not occur, the process or thread will be blocked, the function cannot be returned immediately ).
However, using select can achieve non-blocking (the so-called non-block method means that the process or thread does not have to wait for the event to execute this function. Once executed, it will definitely return, different return values are used to reflect the execution of the function. If the event occurs in the same way as the blocking method, if the event does not occur, a code is returned to inform the event that the event has not occurred, while the process or thread continues to execute, so it is more efficient). It can monitor changes in file descriptors that we need to monitor-read/write or exceptions.
The following example of the select function in man:
/* According to POSIX.1-2001 */
# Include <sys/select. h>
/* According to earlier standards */
# Include <sys/time. h>
# Include <sys/types. h>
# Include <unistd. h>
Int select (INT NFDs, fd_set * readfds, fd_set * writefds,
Fd_set * exceptfds, structtimeval * timeout );
First, struct fd_set can be understood as a collection in which the file descriptor (filedescriptor) is stored, that is, the file handle. This can be what we call a common object, of course, any device, pipeline, and FIFO in Linux are all in the file format, so there is no doubt that a socket is a file, and a socket handle is a file descriptor.
The fd_set set can be operated manually by some macros,
For example
Clear the collection fd_zero (fd_set *);
Add a given file descriptor to the set fd_set (INT, fd_set *);
Deletes a given file descriptor from the collection fd_clr (INT, fd_set *);
Check whether the specified file descriptor in the set can read and write fd_isset (INT, fd_set *).
Second, structtimeval is a common structure used to represent the time value. It has two members, one being the number of seconds and the other being the number of milliseconds.
Specific interpretation of select parameters:
Int maxfdp is an integer that indicates the range of all file descriptors in the collection. That is, the maximum value of all file descriptors is 1. It cannot be wrong!
Fd_set * readfds is a pointer to the fd_set structure. This collection should contain file descriptors. We want to monitor the read changes of these file descriptors, that is, we are concerned about whether data can be read from these files. If a file in this collection is readable, select returns a value greater than 0, indicating that the file is readable, if there is no readable file, the timeout parameter is used to determine whether to time out. If the time exceeds the timeout time, select returns 0. If an error occurs, a negative value is returned. You can pass in a null value, indicating that you do not care about any file read changes.
Fd_set * writefds is a pointer to the fd_set structure. This collection should contain file descriptors. We want to monitor the write changes of these file descriptors, that is, we are concerned about whether data can be written to these files. If there is a file in this collection that can be written, select will return a value greater than 0, indicating that a file is writable, if no writable file exists, the timeout parameter is used to determine whether to time out. If the time exceeds the timeout time, select returns 0. If an error occurs, a negative value is returned. You can pass in a null value, indicating that you do not care about any file write changes.
Fd_set * errorfds indicates the intention of the two parameters above, which is used to monitor file error exceptions.
Struct timeval * timeout is the Select time-out time. This parameter is crucial and can make the SELECT statement in three states,
First, if null is input as a form parameter, that is, if the time structure is not input, the SELECT statement is placed in a blocking state and must wait until a file descriptor in the monitored file descriptor set changes;
Second, second, if the time value is set to 0 s and 0 ms, it becomes a pure non-blocking function. no matter whether the file descriptor changes or not, it immediately returns and continues execution, if the file is not changed, 0 is returned. If the file is changed, a positive value is returned;
Third, the value of timeout is greater than 0, which is the wait timeout time. That is, the SELECT statement is blocked within the timeout time. If an event arrives within the time-out period, the return value is 0, A positive value is returned when there is a change;
Return Value:
Negative value: Select Error
Positive Value: some files can be read or written, or errors occur.
0: wait for timeout. No file can be read/written or error
A simple example:
# Include <sys/time. h>
# Include <sys/types. h>
# Include <unistd. h>
# Include <sys/select. h>
# Include <stdio. h>
# Include <stdlib. h>
Int main ()
{
Fd_setreadfds;
Fd_zero (& readfds );
Fd_set (stdin_fileno, & readfds );
Intret;
Charbuf [256] = {0 };
Structtimeval TV = {5, 1000 };
// TV. TV _sec = 5;
// TVs. TV _usec = 1000;
Ret = select (stdin_fileno + 1, & readfds, null, null, & TV );
// Ret = select (stdin_fileno + 1, & readfds, null );
// Ret = select (stdin_fileno + 1, & readfds, null, null, 0 );
Printf ("ret = % d \ n", RET );
If (ret =-1)
{
Perror ("selsecterror ");
Exit (exit_failure );
}
Elseif (RET)
{
If (fd_isset (stdin_fileno, & readfds ))
Read (stdin_fileno, Buf, 256 );
Printf ("readfrom stdin MSG: % s \ n", Buf );
}
Else
Printf ("timeout \ n ");
Return0;
}
Running result: 3 in the running status:
1. Error.
2. Timeout. user input is not detected within the specified time. Selsect () returns 0
3. the user input indicates that the standard input is ready.
Next, let's take a look at the use of the select () function in socket programming.
Paste the select function section here.
Client:
While (1)
{
Fd_zero (& readfds );
Fd_set (sockfd, & readfds );
Fd_set (stdin_fileno, & readfds );
Maxfd = sockfd;
Printf ("before Select % d \ n", readfds );
If (ret = select (maxfd + 1, & readfds, null) =-1)
{
Perror ("select :");
Exit (exit_failure );
}
// When the file descriptor is ready, set the corresponding bit to 1, and set the other bit to 0.
// Eg: When socket = 3 is ready, readfds is 00000100 (8)
// The Return Value of the select function is the number of prepared file descriptors.
Debug ("ret = % d \ n", RET );
Printf ("After Select % d \ n", readfds );
If (fd_isset (sockfd, & readfds ))
{Printf ("sockfd select % d \ n", readfds );
If (read (sockfd, read_buf, sizeof (read_buf) <= 0)
{
Perror ("read <= 0 ");
Break;
}
Printf ("% s \ n", read_buf );
Memset (read_buf, 0, sizeof (read_buf ));
}
If (fd_isset (stdin_fileno, & readfds) // standard input descriptor is ready
{Printf ("stdin_fileno select % d \ n", readfds );
Read (stdin_fileno, Buf, sizeof (BUF ));
// Or scanf ("% s", Buf); // extract the content from the buffer of the standard input device and put it in the Buf.
Write (sockfd, Buf, strlen (BUF) + 1 );
}
}
Server:
Void server_write_read (INT sockfd)
{
Fd_set rfds;
Fd_zero (& rfds );
Fd_set (sockfd, & rfds );
Int I, maxfd = sockfd, ret =-1;
Int newfd;
Struct sockaddr_in client;
Socklen_t Len = sizeof (client );
Char Buf [maxbuf] = {'\ 0 '};
While (1)
{
Fd_zero (& rfds); // clear set
For (I = 0; I <maxclient; I ++)
{
// Add FD to set
If (connfd [I]. FD! = 0)
{
Fd_set (connfd [I]. FD, & rfds );
}
// Get tje Max FD
If (connfd [I]. FD! = 0 & connfd [I]. FD> maxfd)
{
Maxfd = connfd [I]. FD;
}
}
Fd_set (sockfd, & rfds );
Printf ("server_write_read before Select % d \ n", rfds );
If (ret = select (maxfd + 1, & rfds, null) =-1)
{
Perror ("select :");
Continue;
}
Printf ("server_write_read After Select % d \ n", rfds );
If (fd_isset (sockfd, & rfds ))
{Printf ("server_write_read sockfd % d \ n", rfds );
Newfd = accept (sockfd, (struct sockaddr *) & client, & Len );
If (-1 = newfd)
System_error ("accept ");
Else
Printf ("Welcome % s was connected. \ n", inet_ntoa (client. sin_addr ));
Debug ("newfd = % d \ n", newfd );
// Save socket description
For (I = 0; I <maxclient; I ++)
{
If (connfd [I]. FD = 0)
{
Connfd [I]. FD = newfd;
Break;
}
}
}
For (I = 0; I <maxclient; I ++)
{
If (fd_isset (connfd [I]. FD, & rfds ))
{
If (connfd [I]. FD = 0) continue;
If (read (connfd [I]. FD, Buf, maxbuf) <= 0)
{Debug ("server read <= 0 \ n ");
For (I = 0; I <maxclient; I ++)
{
If (connfd [I]. FD = newfd)
{
Connfd [I]. FD = 0;
Break;
}
}
Close (newfd );
Fd_clr (newfd, & rfds );
}
Else
{
Int J = 0;
Printf ("// Buf = % s \ n", Buf );
For (; chat_func [J]. func; j ++)
{
If (BUF [0] = chat_func [J]. Protocol)
{
Chat_func [J]. func (BUF, connfd [I]. FD );
Break;
}
}
Memset (BUF, 0, maxbuf );
}
}
}
}
}