问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

linux服务器如何感知有connect请求

发布网友 发布时间:2022-04-25 09:10

我来回答

3个回答

热心网友 时间:2023-07-12 18:33

1、sys_connect

对于客户端来说,当创建了一个套接字后,就可以连接它了。
case SYS_CONNECT:
err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
break;[/code]

asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)
{
struct socket *sock;
char address[MAX_SOCK_ADDR];
int err;

sock = sockfd_lookup(fd, &err);
if (!sock)
goto out;
err = move_addr_to_kernel(uservaddr, addrlen, address);
if (err < 0)
goto out_put;

err = security_socket_connect(sock, (struct sockaddr *)address, addrlen);
if (err) goto out_put;

err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen,
sock->file->f_flags);
out_put:
sockfd_put(sock);
out:
return err;
}

跟其它操作类似,sys_connect 接着调用 inet_connect:
/*
* Connect to a remote host. There is regrettably still a little
* TCP 'magic' in here.
*/
int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
struct sock *sk = sock->sk;
int err;
long timeo;

lock_sock(sk);

if (uaddr->sa_family == AF_UNSPEC) {
err = sk->sk_prot->disconnect(sk, flags);
sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
goto out;
}

提交的协议簇不正确,则断开连接。

switch (sock->state) {
default:
err = -EINVAL;
goto out;
case SS_CONNECTED:
err = -EISCONN;
goto out;
case SS_CONNECTING:
err = -EALREADY;
/* Fall out of switch with err, set for this state */
break;[/code] socket 处于不正确的连接状态,返回相应的错误值。

case SS_UNCONNECTED:
err = -EISCONN;
if (sk->sk_state != TCP_CLOSE)
goto out;
/*调用协议的连接函数*/
err = sk->sk_prot->connect(sk, uaddr, addr_len);
if (err < 0)
goto out;
/*协议方面的工作已经处理完成了,但是自己的一切工作还没有完成,所以切换至正在连接中*/
sock->state = SS_CONNECTING;

/* Just entered SS_CONNECTING state; the only
* difference is that return value in non-blocking
* case is EINPROGRESS, rather than EALREADY.
*/
err = -EINPROGRESS;
break;
}

对于 TCP的实际的连接,是通过调用 tcp_v4_connect()函数来实现的。

二、tcp_v4_connect函数
对于 TCP 协议来说,其连接,实际上就是发送一个 SYN 报文,在服务器的应到到来时,回答它一个 ack 报文,也就是完成三次握手中的第一和第三次。

要发送 SYN 报文,也就是说,需要有完整的来源/目的地址,来源/目的端口,目的地址/端口由用户态提交,但是问题是没有自己的地址和端口,因为并没有调 用过 bind(2),一台主机,对于端口,可以像 sys_bind()那样,从本地未用端口中动态分配一个,那地址呢?因为一台主机可能会存在多个 IP地 址,如果随机动态选择,那么有可能选择一个错误的来源地址,将不能正确地到达目的地址。换句话说,来源地址的选择,是与路由相关的。

调用路由查找的核心函数 ip_route_output_slow(),在没有提供来源地址的情况下,会根据实际情况,调用 inet_select_addr()函数来选择一个合适的。同时,如果路由查找命中,会生成一个相应的路由缓存项,这个缓存项,不但对当前发送 SYN 报 文有意义,对于后续的所有数据包,都可以起到一个加速路由查找的作用。这一任务,是通过 ip_route_connect()函数完成的,它返回相应的路由缓存项(也就是说,来源地址也在其中了):

