服务器通信初试-tcp通信

本文最后更新于:2024年3月7日 下午


其实很早就想自己整个服务器什么的了,不过一直没有什么迫切的需求,所以也一直搁置,前几天发现上学期拜托马老师在旁边整的linux一直没用上,所以就心血来潮整了整。
也是蛮简单的一个东西,就是写个TCP接收的服务,再做个内网穿透,就能把你的机房电脑变成免费服务器了(bushi)


TCP服务器接收程序

TCP协议我在计算机网络中曾介绍过。就不详细展开了。
python代码如下:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

import socket,threading,time
# 存储连接到服务器的客户端的dir,键是客户端ID,值是 client 对象
clients = {}
# 定义客户端类,包含 socket 连接、IP地址、端口号、用户名等信息
class client(object):
def __init__(self,socket,addr,username):
self.addr = addr[0] # IP地址
self.port = addr[1] # 端口号
self.username = username # 用户名
self.socket=socket # socket 连接对象

def sendMsg(self,msg,username,admin):# 发送消息
try:
if admin: # 判断是否是管理员
self.socket.send(("%s %s(管理员): %s" % (self.getTime(), username, msg)).encode("utf-8"))
else:
self.socket.send(("%s %s: %s" %(self.getTime(), username, msg)).encode("utf-8"))
return True
except:
return False

def recv(self,mtu=1024):# 接收消息的
try:
data = self.socket.recv(mtu).decode("utf-8")
if data == "-!-quit-!-" or not data:
return False
return data
except:
return False

def close(self): # 关闭连接
try:
self.socket.close()
return True
except:
return False

def getId(self):# 获取客户端ID的方法
return "%s-%s" % (self.addr,self.port)
def getTime(self):# 获取当前时间=
return str(time.strftime("%Y-%m-%d %H:%M:%S"))

def new_client(c):# 新客户端加入聊天室时的处理函数
try:
print("%s(%s) 尝试连接" %(c.addr,c.port))
data = c.recv()# 接收客户端发送的用户名
if not data:
return
if len(data) >= 16:
c.socket.send("用户名太长了")
return
c.username = data
print("用户%s %s(%s)已连接" %(c.username,c.addr,c.port))
c.socket.send("已连接".encode("utf-8"))
while True:
data = c.recv()
if not data:
break
else:
print("用户%s %s(%s) 发送了: %s" % (c.username,c.addr, c.port, data))
broadcast(data,c.username)

except socket.errno as e:
print("Socket error: %s" % str(e))
except Exception as e:
print("Other exception: %s" % str(e))
finally:# 输出断开连接的信息,并从字典中删除该客户端
print("%s(%s) 断开连接" % (c.addr, c.port))
c.close()
clients.pop(c.getId())

def broadcast(msg,username,admin=False):
for c in clients.values():
c.sendMsg(msg,username,admin)

def start_server(port):#在本地端口上开放连接
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

host = "192.168.153.1"
server.bind((host, port))

# 监听客户端
server.listen(10)
print("服务器已开启,正在监听{}".format(server.getsockname()))

while True:
# 接受客户端连接
conn, addr = server.accept()
c = client(conn,addr,"")
clients[c.getId()] = c
t = threading.Thread(target=new_client, args=(c,))#为每个用户新创建一个线程。
t.start()


if __name__ == "__main__":
start_server(23333)
print("服务器已关闭")

内网穿透服务

内网穿透是一种网络技术,可以让外网用户通过公网访问内网中的服务。通常情况下,内网中的设备无法直接从公网访问,因为它们位于私有 IP 地址空间内,无法直接被公网访问到。而内网穿透技术通过一定的方法,可以将公网请求转发到内网设备上,实现公网用户访问内网服务的目的。

内网穿透可以通过多种方式实现,其中比较常见的是端口映射和反向代理。

端口映射
端口映射是指将公网端口和内网端口进行映射,将公网请求发送到公网端口,然后通过映射将请求转发到内网端口上。这种方式需要在公网服务器上安装内网穿透软件,通常还需要配置一些参数和规则来实现转发。

反向代理
反向代理是指将公网请求发送到反向代理服务器上,然后由反向代理服务器将请求转发到内网服务器上,并将响应返回给公网用户。这种方式需要在公网服务器上安装反向代理软件,将公网请求转发到内网服务器上。
可以简单理解为由于内网服务器没有一个公网的地址,公网用户只能先发消息到一个公网的服务器作为中间站,然后再由中间站转发到你内网中的服务器中。

我用的是SAKURA FRP的反向代理,仅仅是tcp的话是免费的。之前用的是花生壳的不知道为啥过一段时间就掉,这个还相对稳定些。
需要在服务器上下载它的客户端,注册后会给你个密钥,在设备上下好后使用密钥启动隧道。
它本身的教程很详细,再此就不多赘述

成果

服务器打开后,可以看到可以接收到从客户端发来的数据。
能接收客户端发送过来的数据
客户端也可以收到服务器的反馈(只要是写可以tcp通信的服务都可以作为客户端),以下是我用来测试的客户端代码和示例:

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
import socket

# 创建一个socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接服务器
server_addr = ('你的服务器地址', 你的服务器端口)
s.connect(server_addr)

while True:
# 输入要发送的数据
send_data = input("请输入要发送的数据:")
if send_data == 'exit':
s.send(send_data.encode())
break
# 发送数据
send_data+='@'
print(send_data,'676767')
#中间测试用,以上这两句可以删((
s.send(send_data.encode())
recv_data=s.recv(1024).decode("utf-8")
print(recv_data)

s.close()

能接收服务器发送过来的数据