C语言socket accept()函数(提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符)
文章目錄
- 名稱
- 使用格式
- 功能參數描述
- 參數
- sockfd
- addr
- addrlen
- 返回值
- 示例
- man 2 文檔中的accept解釋
- 錯誤處理
名稱
accept()接收一個套接字中已建立的連接
使用格式
#include <sys/types.h>#include <sys/socket.h>int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);功能參數描述
accept()系統調用主要用在基于連接的套接字類型,比如SOCK_STREAM和SOCK_SEQPACKET。它提取出所監聽套接字的等待連接隊列中第一個連接請求,創建一個新的套接字,并返回指向該套接字的文件描述符。新建立的套接字不在監聽狀態,原來所監聽的套接字也不受該系統調用的影響。
備注:新建立的套接字準備發送send()和接收數據recv()。
參數
sockfd
利用系統調用socket()建立的套接字描述符,通過bind()綁定到一個本地地址(一般為服務器的套接字),并且通過listen()一直在監聽連接;
addr
指向struct sockaddr的指針,該結構用通訊層服務器對等套接字的地址(一般為客戶端地址)填寫,返回地址addr的確切格式由套接字的地址類別(比如TCP或UDP)決定;若addr為NULL,沒有有效地址填寫,這種情況下,addrlen也不使用,應該置為NULL;
備注:addr是個指向局部數據結構sockaddr_in的指針,這就是要求接入的信息本地的套接字(地址和指針)。
addrlen
一個值結果參數,調用函數必須初始化為包含addr所指向結構大小的數值,函數返回時包含對等地址(一般為服務器地址)的實際數值;
備注:addrlen是個局部整形變量,設置為sizeof(struct sockaddr_in)。
如果隊列中沒有等待的連接,套接字也沒有被標記為Non-blocking,accept()會阻塞調用函數直到連接出現;如果套接字被標記為Non-blocking,隊列中也沒有等待的連接,accept()返回錯誤EAGAIN或EWOULDBLOCK。
備注:一般來說,實現時accept()為阻塞函數,當監聽socket調用accept()時,它先到自己的receive_buf中查看是否有連接數據包;
若有,把數據拷貝出來,刪掉接收到的數據包,創建新的socket與客戶發來的地址建立連接;
若沒有,就阻塞等待;
為了在套接字中有到來的連接時得到通知,可以使用select()或poll()。當嘗試建立新連接時,系統發送一個可讀事件,然后調用accept()為該連接獲取套接字。另一種方法是,當套接字中有連接到來時設定套接字發送SIGIO信號。
返回值
成功時,返回非負整數,該整數是接收到套接字的描述符;出錯時,返回-1,相應地設定全局變量errno。
示例
while (1) {if ((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1) { //傳NULL什么意思?printf("accept socket error: %s(errno: %d)", strerror(errno), errno);continue;}n = recv(connfd, buff, MAXLINE, 0);buff[n] = '\0';printf("recv msg from client: %s\n", buff);close(connfd);}man 2 文檔中的accept解釋
ACCEPT(2) Linux Programmer's Manual ACCEPT(2)NAMEaccept, accept4 - accept a connection on a socket //接受套接字上的連接SYNOPSIS#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);#define _GNU_SOURCE /* See feature_test_macros(7) */#include <sys/socket.h>int accept4(int sockfd, struct sockaddr *addr,socklen_t *addrlen, int flags);DESCRIPTIONThe accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in the listening state. The original socket sockfd is unaffected by this call.//accept() 系統調用與基于連接的套接字類型(SOCK_STREAM、SOCK_SEQPACKET)一起使用。 它為偵聽套接字提取掛起連接隊列中的第一個連接請求,sockfd,創建一個新的連接套接字,并返回一個引用該套接字的新文件描述符。 新創建的套接字不處于監聽狀態。 原始套接字 sockfd 不受此調用的影響。The argument sockfd is a socket that has been created with socket(2), bound to a local address with bind(2), and is listening for connections after a listen(2).//參數 sockfd 是一個使用 socket(2) 創建的套接字,使用 bind(2) 綁定到本地地址,并在 listen(2) 之后偵聽連接。The argument addr is a pointer to a sockaddr structure. This structure is filled in with the address of the peer socket, as known to the communications layer. The exact format of the address returned addr is determined by the socket's address family (see socket(2) and the respective protocol man pages).When addr is NULL, nothing is filled in; in this case, addrlen is not used, and should also be NULL.//參數 addr 是指向 sockaddr 結構的指針。 如通信層所知,該結構用對等套接字的地址填充。 返回地址 addr 的確切格式由套接字的地址族確定(參見 socket(2) 和相應的協議手冊頁)。//addr為NULL時,不填任何內容; 在這種情況下,不使用 addrlen,也應該為 NULL。The addrlen argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.//addrlen 參數是一個值結果參數:調用者必須將其初始化為包含 addr 指向的結構的大小(以字節為單位); 返回時它將包含對等地址的實際大小。The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.//如果提供的緩沖區太小,返回的地址會被截斷; 在這種情況下,addrlen 將返回一個大于提供給調用的值。If no pending connections are present on the queue, and the socket is not marked as nonblocking, accept() blocks the caller until a connection is present.If the socket is marked nonblocking and no pending connections are present on the queue, accept() fails with the error EAGAIN or EWOULDBLOCK.//如果隊列中沒有掛起的連接,并且套接字沒有被標記為非阻塞,accept() 會阻塞調用者,直到連接出現。//如果套接字被標記為非阻塞并且隊列中沒有掛起的連接,accept() 將失敗并返回 EAGAIN 或 EWOULDBLOCK 錯誤。In order to be notified of incoming connections on a socket, you can use select(2) or poll(2). A readable event will be delivered when a new connection is attempted and you may then call accept() to get a socket for that connection. Alternatively, you can set the socket to deliver SIGIO when activity occurs on a socket; see socket(7) for details.//為了通知套接字上的傳入連接,您可以使用 select(2) 或 poll(2)。 嘗試新連接時將傳遞一個可讀事件,然后您可以調用 accept() 以獲取該連接的套接字。 或者,您可以將套接字設置為在套接字上發生活動時傳遞 SIGIO; 有關詳細信息,請參閱套接字(7)。For certain protocols which require an explicit confirmation, such as DECNet, accept() can be thought of as merely dequeuing the next connection request and not implying confirmation. Confirmation can be implied by a normal read or write on the new file descriptor, and rejection can be implied by closing the new socket. Currently only DECNet has these semantics on Linux.//對于某些需要顯式確認的協議,例如 DECNet,accept() 可以被認為只是將下一個連接請求出隊而不是暗示確認。 對新文件描述符的正常讀取或寫入可以暗示確認,并且可以通過關閉新套接字來暗示拒絕。 目前只有 DECNet 在 Linux 上具有這些語義。If flags is 0, then accept4() is the same as accept(). The following values can be bitwise ORed in flags to obtain different behavior://如果 flags 為 0,則 accept4() 與 accept() 相同。 可以在標志中對以下值進行按位或運算以獲得不同的行為:SOCK_NONBLOCK Set the O_NONBLOCK file status flag on the new open file description. Using this flag saves extra calls to fcntl(2) to achieve the same result.SOCK_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description of the O_CLOEXEC flag in open(2) for reasons whythis may be useful.RETURN VALUEOn success, these system calls return a nonnegative integer that is a descriptor for the accepted socket. On error, -1 is returned, and errno is setappropriately.Error handlingLinux accept() (and accept4()) passes already-pending network errors on the new socket as an error code from accept(). This behavior differs from other BSD socket implementations. For reliable operation the application should detect the network errors defined for the protocol after accept() and treat them like EAGAIN by retrying. In the case of TCP/IP, these are ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, and ENETUNREACH.//Linux accept()(和 accept4())將新套接字上已經掛起的網絡錯誤作為來自 accept() 的錯誤代碼傳遞。 此行為不同于其他 BSD 套接字實現。 為了可靠運行,應用程序應該在 accept() 之后檢測為協議定義的網絡錯誤,并通過重試將它們視為 EAGAIN。 對于 TCP/IP,它們是 ENETDOWN、EPROTO、ENOPROTOOPT、EHOSTDOWN、ENONET、EHOSTUNREACH、EOPNOTSUPP 和 ENETUNREACH。ERRORSEAGAIN or EWOULDBLOCKThe socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 and POSIX.1-2008 allow either error to be returned forthis case, and do not require these constants to have the same value, so a portable application should check for both possibilities.EBADF The descriptor is invalid.ECONNABORTEDA connection has been aborted.EFAULT The addr argument is not in a writable part of the user address space.EINTR The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7).EINVAL Socket is not listening for connections, or addrlen is invalid (e.g., is negative).EINVAL (accept4()) invalid value in flags.EMFILE The per-process limit on the number of open file descriptors has been reached.ENFILE The system-wide limit on the total number of open files has been reached.ENOBUFS, ENOMEMNot enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory.ENOTSOCKThe file descriptor sockfd does not refer to a socket.EOPNOTSUPPThe referenced socket is not of type SOCK_STREAM.EPROTO Protocol error.In addition, Linux accept() may fail if:EPERM Firewall rules forbid connection.In addition, network errors for the new socket and as defined for the protocol may be returned. Various Linux kernels can return other errors such asENOSR, ESOCKTNOSUPPORT, EPROTONOSUPPORT, ETIMEDOUT. The value ERESTARTSYS may be seen during a trace.VERSIONSThe accept4() system call is available starting with Linux 2.6.28; support in glibc is available starting with version 2.10.CONFORMING TOaccept(): POSIX.1-2001, POSIX.1-2008, SVr4, 4.4BSD (accept() first appeared in 4.2BSD).accept4() is a nonstandard Linux extension.On Linux, the new socket returned by accept() does not inherit file status flags such as O_NONBLOCK and O_ASYNC from the listening socket. This behaviordiffers from the canonical BSD sockets implementation. Portable programs should not rely on inheritance or noninheritance of file status flags and alwaysexplicitly set all required flags on the socket returned from accept().NOTESPOSIX.1-2001 does not require the inclusion of <sys/types.h>, and this header file is not required on Linux. However, some historical (BSD) implementa‐tions required this header file, and portable applications are probably wise to include it.There may not always be a connection waiting after a SIGIO is delivered or select(2) or poll(2) return a readability event because the connection might have been removed by an asynchronous network error or another thread before accept() is called. If this happens, then the call will block waiting for the next connection to arrive. To ensure that accept() never blocks, the passed socket sockfd needs to have the O_NONBLOCK flag set (see socket(7)).//在傳遞 SIGIO 或 select(2) 或 poll(2) 返回可讀性事件之后,可能并不總是有連接等待,因為在調用 accept() 之前,連接可能已被異步網絡錯誤或另一個線程刪除。 如果發生這種情況,則調用將阻塞等待下一個連接到達。 為了確保 accept() 永遠不會阻塞,傳遞的套接字 sockfd 需要設置 O_NONBLOCK 標志(參見 socket(7))。The socklen_t typeThe third argument of accept() was originally declared as an int * (and is that under libc4 and libc5 and on many other systems like 4.x BSD, SunOS 4,SGI); a POSIX.1g draft standard wanted to change it into a size_t *, and that is what it is for SunOS 5. Later POSIX drafts have socklen_t *, and so dothe Single UNIX Specification and glibc2. Quoting Linus Torvalds:"_Any_ sane library _must_ have "socklen_t" be the same size as int. Anything else breaks any BSD socket layer stuff. POSIX initially did make it asize_t, and I (and hopefully others, but obviously not too many) complained to them very loudly indeed. Making it a size_t is completely broken, exactlybecause size_t very seldom is the same size as "int" on 64-bit architectures, for example. And it has to be the same size as "int" because that's what theBSD socket interface is. Anyway, the POSIX people eventually got a clue, and created "socklen_t". They shouldn't have touched it in the first place, butonce they did they felt it had to have a named type for some unfathomable reason (probably somebody didn't like losing face over having done the originalstupid thing, so they silently just renamed their blunder)."EXAMPLESee bind(2).SEE ALSObind(2), connect(2), listen(2), select(2), socket(2), socket(7)COLOPHONThis page is part of release 4.04 of the Linux man-pages project. A description of the project, information about reporting bugs, and the latest versionof this page, can be found at http://www.kernel.org/doc/man-pages/.Linux 2015-12-28 ACCEPT(2)Manual page accept(2) line 104/145 (END) (press h for help or q to quit)錯誤處理
Linux下,accept()把已等待的網絡錯誤傳給新建立的連接,當作是accept()返回的錯誤。這于其他的BSD實現是不同的。為了可靠運行,應該在accept()之后檢測協議已定義的一些網絡錯誤,并把這些錯誤當作EAGAIN并重試。對于TCP/IP協議來說,主要有錯誤:ENETDOWN,EPROTO,ENOPROTOOPT,EHOSTDOWN,ENONET,EHOSTUNREACH,EOPNOTSUPP和ENETUNREACH。
參考文章1:socket編程之accept()函數
參考文章2:linux C語言 socket編程教程(附兩個例子)
總結
以上是生活随笔為你收集整理的C语言socket accept()函数(提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言 socket listen()函
- 下一篇: 什么是Linux系统调用system c