socket编程中listen(), connect(), accept() 三者的关系

本文详细介绍了socket编程的基本流程,包括listen(), connect() 和 accept() 函数的使用方式及原理。解释了这些函数如何帮助服务器监听连接请求、客户端发起连接以及服务器接受连接。还探讨了服务器如何处理客户端数据和区分不同客户端的连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文章:
https://blog.youkuaiyun.com/sinat_36629696/article/details/81984228

https://blog.youkuaiyun.com/qq_25665807/article/details/77884936?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=1332042.23896.16193336669577365&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

socket 编程流程
在这里插入图片描述

listen()函数

#include <sys/types.h>          
#include <sys/socket.h>

int listen(int sockfd, int backlog);

在网络通信中, 客户端通常处于主动的一方, 而服务器则是被动的一方, 服务器是被连接的, 所以他要时刻准备着被连接, 所以就需要调用 listen() 来监听, 等着被连接.

listen() 函数的主要作用就是将 socket() 函数得到的 sockfd 变成一个被动监听的套接字, 用来被动等待客户端的连接, 而参数 backlog 的作用就是设置连接队列的长度

三次握手,建立连接不是 listen() 函数完成的, 而是内核完成的, listen() 函数只是将 sockfd 和 backlog 告诉内核, 然后就返回了

之后, 如果有客户端通过 connect() 发起连接请求, 内核就会通过三次握手建立连接, 然后将建立好的连接放到一个队列中, 这个队列称为: 已完成连接队列

这里还需要再分析一下 listen() 函数的第二个参数 backlog, 实际上, 内核为每一个监听套接字维护两个队列

  • 未完成连接队列

    其中存储着尚未建立连接的套接字

  • 已完成连接队列

    存储着已经完成连接的套接字

一般认为 backlog 参数的大小是这两个队列大小之和, 收到客户端的连接请求之后, 内核创建一个套接字存储在未完成连接队列中, 来进行三次握手建立连接

连接建立完成以后, 这个套接字就加到已完成连接队列的队尾, 服务器从已完成连接队列中取走一个, 又空出一个位置, 然后已经完成连接的套接字由补充进来, 就这样实现动态平衡

所以说, 如果在 listen 之后不进行 accept , connect 也是会成功返回的, 其实此时连接就已经建立好了

connect()函数

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd,
            const struct sockaddr *addr,
            socklen_t addrlen);

通常客户端通过 connect() 函数来向服务端主动发起连接, 但是建立连接也不是这个函数完成的, 而是由内核完成的, 这个函数仅仅是通知内核通过三次握手建立连接, 然后将结果返回给这个函数.

这个函数默认会一直阻塞, 直到内核连接建立成功或者超时失败才返回(但一般这个过程很快)

客户端通过 connect() 函数来通知内核建立连接

accept()函数

#include <sys/types.h>          
#include <sys/socket.h>

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

accept() 函数的作用就是在已完成连接队列中取出一个已经建立好的连接

如果这个队列中已经没有已完成连接的套接字, 那么 accept() 就会一直阻塞, 直到取得一个已经建立连接的套接字

如果服务器来不及调用 accept() 取走队列中已经建立好的连接, 导致队列中的连接满了, 会怎么样呢 ?

这取决于内核的具体实现, 在Linux中会延时建立连接.


Q1:服务器收到数据后会怎样处理?(猜测)
客户端发送过来的数据可以分为2种,一种是连接请求,一种是已经建立好连接后的数据传输。

由于TCP/IP协议栈是维护着一个接收和发送缓冲区的。在接收到来自客户端的数据包后,服务器端的TCP/IP协议栈应该会做如下处理:如果收到的是请求连接的数据包,则传给监听着连接请求端口的socetfd套接字,进行accept处理;如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲区。这样,当服务器端需要读取指定客户端的数据时,则可以利用socketfd_new套接字通过recv或者read函数到缓冲区里面去取指定的数据(因为socketfd_new代表的socket对象记录了客户端IP和端口,因此可以鉴别)。

Q2:为什么多个客户端可以连接服务器的同个端口?服务器如何区分来自两个客户端的连接?
一个Socket连接的主键(即不同socket之间的区分)是由一个五元组{SRC-IP, SRC-PORT, DEST-IP, DEST-PORT, PROTOCOL}组成,即{源地址,源端口,目标地址,目标端口,协议}组成。


其他更详细的过程参考
https://www.cnblogs.com/f-ck-need-u/p/7623252.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值