[Computer Network] Interpretation of Network Programming Interface Socket API (4)

         Socket is an API exposed by the network protocol stack to programmers. Compared with complex computer network protocols, API abstracts key operations and configuration data, simplifying program programming.

        The socket content described in this article comes from Linux man . This article mainly introduces each API in detail to better understand socket programming.


listen

poll() complies with POSIX.1 - 2008

ppoll() follows Linux

1.Library

标准 c 库,libc, -lc

2.Header file

<sys/socket.h>

3.Interface definition

       int listen(int sockfd, int backlog);

4.Interface description

        listen() marks the socket specified by sockfd as a passive socket, which means that the socket receives incoming connection requests through accept().

        The sockfd parameter is a file descriptor pointing to a SOCK_STREAM or SOCK_SEQPACKET type.

        The backlog parameter defines the maximum length that can be queued on sockfd. If the queue is full when a connection request arrives, the client will receive an ECONNREFUSED error, or if the underlying protocol supports retransmission, the request will be ignored causing the client to End connection retries may succeed.

5.Return value

        On success, the return value is 0.

        When an error occurs, -1 is returned and errno is set to indicate the type of error.

        The error value is defined as follows:

EADDRINUS Another socket is already listening on the same port
EADDRINUS (Network domain socket) The socket pointed to by sockfd was not bound to an address. When trying to bind to a temporary port, the temporary port was exhausted.
EBADF sockfd is not a valid file descriptor
ENOTSOCK sockfd file descriptor does not point to a socket
EOPNOTSUPP The socket is not a socket that supports the listen() operation.

6.Attention

       In order to receive a connection, the following steps are required:

        (1) Create a socket through the socket() interface.

        (2) Bind the socket to the local address through bing(), so that other sockets can connect to it through connect.

        (3) According to your wishes, you can receive connections through the listen() interface and set the upper limit of the connection queue.

        Since Linux 2.2, the backlog parameter of the TCP socket has changed. It represents the length of the queue that has established connections waiting to be received (accept), rather than the length of the queue of uncompleted connections. The upper limit on the length of the outstanding connection queue can   be set via /proc/sys/net/ipv4/tcp_max_syn_backlog . When the synchronous cookie function is turned on, this setting will be ignored, that is, there is no local maximum length limit. See tcp(7) for more information.

        If the backlog is larger than /proc/sys/net/core/somaxconn, then this value is copied to somaxconn by default. After Linux 5.4, this default value is 4096, while in earlier versions, the default value was 128. Before Linux 2.4.25, this value was hard-coded to 128 and cannot be changed.

accept

accept() complies with POSIX.1 - 2008

accept4() follows Linux

1.Library

标准 c 库,libc, -lc

2.Header file

<sys/socket.h>

3.Interface definition

       int accept(int sockfd, struct sockaddr *_Nullable restrict addr,
                  socklen_t *_Nullable restrict addrlen);

       int accept4(int sockfd, struct sockaddr *_Nullable restrict addr,
                  socklen_t *_Nullable restrict addrlen, int flags);

4.Interface description

       The accept() system call is used on connection-based sockets (SOCK_STREAM, SOCK_SEQPACKET). It will take out the first connection request from the waiting connection queue of the listening socket (sockfd), then create a new connected socket, and return a new file descriptor pointing to the socket. The newly created socket is not in the listening state, and the original socket (sockfd) is not affected by this call.

        The sockfd parameter is a socket created through the socket() interface, bound to the local address through bind(), and listen() is used to monitor its connection.

        The addr parameter is a pointer to a sockaddr structure, which is populated by the communication layer with the peer socket address. The format of the returned addr address depends on the specific address family (refer to the socket() and related protocol man pages). When addr is NULL, nothing is filled in. In this case addrlen is also useless and should be NULL.

        The addrlen parameter is an input and output parameter. The caller must initialize it with the size of the addr structure and return the actual size of the peer address.

        If the provided buffer is too small, the returned address will be truncated. In this case, addrlen will return a value greater than the supplied value.

        If there are no connections waiting on the current queue and the socket is not set to non-blocking, accept() will block until a connection arrives. And if the socket is set to non-blocking, accept() will report an EAGAIN or EWOULDBLOCK error code.

        In order to get notification of new connections, we can use the select(), poll(), and epoll interfaces. When a new connection attempt occurs, we will receive a readable event, and then we can call accept() to obtain the socket of the corresponding connection.

        You can also set up the SIGIO signal to be sent when there is movement on the socket, refer to socket(7).

        If flags is 0, then accept4() is equivalent to accept(). flags can be the bitwise OR of the following flags:

        SOCK_NONBLOCK

        Set the file status flag of the new file descriptor to O_NONBLOCK so that you no longer need to call fcntl() to achieve the same effect.

        SOCK_CLOEXEC

        Set the FD_CLOEXEC flag of the new file descriptor. You can view the open(2) description to see the meaning of this flag.