static inline int ip_route_connect(struct rtable **rp, u32 dst,
u32 src, u32 tos, int oif, u8 protocol,
u16 sport, u16 dport, struct sock *sk)
{ struct flowi fl = { .oif = oif,
.nl_u = { .ip4_u = { .daddr = dst,
.saddr = src,
.tos = tos } },
.proto = protocol,
.uli_u = { .ports =
{ .sport = sport,
.dport = dport } } };

int err;
if (!dst || !src) {
err = __ip_route_output_key(rp, &fl);
if (err)
return err;
fl.fl4_dst = (*rp)->rt_dst;
fl.fl4_src = (*rp)->rt_src;
ip_rt_put(*rp);
*rp = NULL;
}
return ip_route_output_flow(rp, &fl, sk, 0);
}

首先,构建一个搜索 key fl,在搜索要素中,来源地址/端口是不存在的。所以,当通过__ip_route_output_key 进行查找时,第一次是不会命中缓存的。 __ip_route_output_key 将继续调用ip_route_output_slow()函数,在路由表中搜索,并返回一个合适的来源地址, 并且生成一个路由缓存项。 路由查找的更多细节,我会在另一个贴子中来分析。

/* This will initiate an outgoing connection. */
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_sock *inet = inet_sk(sk);
struct tcp_sock *tp = tcp_sk(sk);
struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
struct rtable *rt;
u32 daddr, nexthop;
int tmp;
int err;

if (addr_len < sizeof(struct sockaddr_in))
return -EINVAL;

if (usin->sin_family != AF_INET)

return -EAFNOSUPPORT;

校验地址长度和协议簇。
nexthop = daddr = usin->sin_addr.s_addr;
将下一跳地址和目的地址的临时变量都暂时设为用户提交的地址。

if (inet->opt && inet->opt->srr) {
if (!daddr)
return -EINVAL;
nexthop = inet->opt->faddr;
}
如果使用了来源地址路由,选择一个合适的下一跳地址。

tmp = ip_route_connect(&rt, nexthop, inet->saddr,
RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
IPPROTO_TCP,
inet->sport, usin->sin_port, sk);
if (tmp < 0)
return tmp;

if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
ip_rt_put(rt);
return -ENETUNREACH;
}
进行路由查找,并校验返回的路由的类型,TCP是不被允许使用多播和广播的。

if (!inet->opt || !inet->opt->srr)
daddr = rt->rt_dst;
更新目的地址临时变量——使用路由查找后返回的值。

if (!inet->saddr)
inet->saddr = rt->rt_src;
inet->rcv_saddr = inet->saddr;
如果还没有设置源地址,和本地发送地址,则使用路由中返回的值。

if (tp->rx_opt.ts_recent_stamp && inet->daddr != daddr) {
/* Reset inherited state */
tp->rx_opt.ts_recent = 0;
tp->rx_opt.ts_recent_stamp = 0;
tp->write_seq = 0;
}

if (sysctl_tcp_tw_recycle &&
!tp->rx_opt.ts_recent_stamp && rt->rt_dst == daddr) {
struct inet_peer *peer = rt_get_peer(rt);
/* VJ's idea. We save last timestamp seen from
* the destination in peer table, when entering state TIME-WAIT
* and initialize rx_opt.ts_recent from it, when trying new connection.
*/

if (peer && peer->tcp_ts_stamp + TCP_PAWS_MSL >= xtime.tv_sec) {
tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;
tp->rx_opt.ts_recent = peer->tcp_ts;
}
}
这个更新初始状态方面的内容,还没有去分析它。

inet->dport = usin->sin_port;
inet->daddr = daddr;
保存目的地址及端口。

tp->ext_header_len = 0;
if (inet->opt)
tp->ext_header_len = inet->opt->optlen;

tp->rx_opt.mss_clamp = 536;
设置最小允许的 mss 值

tcp_set_state(sk, TCP_SYN_SENT);
套接字状态被置为 TCP_SYN_SENT,

err = tcp_v4_hash_connect(sk);
if (err)
goto failure;
动态选择一个本地端口,并加入 hash 表,与bind(2)选择端口类似。

err = ip_route_newports(&rt, inet->sport, inet->dport, sk);
if (err)
goto failure;

