python - Python打孔发送syn,但不接收syn_ack和ack
问题描述
我有三台debian pc。一个是 stun 服务器,另外两个是客户端。我需要让这些客户端建立对等 tcp 连接。对于这种情况,我正在使用这个 github 项目。
这是服务器端代码:
#!/usr/bin/env python
import sys
import logging
import socket
import struct
import fcntl
import os
from util import *
logger = logging.getLogger()
clients = {}
def main(host='0.0.0.0', port=5005):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(1)
s.settimeout(30)
while True:
try:
conn, addr = s.accept()
except socket.timeout:
continue
logger.info('connection address: %s', addr)
data = recv_msg(conn)
priv_addr = msg_to_addr(data)
send_msg(conn, addr_to_msg(addr))
data = recv_msg(conn)
data_addr = msg_to_addr(data)
if data_addr == addr:
logger.info('client reply matches')
clients[addr] = Client(conn, addr, priv_addr)
else:
logger.info('client reply did not match')
conn.close()
logger.info('server - received data: %s', data)
if len(clients) == 2:
(addr1, c1), (addr2, c2) = clients.items()
logger.info('server - send client info to: %s', c1.pub)
send_msg(c1.conn, c2.peer_msg())
logger.info('server - send client info to: %s', c2.pub)
send_msg(c2.conn, c1.peer_msg())
clients.pop(addr1)
clients.pop(addr2)
conn.close()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
main(*addr_from_args(sys.argv))
在其目录中有此 util.py :
import struct
from collections import namedtuple
def addr_from_args(args, host='127.0.0.1', port=9999):
if len(args) >= 3:
host, port = args[1], int(args[2])
elif len(args) == 2:
host, port = host, int(args[1])
else:
host, port = host, port
return host, port
def msg_to_addr(data):
ip, port = data.decode('utf-8').strip().split(':')
return (ip, int(port))
def addr_to_msg(addr):
return '{}:{}'.format(addr[0], str(addr[1])).encode('utf-8')
def send_msg(sock, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
def recvall(sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = b''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
def recv_msg(sock):
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
class Client(namedtuple('Client', 'conn, pub, priv')):
def peer_msg(self):
return addr_to_msg(self.pub) + b'|' + addr_to_msg(self.priv)
这是客户端的代码,具有相同的 util.py 但不同的 ip,服务器端的 ip 是服务器的本地 ip,客户端是服务器的公共 ip:
#!/usr/bin/env python
import sys
import logging
import socket
import struct
from threading import Event, Thread
from util import *
logger = logging.getLogger('client')
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
STOP = Event()
def accept(port):
logger.info("accept %s", port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.bind(('', port))
s.listen(1)
s.settimeout(5)
while not STOP.is_set():
try:
conn, addr = s.accept()
except socket.timeout:
continue
else:
logger.info("Accept %s connected!", port)
# STOP.set()
def connect(local_addr, addr):
logger.info("connect from %s to %s", local_addr, addr)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.bind(local_addr)
while not STOP.is_set():
try:
s.connect(addr)
except socket.error:
continue
# except Exception as exc:
# logger.exception("unexpected exception encountered")
# break
else:
logger.info("connected from %s to %s success!", local_addr, addr)
# STOP.set()
def main(host='54.187.46.146', port=5005):
sa = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sa.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sa.connect((host, port))
priv_addr = sa.getsockname()
send_msg(sa, addr_to_msg(priv_addr))
data = recv_msg(sa)
logger.info("client %s %s - received data: %s", priv_addr[0], priv_addr[1], data)
pub_addr = msg_to_addr(data)
send_msg(sa, addr_to_msg(pub_addr))
data = recv_msg(sa)
pubdata, privdata = data.split(b'|')
client_pub_addr = msg_to_addr(pubdata)
client_priv_addr = msg_to_addr(privdata)
logger.info(
"client public is %s and private is %s, peer public is %s private is %s",
pub_addr, priv_addr, client_pub_addr, client_priv_addr,
)
threads = {
'0_accept': Thread(target=accept, args=(priv_addr[1],)),
'1_accept': Thread(target=accept, args=(client_pub_addr[1],)),
'2_connect': Thread(target=connect, args=(priv_addr, client_pub_addr,)),
'3_connect': Thread(target=connect, args=(priv_addr, client_priv_addr,)),
}
for name in sorted(threads.keys()):
logger.info('start thread %s', name)
threads[name].start()
while threads:
keys = list(threads.keys())
for name in keys:
try:
threads[name].join(1)
except TimeoutError:
continue
if not threads[name].is_alive():
threads.pop(name)
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, message='%(asctime)s %(message)s')
main(*addr_from_args(sys.argv))
所以这就是问题所在:首先我运行服务器,然后运行一个客户端,3秒后我将运行另一个客户端,我总是正常的日志,如:
2020-12-07 17:00:24,020 - client 172.20.10.5 36102 - received data: b'5.17.81.184:65333'
2020-12-07 17:00:24,473 - client public is ('5.17.81.184', 65333) and private is ('172.20.10.5', 36102), peer public is ('25.9.153.68', 52059) private is ('192.168.1.3', 45248)
2020-12-07 17:00:24,473 - start thread 0_accept
2020-12-07 17:00:24,474 - accept 36102
2020-12-07 17:00:24,474 - start thread 1_accept
2020-12-07 17:00:24,475 - accept 52059
2020-12-07 17:00:24,475 - start thread 2_connect
2020-12-07 17:00:24,476 - connect from ('172.20.10.5', 36102) to ('25.9.153.68', 52059)
2020-12-07 17:00:24,476 - start thread 3_connect
2020-12-07 17:00:24,477 - connect from ('172.20.10.5', 36102) to ('192.168.1.3', 45248)
但是当采取 netstat 我会看到 syn-sent,我应该怎么做才能采取 syn-ack 并在那个 ack 之后?代码有问题吗?或者有什么我看不到的?
解决方案
推荐阅读
- events - Vue.js 自定义事件问题
- vue.js - 如何将数据从 Api 绑定到 Vue 组件?
- mysql - MySql 更新后触发器执行顺序
- windows - Windows 右键单击上下文菜单项数量限制
- android - 如何擦除未启动的 AVD 数据
- javascript - Rails 中的实时页面查看计数器
- google-sheets - 将加数限制为具有加法的单元格的特定值?
- java - JAVA 和 XML:com.sun.org.apache.xpath.internal.XPathException:无法将#STRING 转换为 NodeList
- vulkan - 我什么时候可以释放传递给 vulkan vkCreateXXX 函数的资源和结构?
- assembly - MIPS 32 位,汇编程序:push 和 pop