Python基于UDP的通讯

最近准备改造以前的一个项目,这个项目是我从半道接手的,消息发送机制是客户端监听自己的10001端口,然后服务端想发送消息的时候,像客户端的10001端口发送socket信息(基于TCP的),从而实现通讯,这样的机制弊端太多了,公司内部有很严格的ACL策略,为此还专门把服务器设置成与所有机器啊的10001端口胡同,这次仅是准备改造,前期研究了一下技术。

也因为这个项目想要放到外网,所以先前的机制根本行不通,所以新机制的一个方案是,基于UDP的通讯,由客户端主动请求服务端,并且定期发送心跳包,为nat下的设备打开通路,服务器监听客户端发来的请求并入库,记录下nat设备上打的洞的IP和端口,在需要发送消息的时候直接回执就行了

可惜实际测试不容乐观,UDP过NAT设备后的丢包率太严重了,如果不仅过NAT设备,则几乎不会丢包。

So,最终结论,要不然用UDP模拟实现TCP的验证机制,要不然就得用TCP来做,不想用TCP来做其实是不想做TCP的长连接,还是感觉无状态比较靠谱,如果定期发送TCP的心跳包不知道行不行,这个到时候在尝试一下。

上一下Python的Server 和 Client

Server.py

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#! encoding:utf-8
import socket
import sys
import threading
import MySQLdb
import sys,time
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('124.193.159.6', 6500))

def Input(s):
global adpol,cur
while True:
ipnub = raw_input()
ishave = cur.execute("select ip,port from perminal_new.cstest where user = '"+ipnub+"' limit 1")
if(ishave>0):
print "please input contents!"
cont = raw_input()
check = cur.fetchone()
print check[0],check[1]
s.sendto(cont,(check[0],int(check[1])))

def Show(s):
global adpol,cur
while True:
try:
data, address = s.recvfrom(1024)
if address not in adpol:
adpol.append(address)
cur.execute("UPDATE perminal_new.cstest set ip = '"+address[0]+"' ,port = '"+str(address[1])+"' , updatetime = now() where user = "+str(int(data)))
#s.sendto("Check OK!["+time.strftime('%H:%M:%S')+"]",address)
s.sendto("OK",address)
except Exception,e:
print e

conn = MySQLdb.connect(host='127.0.0.1',user='root',passwd='1q2w3e4r',port=3306)
cur=conn.cursor()
adpol = []
thin = threading.Thread(target = Input, args = (sock,))
thin.start()
thout = threading.Thread(target = Show, args = (sock,))
thout.start()

Client.py

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
27
28
29
30
31
32
33
34
35
36
# -*- coding: utf-8 -*-
import socket
import threading,time

inString = ''
outString = ''
nick = ''

def Input(s):
global nick, outString,ipaddress
while True:
#outString = raw_input()
outString = "1"
s.sendto(outString,ipaddress)
time.sleep(30)

def Show(s):
global inString
while True:
try:
inString = s.recv(1024)
if not inString:
break
if outString != inString:
print inString
except:
break

ipaddress = ('124.193.159.6', 6500)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect(ipaddress)

thin = threading.Thread(target = Input, args = (sock,))
thin.start()
thout = threading.Thread(target = Show, args = (sock,))
thout.start()
1
2
3
4
5
6
CREATE TABLE `cstest` (
`user` int(11) NOT NULL,
`ip` varchar(15) NOT NULL,
`port` int(11) NOT NULL,
`updatetime` datetime DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8

因为是测试技术用的,所以就代码很多参数写死了,如果自己想测,那就在2次加工一下吧。