/* OK, now commit destination to socket. */
__sk_dst_set(sk, &rt->u.dst);
tcp_v4_setup_caps(sk, &rt->u.dst);
因为本地端口已经改变,使用新端口,重新查找路由,并用新的路由缓存项更新 sk 中保存的路由缓存项。

if (!tp->write_seq)

tp->write_seq = secure_tcp_sequence_number(inet->saddr,
inet->daddr,
inet->sport,
usin->sin_port);
为 TCP报文计算一个 seq值(实际使用的值是 tp->write_seq+1)。

inet->id = tp->write_seq ^ jiffies;

err = tcp_connect(sk);
rt = NULL;
if (err)
goto failure;

return 0;

tp_connect()函数用来根据 sk 中的信息,构建一个完成的 syn 报文,并将它发送出去。在分析 tcp栈的实现时再来分析它。

根据 TCP协议,接下来的问题是,
1、可能收到了服务器的应答,则要回送一个 ack 报文;
2、如果超时还没有应答,则使用超时重发定时器;

热心网友 时间:2023-07-12 18:34

用accept接受客户端请求。

热心网友 时间:2023-07-12 18:34

22、书湖阴先生壁 王安石
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
哥们,我是黑龙江齐齐哈尔现在高一的,想报考中国刑警学院,什么分数线啊... 没有驾驶证车辆查到有违章怎么办 广州岭南印象园景区介绍 ...从新选像我这样的人不知道读什么又懒又不想背书。 哎呀哎呀好烦阿要期末考试了,我不想背书 高考我是文科的光不想背书怎么办呢 我不想背书啊啊啊啊,考第一真难,现在的初中生涯好难熬,哪位大神教我... 现在初二了 不想背书 感觉好无聊 只想玩 但是成绩还是不错的 我该... ...时候学习还中等。现在基本一窍不通。也不想背书,身边也没人背,上课... [精选]菊花茶的副作用 济南赤霞山庄怎么样? 帮忙起一个山庄的名字。霸气点 ip地址是172.16.11.20~200,那子网掩码和网关以及DNS分别是什么? 管道砂石基础的压实度一般规范规定的是多少? 求助?如何区别link,connect,join 丹霞山景区里的旅馆? 今天刚从百度云把模拟火车2016下载到电脑里,下载解压并安装完毕后进行游戏却出现了问题,附加了图片 怎么样修改网卡地址 游白书水付过文言文翻译 connect the dots是什么意思 模拟火车2016电脑版怎样下载中国机车和中国线路? 什么是砂石回填? 游白树付过的翻译 广东韶关丹霞山 connect失败 苏轼的《游白水付过》的翻译 模拟火车2016哪里可以下载正版?我在快吧下的玩不了 武侠小说里山庄的名字都有哪些? 172.16.2.10 10.6.24.20 10.30.36.200 的子网掩码 网络号 广播地址 主机地址范围 地基处理级配砂石能换填多深?哪个规范上有依据? connect连接成功为什么返回-1 华为vlan batch 2 to 4094 创建了四千多没用的vlan有何影响? 不复甚寐,书以付过的原因 丹霞山之旅 级配砂石 在哪个规范上 陆小凤传奇剧情简介 ip地址:172.20.5.200/16这个 /16代表什麽呢 山东著名旅游景点、度假村 172.16.16.0/20这个IP地址后面的/20是什么意思? 山东荣成住宿问题 游白水书付过中 书以付过的以的意思? 中国有姓龙的吗 人事调动申请书怎么写 离岸银行账户现在办理需要什么条件? UDP的Connect和TCP的Connect有什么不同? 神雕侠侣的李莫愁是怎么死的 《游白水书付过》中作者为什么会“不复甚寐,书已付过”? 个人离岸账户开户条件是什么 linux socket中的connect无法连接 文言文游白水书付过中作者为什么会不复甚寐,书以付过