对 socket 的认识一直不够深入,之前一直是照类似于下面这样的图在理解
图中显示server
先accept
,然后才是client
发送connect
。
while(1){
clin_len = sizeof(clin_addr);
printf("1111\n");
cfd = accept(lfd, (struct sockaddr *)&clin_addr, &clin_len);
printf("2222\n");
...
当服务器端程序启动的时候,会先打印出1111
,当有客户端连接的时候才会打印出2222
,也就证明了在上面的代码中是先有了accept
再connect
的。
而在使用select
模型的时候,把服务器的监听套截字描述符加入到 select read fd set
中之后,默认是没有数据可读的,只有当有客户端连接的之后,才调用accept
,如下代码:
FD_ZERO(&read_set);
FD_SET(listenfd,&read_set);
while(1){
struct sockaddr_in addr;
read_set = allset;
fd_ready_num = select(maxfd, &read_set, NULL, NULL, NULL);
if (FD_ISSET(listenfd,&read_set))
{
sin_size = sizeof(addr);
if ((connectfd = accept(listenfd, (struct sockaddr *)&addr, &sin_size)) == -1)
{
perror("接收错误\\n");
continue;
}
...
当有客户端连接过来之后,listenfd
有数据可读。
accept
翻译成人类的语言来说就是(服务员)「接受顾客的请求」,connect
就是(顾客)「上门寻求服务」。所以没有客户端连接的时候,accept
就在那里等着。那个传入的客户端地址加粗文字的参数是用来返回数据的加粗文字,因为 C 里边没法返回一个结构体,更没法返回多个值。
第一个图说的是函数调用的时机,而不是它返回的时机。第二个图则相反。
当服务器端程序启动的时候,会先打印出1111,当有客户端连接的时候才会打印出2222,也就证明了在上面的代码中是先有了accept再connect的。
这只能说明 connect
会导致执行状态的 accept
返回,并不能说明你不 accept
对端就不能成功地 connect
。一个肯定的事例并不排除其否定事例的存在。
其实呢,这两种都不能算是错但也不是完全对,只是第一种好一点儿而已。listen
之后,客户端就可以链接服务器了,连接中需要的各种握手都是操作系统帮你完成的。accept
跟select
从作用上来说都是判断有没有客户端连接进来。accept
只是一种专用的判断:
accept
是无限等待的accept
只是判断有没有客户端连接进来如果同时有多个客户端连接进来,
accept
只会返回其中一个连接的描述符,其他的要等下次(下下次。。。)调用accept
也就是说 accept
是一种同步模型,调用这个这个函数的意思就是,判断有没有客户端连接进来,如果有就返回这个链接的描述符(同时把客户端的地址放到第二个参数指向的位置),如果没有客户端连接进来,就无限的等待直到有客户端连接进来,再返回这个客户端的连接描述符和地址。
这两幅图都只是accept
在上述两种情况下不同的行为而已,所以说都不能说是错的,也不能说是对的。只是第一种好一点儿而已:(如果没有连接进来)执行到accept
应该要表明它是停止在这里的,等到有连接进来才会执行accept
的下一句,从这方面说第一个图比较好,但是不应该阻塞
和accept
不应该加那个箭头。从时间上来说一般执行listen
之后立即执行accept
所以一般情况下这之间很少会有客户端连接。从这方面来说也是第一个图好一点。