5.Return value

        On success, this system call returns a file descriptor (non-negative integer) for the receiving socket.

        When an error occurs, -1 is returned, and errno is set to indicate the error type, and addrlen is not changed.

        Linux's accept() and accept4() interfaces will pass existing network errors to newly created sockets. This behavior is different from the BSD socket implementation. In order to achieve reliable operation, we need to handle network errors for the corresponding protocol and treat them as EAGAIN retries. In the TCP/IP scenario, there will be network errors such as ENETDOWN/EPROTO/ENOPROTOOPT/EHOSTDOWN/ENONET/EHOSTUNREACH/EOPNOTSUPP/ENETUNREACH.

        The error value is defined as follows:

EAGAIN/EWOULDBLOCK The socket is set to non-blocking, and there are currently no connections waiting to be received. Both POSIX.1-2001 and POSIX.1-2008 allow any error code to be returned, and do not require the two values ​​to be the same, so porters should handle each.
EBADF sockfd is not an open file descriptor
ECONNABORTED The connection has been terminated
EFAULT The addr parameter is not a writable address in the user address space
EINTR The system call was interrupted by a signal before a valid connection arrived.
SINGLE CHOICE The socket is not in the listening connection state, or the addrlen is invalid.
SINGLE CHOICE (accept4()) The value of flags is illegal
DEAD The number of file descriptors reaches the process maximum limit
PUT ON The number of system file descriptors reaches the system maximum limit
ENOBUFS/ENOMEM Not enough memory. This usually does not mean system memory, but that memory allocation is limited by the socket buffer and cannot be allocated.
ENOTSOCK sockfd file descriptor is not a socket
EOPNOTSUPP socket is not of type SOCK_STREAM
Upper Firewall rules prohibit connection
EPROTO protocol error

        In addition, network errors for the new socket protocol will also be returned, and the Linux kernel may also return some other errors: ENOSR/ESOCKTNOSUPPORT/EPROTONOSUPPORT/ETIMEDOUT. ERESTARTSYS may also be returned during trace.

On Linux, the socket newly returned by accept() will not integrate file status flags from the listening socket, such as O_NONBLOCK and O_ASYNC. This behavior is different from the BSD implementation. A portable program should not make assumptions about these and set these flags explicitly.

6.Attention

       After we receive the SIGIO signal or select()/poll/epoll returns a readable event, there may not really be a connection, because it is likely that the connection was blocked by an asynchronous network error or other threads before accept() was called. Remove. Once this condition is sent, the system call blocks until the next connection arrives. In order to ensure that accept() never blocks, the socket specified by sockfd must have the O_NONBLOCK flag set.

        For some protocols that require explicit confirmation, such as DECnet, accept() just takes the next connection request out of the queue without confirming it. Confirmation is done by reading or writing the file descriptor. Currently only DECnet has similar semantics on Linux.

        socklen_t type

        In the original BSD implementation, the third parameter of accept() was declared as int *. The POSIX.1g draft standard wanted to change it to size_t *C, and later the POSIX standard and glibc 2.x defined it as socklen_t *.

7.Code

       #include <stdio.h>
       #include <stdlib.h>
       #include <string.h>
       #include <sys/socket.h>
       #include <sys/un.h>
       #include <unistd.h>

       #define MY_SOCK_PATH "/somepath"
       #define LISTEN_BACKLOG 50

       #define handle_error(msg) \
           do { perror(msg); exit(EXIT_FAILURE); } while (0)

       int
       main(void)
       {
           int                 sfd, cfd;
           socklen_t           peer_addr_size;
           struct sockaddr_un  my_addr, peer_addr;

           sfd = socket(AF_UNIX, SOCK_STREAM, 0);
           if (sfd == -1)
               handle_error("socket");

           memset(&my_addr, 0, sizeof(my_addr));
           my_addr.sun_family = AF_UNIX;
           strncpy(my_addr.sun_path, MY_SOCK_PATH,
                   sizeof(my_addr.sun_path) - 1);

           if (bind(sfd, (struct sockaddr *) &my_addr,
                    sizeof(my_addr)) == -1)
               handle_error("bind");

           if (listen(sfd, LISTEN_BACKLOG) == -1)
               handle_error("listen");

           /* Now we can accept incoming connections one
              at a time using accept(2). */

           peer_addr_size = sizeof(peer_addr);
           cfd = accept(sfd, (struct sockaddr *) &peer_addr,
                        &peer_addr_size);
           if (cfd == -1)
               handle_error("accept");

           /* Code to deal with incoming connection(s)... */

           if (close(sfd) == -1)
               handle_error("close");

           if (unlink(MY_SOCK_PATH) == -1)
               handle_error("unlink");
       }

Next article [Computer Network] Interpretation of Network Programming Interface Socket API (5 )

Guess you like

Origin blog.csdn.net/BillyThe/article/details/132780158