一个人也可以建立 TCP 连接呢
今天(恰巧是今天)看到有人在 SegmentFault 上问「TCP server 为什么一个端口可以建立多个连接?」。提问者认为 client 端就不能使用相同的本地端口了。理论上来说,确定一条链路,只要五元组(源IP、源端口号、目标IP、目标端口号、协议)唯一就可以了,所以这不应该是技术限制。而实际上,Linux 3.9 之后确实可以让客户端使用相同的地址来连接不同的目标,只不过要提前跟内核说好而已。
当然,你不能使用同一个 socket,不然调用connect连接的时候会报错:
|
1
|
[Errno 106] (EISCONN) Transport endpoint is already connected |
man 2 connect里说了:
Generally, connection-based protocol sockets may successfully connect() only once; connectionless protocol sockets may use connect() multiple times to change their association.
想也是,一个 socket 连接到多个目标,那发送的时候到底发给谁呢?TCP 又不像 UDP 那样无状态的,以前做过什么根本不管。
那用多个 socket 就可以了嘛。服务端其实也一直是用多个 socket 来处理多个连接的不是么,每次accept都生成个新的 socket。
|
1
2
3
4
5
6
7
8
9
10
11
|
>>> import socket>>> s = socket.socket()# since Linux 3.9, 见 man 7 socket>>> s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)>>> s2 = socket.socket()>>> s2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)>>> s.bind(('127.0.0.1', 12345))>>> s2.bind(('127.0.0.1', 12345))# 都可以使用同一本地地址来连接哦>>> s.connect(('127.0.0.1', 80))>>> s2.connect(('127.0.0.1', 4321)) |
连上去之后 netstat 的输出(4568 进程是上边这个程序,另两个进程一个是 nginx,另一个是我的另一个 Python 程序):
|
1
2
3
4
5
6
7
|
>>> netstat -npt | grep 12345(Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.)tcp 0 0 127.0.0.1:4321 127.0.0.1:12345 ESTABLISHED 18284/python3tcp 0 0 127.0.0.1:12345 127.0.0.1:4321 ESTABLISHED 4568/python3tcp 0 0 127.0.0.1:80 127.0.0.1:12345 ESTABLISHED -tcp 0 0 127.0.0.1:12345 127.0.0.1:80 ESTABLISHED 4568/python3 |
当然你要是连接相同的地址会报错的:
|
1
|
OSError: [Errno 99] Cannot assign requested address |
那个五元组已经被占用啦。
同时创建连接:恰巧你也在这里
有时候,我们不能一个劲地等待。主动出击也是可以的,即便对方并没有在等待。
这个在 TCP 里叫「simultaneous open」,用于 TCP 打洞。但是比起 UDP 打洞难多了,因为那个「simultaneous」字眼:必须同时调用connect,双方的 SYN 包要交叉,早了或者晚了都是会被拒绝的。
所以手工就办不到啦,在本地测试也不容易办到。我本地的系统时间是使用 NTP 同步的,再用一个时钟也和 NTP 同步的 VPS 就可以啦,我这里延迟 80ms 左右,足够那两个 SYN 「在空中会面」了。以下是代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#!/usr/bin/env python3import timeimport sysimport socketimport datetimedef wait_until(t): deadline = t.timestamp() to_wait = deadline - time.time() time.sleep(to_wait)s = socket.socket()s.bind(('', 1314))if sys.argv[1] == 'local': ip = 'VPS 的地址'else: ip = '我的地址't = datetime.datetime(2015, 8, 19, 22, 14, 30)wait_until(t)s.connect((ip, 1314))s.send(b'I love you.')print(s.recv(1024)) |
当然,我是公网 IP。在内网里包就不容易进来啦。
然后双方在约定的时间之前跑起来即可,结果是这样子的:
|
1
2
3
4
5
6
7
|
# 本地>>> python3 t.py localb'I love you.'# VPS 上>>> python3 t.py remoteb'I love you.' |
一个人也可以建立 TCP 连接呢
如果你没有 VPS,或者没有公网 IP,也是有活动可以参与的哦。即使只有一个 socket,也可以自己连接到自己的:
|
1
2
3
4
5
6
7
8
|
>>> import socket >>> s = socket.socket()>>> s.bind(('127.0.0.1', 1314))>>> s.connect(('127.0.0.1', 1314))>>> s.send(b'I love you.')11>>> s.recv(1024)b'I love you.' |
netstat 输出:
|
1
2
|
>>> netstat -npt | grep 1314tcp 0 0 127.0.0.1:1314 127.0.0.1:1314 ESTABLISHED 8050/python |
一个人也可以建立 TCP 连接呢的更多相关文章
- 为什么建立TCP连接需要三次握手,为什么断开TCP连接需要四次握手,TIME_WAIT状态的意义
为什么建立TCP连接需要三次握手? 原因:为了应对网络中存在的延迟的重复数组的问题 例子: 假设client发起连接的连接请求报文段在网络中没有丢失,而是在某个网络节点长时间滞留了,导致延迟到达ser ...
- Linux 建立 TCP 连接的超时时间分析(解惑)
Linux 系统默认的建立 TCP 连接的超时时间为 127 秒,对于许多客户端来说,这个时间都太长了, 特别是当这个客户端实际上是一个服务的时候,更希望能够尽早失败,以便能够选择其它的可用服务重新尝 ...
- 详解TCP三次握手(建立TCP连接过程)
在讲述TCP三次握手,即建立TCP连接的过程之前,需要先介绍一下TCP协议的包结构. 这里只对涉及到三次握手过程的字段做解释 (1) 序号(Sequence number) 我们通过 TCP 协议将数 ...
- 通过UDP建立TCP连接
解释 通过UDP广播查询服务器的IP地址,然后再建立TCP点对点连接. 应用场景 在服务器IP未知时,并且已知服务器与客户端明确在一个局域网或者允许组播的子网下. 通过UDP发现服务器地址然后再进行T ...
- 最简单的理解 建立TCP连接 三次握手协议
最简单的理解一:建立TCP连接:三次握手协议 客户端:我要对你讲话,你能听到吗:服务端:我能听到:而且我也要对你讲话,你能听到吗:客户端:我也能听到.…….互相开始通话…….. 二:关闭TCP ...
- 图说使用socket建立TCP连接
在网络应用如火如荼的今天,熟悉TCP/IP网络编程,那是最好不过.如果你并不非常熟悉,不妨花几分钟读一读. 为了帮助快速理解,先上个图,典型的使用socket建立和使用TCP/UDP连接过程为(截图来 ...
- 放弃等待,故障到来:少一个 await 引发的 tcp 连接泄漏故障
更新:后来升级至 .NET Core 2.2 Preview 3 ,并将 System.Net.Http 升级至 4.3.4 之后没出现这个问题,问题与 https://github.com/dotn ...
- 建立TCP连接的三次握手
请求端(通常称为客户)发送一个 SYN 报文段( SYN 为 1 )指明客户打算连接的服务器的端口,以及初始顺序号( ISN ).服务器发回包含服务器的初始顺序号( ISN )的 SYN 报文段( S ...
- 建立TCP连接过程
1.服务器实例化一个ServerSocket 对象, 表示通过服务器上的端口通信. ServerSocket serverSocket = new ServerSocket(port); 2.服务器调 ...
随机推荐
- 让PHPCms内容页支持JavaScript_
在PHPCms内容页中,出于完全考虑,默认是禁止JavaScript脚本的,所以我们在添加文章时,虽然加入了js代码,但实际上并没有起作用,而是以文本形式显示.如果要让内容页支持JavaScript, ...
- python基础——python解析yaml类型文件
一.yaml介绍 yaml全称Yet Another Markup Language(另一种标记语言).采用yaml作为配置文件,文件看起来直观.简洁.方便理解.yaml文件可以解析字典.列表和一些基 ...
- Shell-help格式详解
前言 linux shell命令通常可以通过-h或--help来打印帮助说明,或者通过man命令来查看帮助,有时候我们也会给自己的程序写简单的帮助说明,其实帮助说明格式是有规律可循的 帮助示例 下面是 ...
- 【Linux】Linux基本命令扫盲【转】
转自:http://www.cnblogs.com/lcw/p/3762927.html [VI使用] 1.在命令行模式 :在vi编辑器中将光标放在函数上,shift + k 可直接man手册 ...
- HTTP Headers解析
什么是HTTP Headers? 它包含了哪些内容? 利用requests.get()函数对豆瓣读书进行请求, 返回的r.headers如下所示: >>> import reques ...
- xgboost gbdt特征点分烈点
lightGBM与XGBoost的区别:(来源于:http://baijiahao.baidu.com/s?id=1588002707760744935&wfr=spider&for= ...
- TObject、TPersisent 、TComponent、TControl、TGraphicControl、TWinControl 关系图
VCL的类图结构 TObject | TPersisent | ...
- CF1030A 【In Search of an Easy Problem】
题目巨简单,主要是给大家翻译一下 给n个数,其中存在1就输出HARD,否则输出EASY,不区分大小写 #include<iostream> #include<cstdio> u ...
- hdu 5943(素数间隔+二分图匹配)
Kingdom of Obsession Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Oth ...
- Python线程和进程
一.进程 程序并不能单独和运行只有将程序装载到内存中,系统为他分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别在于:程序是指令的集合,它是进程的静态描述文本:进程是程序的一次执行活动, ...