Set Interface Options
In the previous chapters, we discussed the basic content of using the set interface. Now we will discuss some other available features. After we have mastered the concept of this chapter, we have prepared for the advanced topic of the next set of interfaces. In this chapter, we will focus on the following topics:
How to Use the getsockopt (2) function to obtain the set interface option value
How to Use setsockopt (2) function to set the set interface option value
How to use these common set of Interface Options
Obtain the set interface option.
Sometimes, a program needs to determine which options are currently set for a set of interfaces. This is especially true for a sub-library function, because this library function does not know the settings for this set of interfaces, and this set of interfaces needs to be passed as a parameter. The program may need to know the size of the buffer used by the stream by default.
Let's get the getsockopt function with the set interface option value. This function is summarized as follows:
# Include <sys/types. h>
# Include <sys/socket. h>
Int getsockopt (INT s,
Int level,
Int optname,
Void * optval,
Socklen_t * optlen );
Function parameters are described as follows:
1. Set interface s for Option Test
2 Option check protocol layer level
3. optname
4. pointer optval pointing to the buffer for receiving option values
5. the pointer optlen points to the length of the input buffer and the returned Option Length value.
If the function is successful, 0 is returned. -1 is returned when an error occurs, and the cause of the error is stored in the external variable errno.
The protocol layer parameter specifies the protocol stack where an option is to be accessed. We usually need to use one of the following:
Sol_socket to access the interface layer options
Sol_tcp to access TCP layer options
In this chapter, we will focus on the use of sol_socket layer options.
The optname parameter is an integer. The value used here is first determined by the selected level parameter. On a specified protocol layer, the optname parameter determines which option we want to access. The following table lists the combined values of some layers and options:
Protocol layer option name
Sol_socket so_reuseaddr
Sol_socket so_kkepalive
Sol_socket so_linger
Sol_socket so_broadcast
Sol_socket so_oobinline
Sol_socket so_sndbuf
Sol_socket so_rcvbuf
Sol_socket so_type
Sol_socket so_error
Sol_tcp so_nodelay
Most of the options listed in the above table are set Interface Options, where the layer is specified by sol_socket. For the purpose of comparison, a TCP layer set interface option is included, where the layer is specified by sol_tcp.
Most set of Interface Options are stored in the int data type after they are obtained. When you look at the manual page, the Data Type int usually has some assumptions, unless it indicates something else. When a Boolean value is used, if the value is not zero, int indicates true, and if it is zero, false.
Apply getsockopt (2)
In this section, we will compile and run a getsndrcv. C program, which will obtain and report the size and size of a set of interfaces for sending and receiving buffers.
# Include <stdio. h>
# Include <stdlib. h>
# Include <string. h>
# Include <unistd. h>
# Include <errno. h>
# Include <sys/types. h>
# Include <sys/socket. h>
# Include <assert. h>
Static void bail (const char * on_what)
{
If (errno! = 0)
{
Fputs (strerror (errno), stderr );
Fputs (":", stderr );
}
Fputs (on_what, stderr );
Fputc ('/N', stderr );
Exit (1 );
}
Int main (INT argc, char ** argv)
{
Int Z;
Int S =-1;
Int sndbuf = 0;
Int rcvbuf = 0;
Socklen_t optlen;
S = socket (pf_inet, sock_stream, 0 );
If (S =-1)
Bail ("socket (2 )");
Optlen = sizeof sndbuf;
Z = getsockopt (S, sol_socket, so_sndbuf, & sndbuf, & optlen );
If (z)
Bail ("getsockopt (S, sol_socket ,"
"So_sndbuf )");
Assert (optlen = sizeof sndbuf );
Optlen = sizeof rcvbuf;
Z = getsockopt (S, sol_socket, so_rcvbuf, & rcvbuf, & optlen );
If (z)
Bail ("getsockopt (S, sol_socket ,"
"So_rcvbuf )");
Assert (optlen = sizeof rcvbuf );
Printf ("socket S: % d/N", S );
Printf ("Send Buf: % d Bytes/N", sndbuf );
Printf ("Recv Buf: % d Bytes/N", rcvbuf );
Close (s );
Return 0;
}
The program running result is as follows:
$./Getsndrcv
Socket S: 3
Send Buf: 65535 bytes
Recv Buf: 65535 bytes
Set set Interface Options
If we think that the size of the default sending and receiving buffer of the Set interface is too large, we can design it as a small buffer. This is especially important when several instances of a program run on our system at the same time.
You can use the setsockopt (2) function to design the set Interface Options. This function is summarized as follows:
# Include <sys/types. h>
# Include <sys/socket. h>
Int setsockopt (INT s,
Int level,
Int optname,
Const void * optval,
Socklen_t optlen );
This function is similar to the getsockopt function discussed above. The parameters of the setsockopt function are described as follows:
1 option to change the set interface s to be affected
2 Option Set Interface Level
3. optname
4 pointer to the value to be set for the new option optval
5. optlen
The difference between this function parameter and the above getsockopt function parameter lies in that the last parameter is only passing the parameter value. In this case, it is only an input value.
Apply the setsockopt Function
The following sample code changes the size of the sending and receiving buffers for a set of interfaces. After these options are set, the program will obtain and report the actual buffer size.
# Include <stdio. h>
# Include <stdlib. h>
# Include <unistd. h>
# Include <string. h>
# Include <errno. h>
# Include <sys/types. h>
# Include <sys/socket. h>
# Include <assert. h>
Static void bail (const char * on_what)
{
If (errno! = 0)
{
Fputs (strerror (errno), stderr );
Fputs (":", stderr );
}
Fputs (on_what, stderr );
Fputc ('/N', stderr );
Exit (1 );
}
Int main (INT argc, char ** argv)
{
Int Z;
Int S =-1;
Int sndbuf = 0;
Int rcvbuf = 0;
Socklen_t optlen;
S = socket (pf_inet, sock_stream, 0 );
If (S =-1)
Bail ("socket (2 )");
Sndbuf = 5000;
Z = setsockopt (S, sol_socket, so_sndbuf, & sndbuf, sizeof sndbuf );
If (z)
Bail ("setsockopt (S, sol_socket ,"
"So_sndbuf )");
Rcvbuf = 8192;
Z = setsockopt (S, sol_socket, so_rcvbuf, & rcvbuf, sizeof rcvbuf );
If (z)
Bail ("setsockopt (S, sol_socket ,"
"So_rcvbuf )");
Optlen = sizeof sndbuf;
Z = getsockopt (S, sol_socket, so_sndbuf, & sndbuf, & optlen );
If (z)
Bail ("getsockopt (S, sol_socket ,"
"So_sndbuf )");
Assert (optlen = sizeof sndbuf );
Optlen = sizeof rcvbuf;
Z = getsockopt (S, sol_socket, so_rcvbuf, & rcvbuf, & optlen );
If (z)
Bail ("getsockopt (S, sol_socket"
"So_rcvbuf )");
Assert (optlen = sizeof rcvbuf );
Printf ("socket S: % d/N", S );
Printf ("Send Buf: % d Bytes/N", sndbuf );
Printf ("Recv Buf: % d Bytes/N", rcvbuf );
Close (s );
Return 0;
}
The program running result is as follows:
$./Setsndrcv
Socket S: 3
Send Buf: 10000 bytes
Recv Buf: 16384 bytes
$
In
Here we should pay attention to the results reported by the program. They seem to be twice the specified original size. This reason can be viewed by the Linux kernel source code module net/CORE/sock. C.
To. We can check the case statements of so_sndbuf and so_rcvbuf. The following section is extracted from the kernel module sock. C to process so_sndbuf
Code:
398 case so_sndbuf:
399
403
404 if (Val> sysctl_wmem_max)
405 val = sysctl_wmem_max;
406 set_sndbuf:
407 SK-> sk_userlocks | = sock_sndbuf_lock;
408 if (Val * 2) <sock_min_sndbuf)
409 SK-> sk_sndbuf = sock_min_sndbuf;
410 else
411 SK-> sk_sndbuf = Val * 2;
412
413
417 SK-> sk_write_space (SK );
418 break;
From this code, we can see what actually happened on so_sndbuf:
1. Check the so_sndbuf option value to determine whether it has exceeded the maximum value of the buffer.
2. If the so_sndbuf option value in step 1 does not exceed the maximum value, use this maximum value without returning the error code to the caller.
3 if the so_sndbuf option value is twice smaller than the minimum value of the Set interface so_sndbuf, the actual so_sndbuf option is set to the minimum value of so_sndbuf, otherwise, the so_sndbuf option value is set to twice the so_sndbuf option value.
From this we can see that the so_sndbuf option value is only a prompt value. The kernel will eventually determine the best value for so_sndbuf.
View More kernel source code. We can see that similar situations also apply to the so_rcvbuf option. The following is an excerpt from the code:
427 case so_rcvbuf:
428
432
433 if (Val> sysctl_rmem_max)
434 val = sysctl_rmem_max;
435 set_rcvbuf:
436 SK-> sk_userlocks | = sock_rcvbuf_lock;
437
452 if (Val * 2) <sock_min_rcvbuf)
453 SK-> sk_rcvbuf = sock_min_rcvbuf;
454 else
455 SK-> sk_rcvbuf = Val * 2;
456 break;
Obtain the set of Interface Types
In fact, we can only get some set Interface Options. So_type is an example. This option allows you to pass a subfunction of the set interface to determine which type of interface is being processed.
The following is a sample code for getting the set interface s:
# Include <stdio. h>
# Include <stdlib. h>
# Include <unistd. h>
# Include <string. h>
# Include <errno. h>
# Include <sys/types. h>
# Include <sys/socket. h>
# Include <assert. h>
Static void bail (const char * on_what)
{
If (errno! = 0)
{
Fputs (strerror (errno), stderr );
Fputs (":", stderr );
}
Fputs (on_what, stderr );
Fputc ('/N', stderr );
Exit (1 );
}
Int main (INT argc, char ** argv)
{
Int Z;
Int S =-1;
Int so_type =-1;
Socklen_t optlen;
S = socket (pf_inet, sock_stream, 0 );
If (S =-1)
Bail ("socket (2 )");
Optlen = sizeof so_type;
Z = getsockopt (S, sol_socket, so_type, & so_type, & optlen );
If (z)
Bail ("getsockopt (S, sol_socket ,"
"So_type )");
Assert (optlen = sizeof so_type );
Printf ("socket S: % d/N", S );
Printf ("so_type: % d/N", so_type );
Printf ("so_stream = % d/N", sock_stream );
Close (s );
Return 0;
}
The program running result is as follows:
$./GetType
Socket S: 3
So_type: 1
So_stream = 1
Set so_reuseaddr
In Chapter 11th, the first part of "concurrent client server" provides and tests a server designed to use the fork system call. Figure 12.1 shows the three steps after a Telnet command establishes a connection with the server.
These steps are as follows:
1. Start the server process (PID 926 ). He listens to the client connection.
2. Start the client process (Telnet command) and connect to the server process (PID 926 ).
3. Call fork to create a server sub-process. This will retain the original parent process (PID 926) and create a new sub-process (PID 927 ).
4. The client interface set for connection is disabled by the server parent process (PID 926). Only the client interface set for connection is enabled in the child process (PID 927.
5 Telnet commands interact freely with the server sub-process (PID 927), independent of the parent process (PID 926 ).
In step 5, there are two set of interface activities:
Server (PID 926) listens to 192.168.0.1: 9099
The client uses the set interface 192.168.0.1: 9099 for service (PID 927). It connects to the client address 192.168.0.2: 1035.
The client is served by process ID 927. This means that the process ID 926 can be killed, and the client can continue to be served. However, there will be no new connections to the server, because there is no server listening for new connections (listening server PID 926 has been killed)
Now
If we restart the server to listen for new connections, a problem may occur. When a new server process tries to bind the IP address 192.168.0.1: 9099, the BIND function returns
The error code of eaddrinuse. This error code indicates that the IP address is already used on port 9099. This is because the process PID
927 is still busy serving a client. The IP address 192.168.0.1: 9099 is still used by this process.
The solution to this problem is to kill process 927, which closes the set interface and releases the IP address and port. However, if the customer being served is the CEO of our company, this does not seem to be an option. At the same time, other departments complain about why we need to restart the server.
A good solution to this problem is to use the so_reuseaddr interface option. All servers should use this option unless there is a better reason for not using it. To effectively use this option, we should perform the following operations in the server to which the listener is connected:
1. Use a common socket function to create a listener Interface
2. Call the setsockopt function to set so_reuseaddr to true.
3. Call the BIND Function
The set of APIs are now marked as reusable. If the listening server process is terminated for any reason, we can restart the server. This is especially true when a customer uses the same IP address and port number to serve another server process.
To effectively use the so_reuseaddr option, consider the following:
In listener mode, there are no other interfaces with the same IP address and port number.
The so_reuseaddr option must be set to true for all interfaces with the same IP address and port number.
This means that only one listener can be used for a specified IP address and port number. If such an interface already exists, setting this option will not achieve our goal.
This option is valid only when so_reuseaddr is set to true. If this option is not set for the existing interface, the BIND function will continue and return an error number.
The following code shows how to set this option to true:
# Define true 1
# Define false 0
Int Z;
Int S;
Int so_reuseaddr = true;
Z = setsockopt (S, sol_socket, so_reuseaddr,
& So_reuseaddr,
Sizeof so_reuseaddr );
If you need the so_reuseaddr option, you can query it by the getsockopt function.
Set so_linger options
Another common option is the so_linger option. Different from the so_reuseaddr option, the data type used by this option is not a simple int type.
The purpose of the so_linger option is to control how the interface is closed when the close function is called. This option applies only to connection-oriented protocols, such as TCP.
The default behavior of the kernel is to allow the close function to return immediately to the caller. If any unsent TCP/IP data is transmitted, this is not guaranteed. Because the close function immediately returns control to the caller, the program cannot know whether the last data is sent.
The so_linger option can be used on the set interface to cause the program to block the close function call until all the final data is transmitted to the remote end. In addition, this ensures that the call at both ends knows that the interface is normally closed. If the call fails, the specified option times out and an error is returned to the caller.
You can use different so_linger option values to apply a final scenario. If the caller wishes to stop the communication immediately, you can set a proper value in the linger structure. Then, a call to close will initiate a communication to stop the connection, discard all unsent data, and immediately close the set of interfaces.
This operation mode of so_linger is controlled by the linger structure:
Struct linger {
Int l_onoff;
Int l_linger;
};
Rochelle off is a Boolean value. A non-zero value indicates true, while a zero value indicates false. The three values of this option are described as follows:
1. If l_onoff is set to false, the member l_linger is ignored and the default close behavior is used. That is to say, the close call will be immediately returned to the caller, and if possible, any unsent data will be transmitted.
2
Setting l_onoff to true makes the value of member l_linger important. When l_linger is not zero, this indicates that the application times out in seconds on the close function call.
Limits. If unsent data exists before the timeout occurs and is disabled successfully, the function returns a success response. Otherwise, an error is returned, and the variable errno is set to ewouldblock.
3. If l_onoff is set to true and l_linger is set to zero, the connection is aborted. When close is called, any data sent is discarded.
Me
We may want some suggestions, use the so_linger option in our program, and provide a reasonable timeout period. Then, we can check the return value of the close function to determine whether the connection is
No. If an error is returned, the remote program may not be able to receive all the data sent by us. Relatively, it may only mean that the connection is closed.
However
However, we must stay awake. This method will lead to new problems in some server design. When the so_linger option is configured as a timeout (linger) in the close function call
When the close function call times out, our server stops other clients from providing services. This problem occurs if we are serving multiple client processes in a process. Use the default row
It may be more appropriate because this allows the close function to return immediately. Any unsent data will be sent to the kernel.
Finally, if the program or server knows when the connection should be terminated, the stop action can be used. This may apply when the server considers that the user is not authorized to access the server and is trying to access the server. In this case, customers will not receive special attention.
The following code uses the so_linger option as an example, with a 30-second timeout period:
# Define true 1
# Define false 0
Int Z; int S;
Struct linger so_linger;
...
So_linger.l_onoff = true;
So_linger.l_linger = 30;
Z = setsockopt (S,
Sol_socket,
So_linger,
& So_linger,
Sizeof so_linger );
If (z)
Perror ("setsockopt (2 )");
The following example shows how to set the value of so_linger to stop the current connection on interface s:
# Define true 1
# Define false 0
Int Z;
Int S;
Struct linger so_linger;
...
So_linger.l_onoff = true;
So_linger.l_linger = 0;
Z = setsockopt (S,
Sol_socket,
So_linger,
& So_linger,
Sizeof so_linger );
If (z)
Perror ("setsockopt (2 )");
Close (s );
In the preceding example, when the close function is called, the set interface S is immediately suspended. The meaning of abort is achieved by setting the timeout value to 0.
Set so_kkepalive
When using the connection, sometimes they will be idle for a long time. For example, create a telnet session to access the stock trading service. He may execute some initial queries and leave the connection to keep the service open, because he wants to return to query more content. However, the idle state of simultaneous connection processing may be one hour at a time.
Ren
Which server will assign resources to a connected customer. If the server is a derived type (fork), the entire Linux Process and its corresponding memory are allocated to this
Customer. If everything goes well, this scenario will not cause any problems. However, when the network crashes, the difficulty arises. All of our 578 customers will lose connection from our stock trading service.
After the network service is restored, 578 customers will try to connect to our server and rebuild the connection. This is a real problem for us, because our server did not realize that it had lost its idle client-so_kkepalive to solve this problem.
The following example shows how to use the so_kkepalive option on the set interface s, so that a disconnected idle connection can be detected:
# Define true 1
# Define false 0
Int Z; int S;
Int so_keepalive;
...
So_keepalive = true;
Z = setsockopt (S,
Sol_socket,
So_keepalive,
& So_keepalive,
Sizeof so_keepalive );
If (z)
Perror ("setsockopt (2 )");
In the above example, the so_keepalive option is set, so that when the set of interface connection is idle for a long time, a probe message will be sent to the remote end. This is usually completed after two hours of no activity. There are three possible responses to the detection information of a persistence activity. They are:
The first end will return an appropriate response to indicate that everything is normal. No indication is returned to the program because this is the beginning of the program's assumption.
The 2-end response indicates that he has no idea about the connection. This indicates that the client has reconnected to the host since the last communication. In this way, the econnreset error code will be returned to the program in the next interface operation.
The third end has no response. In this case, the kernel may have made several attempts to connect. If the request is not responded, TCP usually gives up in about 11 minutes. In this case, the etimeout error is returned in the next interface operation. Other errors, such as ehostunreach, are returned when the network no longer reaches the host.
So_keepalive
The called time framework limits its common use. The probe information is sent only after about two hours of inactivity. Then, when there is no response, it will take another 11 minutes for the connection to return an error. Regardless
This option does allow detecting idle connectionless interfaces, and then the server closes them. Correspondingly, servers supporting long idle connections should allow this feature.
Set so_broadcast options
We have not discussed topics that use UDP for broadcast. However, we are easily aware of the misuse of broadcast functions and the resulting network disasters. To avoid broadcasting when no broadcast is scheduled, the broadcast function is disabled for the set interface. If broadcast is required, the C programmer must handle the corresponding troubles for this function of the Set interface.
So_broadcast is a Boolean flag that is set by the int data type. The following example shows how to set the so_broadcast option:
# Define true 1
# Define false 0
Int Z;
Int S;
Int so_broadcast;
...
So_broadcast = true;
Z = setsockopt (S,
Sol_socket,
So_broadcast,
& So_broadcast,
Sizeof so_broadcast );
If (z)
Perror ("setsockopt (2 )");
If you want the setsockopt function to return zero, set interface s to allow broadcasting. However, it is important to note that the selected set of interface types must have broadcast functions, such as UDP sets of interfaces.
Set so_oobinline
In some cases, the amount of data sent may exceed the limit. Generally, these out-of-bounds data are received by different data receiving functions. However, sometimes they prefer to use the usual method to receive the cross-border data. When this method is selected, the out-of-boundary data arrives before the normal data as part of the normal data flow.
To use this feature, we can use the following code:
# Define true 1
# Define false 0
Int Z;
Int S;
Int so_oobinline;
...
So_oobinline = true;
Z = setsockopt (S,
Sol_socket,
So_oobinline,
& So_oobinline,
Sizeof so_oobinline );
If (z)
Perror ("setsockopt (2 )");
After the so_oobinline option is set, the out-of-bounds data will be received together with the normal data. In this way, the received cross-border data is the same as the normal data.
So_passcred and so_peercred options
These options are only applicable to the pf_unix (pf_local) interface. These options are used to control and pass creden。 on the local interface of the current host. This may be the most difficult topic to grasp. For now, we just need to note that we may be interested in these two options if we want to write service programs that serve local host customers.