-
Notifications
You must be signed in to change notification settings - Fork 0
/
tcpClient.py
executable file
·137 lines (116 loc) · 4.45 KB
/
tcpClient.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
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
129
130
131
132
133
134
135
136
137
"""
----------------------------------------------------------------------------
"THE BEER-WARE LICENSE"
As long as you retain this notice you can do whatever you want with this
stuff. If you meet an employee from Windward some day, and you think this
stuff is worth it, you can buy them a beer in return. Windward Studios
----------------------------------------------------------------------------
"""
import threading
import socket as sock
from collections import deque
from debug import trap, bugprint
BUFFER_SIZE = 65536 * 4
PORT = 1707
class TcpClient(threading.Thread):
"""Threaded socket wrapper that sends and receives data from the server."""
def __init__(self, host, callback):
threading.Thread.__init__(self)
socket = sock.socket(sock.AF_INET, sock.SOCK_STREAM, sock.IPPROTO_TCP)
bugprint(host, PORT)
socket.connect((host, PORT))
#socket.settimeout(.5)
self.socket = socket
self.receiver = Receiver((host, PORT), socket, self)
self.callback = callback
self.running = True
def run(self):
bugprint("TcpClient running...")
self.receiver.start()
input = self.receiver.input
while self.running:
if len(input):
self.callback.incomingMessage(input.popleft())
# time.sleep(.25)
self.socket.close()
def sendMessage(self, message):
# compute the length of the message (4 byte, little-endian)
length = len(message)
hexlen = "{:08x}".format(length)
assert len(hexlen) == 8 # length should not be more than 4 bytes
chrstr = [chr(int(hexlen[i:i + 2], 16)) for i in range(0, 8, 2)]
chrstr.reverse()
retlen = ''.join(chrstr)
try:
#send the length
self.socket.send(retlen) # fix this
# send the message
ret = self.socket.send(message)
while ret < length:
ret += self.socket.send(message[ret:])
assert ret == length
except sock.timeout: # fix this
print("Socket operation (send) timed out")
self.sendMessage(message)
def connectionLost(self, err):
self.callback.connectionLost(err)
def close(self):
self.receiver.running = False
self.running = False
class Receiver(threading.Thread):
'''Waits in a separate thread for data from the server.'''
def __init__(self, address, socket, callback):
threading.Thread.__init__(self)
self.callback = callback
self.socket = socket
self.input = deque()
self.running = True
def run(self):
bugprint("Receiver running...")
socket = self.socket
input = self.input
while self.running:
data = getData(socket, self)
while data is None:
data = getData(socket, self)
end = data.rfind('>')
assert end > 0
data = data[:end + 1] # strip ending nonsense C# bogus banana characters
input.append(data)
socket.close()
def connectionLost(self, err):
self.callback.connectionLost(err)
def getData(socket, callback):
try:
# compute the length of the message (4 byte, little-endian)
recstr = socket.recv(4)
while len(recstr) < 4:
recstr += socket.recv(4 - len(recstr))
assert len(recstr) == 4
lenstr = None
lenstr = ["{:02x}".format(ord(str(c))) for c in recstr]
lenstr.reverse()
length = int(''.join(lenstr), 16)
# receive message into buffer
data = socket.recv(length)
received = len(data)
buff = []
while received < length:
buff.append(data)
data = socket.recv(length - received)
received += len(data)
else:
assert received == length
if buff:
buff.append(data)
data = ''.join(buff)
return data
except sock.timeout:
trap("Socket operation (receive) timed out")
return None
except sock.error as err: # fix this
if err.errno == 10054: # The connection has been reset.
callback.connectionLost(err)
else:
print("WARNING - socket error on receive: " + str(err)) # fix this BROKEN
raise err