Repository navigation
Expand file tree
/
Copy pathserver.py
More file actions
128 lines (112 loc) · 3.61 KB
/
Copy pathserver.py
File metadata and controls
128 lines (112 loc) · 3.61 KB
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""
Tiny relay server with LAN auto-discovery.
Run:
python server.py
"""
import socket
import threading
import struct
import pickle
import time
RELAY_HOST = "0.0.0.0"
RELAY_PORT = 9000
BROADCAST_PORT = 54545
BROADCAST_TOKEN = b"VIDEO_RELAY"
BROADCAST_INTERVAL = 1.0
def recv_exact(sock, size: int):
"""Receive exactly size bytes or return None."""
data = b""
while len(data) < size:
chunk = sock.recv(size - len(data))
if not chunk:
return None
data += chunk
return data
def start_beacon(relay_port: int):
def _beacon():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
msg = BROADCAST_TOKEN + b" " + str(relay_port).encode()
while True:
try:
sock.sendto(msg, ("255.255.255.255", BROADCAST_PORT))
except Exception:
pass
time.sleep(BROADCAST_INTERVAL)
t = threading.Thread(target=_beacon, daemon=True)
t.start()
class RelayServer:
def __init__(self, host=RELAY_HOST, port=RELAY_PORT):
self.host = host
self.port = port
self.running = False
self.server_socket = None
self.clients = []
self.lock = threading.Lock()
def broadcast(self, sender, payload: bytes):
header = struct.pack("!I", len(payload))
with self.lock:
targets = [c for c in self.clients if c is not sender]
for c in targets:
try:
c.sendall(header + payload)
except Exception:
pass
def handle(self, sock, addr):
try:
while self.running:
raw = recv_exact(sock, 4)
if not raw:
break
size = struct.unpack("!I", raw)[0]
data = recv_exact(sock, size)
if not data:
break
self.broadcast(sock, data)
finally:
with self.lock:
if sock in self.clients:
self.clients.remove(sock)
try:
sock.close()
except:
pass
def start(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(10)
self.running = True
print(f"Relay listening on {self.host}:{self.port}")
start_beacon(self.port)
print(f"Broadcasting beacon on UDP 255.255.255.255:{BROADCAST_PORT}")
try:
while True:
client, addr = self.server_socket.accept()
client.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
with self.lock:
self.clients.append(client)
threading.Thread(target=self.handle, args=(client, addr), daemon=True).start()
except KeyboardInterrupt:
pass
finally:
self.stop()
def stop(self):
self.running = False
if self.server_socket:
try:
self.server_socket.close()
except:
pass
with self.lock:
sockets = list(self.clients)
self.clients.clear()
for s in sockets:
try:
s.close()
except:
pass
print("Relay stopped.")
if __name__ == "__main__":
RelayServer().start()