功能

accept()接受一个客户端的连接请求,并返回一个新的套接字(被动监听客户端的三次握手连接请求,三次握手成功即建立连接成功)。所谓“新的”就是说这个套接字与socket()返回的用于监听和接受客户端的连接请求的套接字不是同一个套接字。与本次接受的客户端的通信是通过在这个新的套接字上发送和接收数据来完成的。

accept函数指定服务端去接受客户端的连接,接收后,返回了客户端套接字的标识,且获得了客户端套接字的“地方”(包括客户端IP和端口信息等)。

头文件以及参数
# include <sys/types.h>
# include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数一:用来标识服务端套接字(也就是listen函数中设置为监听状态的套接字)
参数二:是用来保存客户端套接字对应的内存空间变量(包括客户端IP和端口信息等)
参数三:参数二内存空间的占地大小。

着重讲讲第二参数
addr参数
1)用于记录发起连接请求的客户端的IP和端口号
2)如果应用层需要记录客户端的IP和端口号,则设置第二的参数,否则设置为NULL
3)如前所述,虽然底层内核指定的用于保存IP和端口的是struct sockaddr结构体变量,但是用此变脸操作较为复杂,需要使用更加便利的结构体变量,比如对于TCP/IP协议族可以选择struct sockaddr_in结构体变量
4)在使用时与bind函数一样,需要自定义struct sockaddr_in结构体变量,将此变量的地址经过强制类型转换后赋值给第二个参数
示例:

int cfd = -1; //定义用于与客户端通信的通信描述符
struct sockaddr_in clnaddr = {0}; //定义用于存放客户端ip/端口号的结构体
int clnaddr_size = sizeof(clnaddr); //定义用于存放第二个参数大小的整型变量
cfd = accept(sockfd,(struct sockaddr *)&clnaddr,&clnaddr_size); //调用accept函数,注意由强制类型转换

返回值

1)成功:
返回一个服务器用于以后通信的“通信描述符”
2)失败:
返回-1

阻塞式的socket:如果没有客户端套接字去请求,它便会在那里一直痴痴地等下去,直到永远
非阻塞式的socket: 那么accept函数就会立即返回

这篇文章专门写这点,请点击查阅!

这里有个注意的点:
在freebsd上,accept调用如果被信号中断,则会返回错误,并设置errno为EINTR,这样程序可以做判断后续操作;但是,如果通过signal来设置信号处理程序的话,accept将不会被中断或者被中断后被重新启动了,而如果使用sigaction来设置信号处理函数的话,accept会返回并设置errno;

这是为什么呢?因为在freebsd上signal是使用sigaction来实现的,这个函数如下:
sigset_t _sigintr;

sig_t
signal(s, a)
int s;
sig_t a;
{
struct sigaction sa, osa;

sa.sa_handler = a;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (!sigismember(&_sigintr, s))
sa.sa_flags |= SA_RESTART;
if (_sigaction(s, &sa, &osa) < 0)
return (SIG_ERR);
return (osa.sa_handler);
}

从代码中可以看出来,signal会判断捕捉的信号是否需要中断处理,如果不需要中断处理则设置SA_RESTART标志以重启系统调用;所以如果想在接受到信号以后不重启系统调用的话,需要通过siginterrupt(sig, 1)来设置该信号不重启系统调用。
(出处:http://blog.sina.com.cn/s/blog_50fea33a01008z5v.html)

多说一下,如何在应用层使用得到或使用客户端IP和端口号?

这个函数的参数二是传出参数,其结构体为struct sockaddr_in,如下:

struct sockaddr_in {
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* Internet地址 */
unsigned char sin_zero[8]; /* 与struct sockaddr一样的长度 */
};

struct in_addr就是32位IP地址。
struct in_addr {
unsigned long s_addr;
};

那么我们就可以在应用层使用得到或使用客户端IP和端口号
步骤:
1)accept函数建立连接成功,那么客户端的IP和端口号将被记录在struct sockaddr_in类型的变量中
2)此变量的第二个成员unsigned short int sin_port,将存放客户端的端口号
3)此变量的第三个成员struct in_addr sin_addr,将存放客户端的IP
4)接下来需要调用 ntohs 与 inet_ntoa 函数进行处理:

对于ntohs函数:将端口从网络端序转为主机端序

对于inet_ntoa函数:不仅要将IP从网络端序转为主机端序,还要将实际存放的32位无符号整型数据转为点分十进制字符串

printf(“clint_port = %d\n clint_ip =%s\n”,ntohs(clnaddr.sin_port),inet_ntoa(clnaddr.sin_addr.s_addr));
//打印客户端的IP和端口号 注意要进行端序转换

Logo

更多推荐