1
2
3
4 '''
5 This software is part of the TCPCom library.
6 It is Open Source Free Software, so you may
7 - run the code for any purpose
8 - study how the code works and adapt it to your needs
9 - integrate all or parts of the code in your own programs
10 - redistribute copies of the code
11 - improve the code and release your improvements to the public
12 However the use of the code is entirely your responsibility.
13 '''
14
15 from threading import Thread
16 import thread
17 import socket
18 import time
19 import sys
20
21 TCPCOM_VERSION = "1.15 - Feb. 15, 2016"
26 '''
27 Class that represents a TCP socket based server.
28 '''
29 isVerbose = False
30 PORT_IN_USE = "PORT_IN_USE"
31 CONNECTED = "CONNECTED"
32 LISTENING = "LISTENING"
33 TERMINATED = "TERMINATED"
34 MESSAGE = "MESSAGE"
35
36 - def __init__(self, port, stateChanged, isVerbose = False):
37 '''
38 Creates a TCP socket server that listens on TCP port
39 for a connecting client. The server runs in its own thread, so the
40 constructor returns immediately. State changes invoke the callback
41 onStateChanged().
42 @param port: the IP port where to listen (0..65535)
43 @param stateChange: the callback function to register
44 @param isVerbose: if true, debug messages are written to System.out, default: False
45 '''
46 Thread.__init__(self)
47 self.port = port
48 self.stateChanged = stateChanged
49 TCPServer.isVerbose = isVerbose
50 self.isClientConnected = False
51 self.terminateServer = False
52 self.isServerRunning = False
53 self.start()
54
56 TCPServer.debug("TCPServer thread started")
57 HOSTNAME = ""
58 self.conn = None
59 self.serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
60 self.serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
61 TCPServer.debug("Socket created")
62 try:
63 self.serverSocket.bind((HOSTNAME, self.port))
64 except socket.error as msg:
65 print "Fatal error while creating TCPServer: Bind failed.", msg[0], msg[1]
66 sys.exit()
67 try:
68 self.serverSocket.listen(10)
69 except:
70 print "Fatal error while creating TCPServer: Port", self.port, "already in use"
71 try:
72 self.stateChanged(TCPServer.PORT_IN_USE, str(self.port))
73 except Exception, e:
74 print "Caught exception in TCPServer.PORT_IN_USE:", e
75 sys.exit()
76
77 try:
78 self.stateChanged(TCPServer.LISTENING, str(self.port))
79 except Exception, e:
80 print "Caught exception in TCPServer.LISTENING:", e
81
82 self.isServerRunning = True
83
84 while True:
85 TCPServer.debug("Calling blocking accept()...")
86 conn, self.addr = self.serverSocket.accept()
87 if self.terminateServer:
88 self.conn = conn
89 break
90 if self.isClientConnected:
91 TCPServer.debug("Returning form blocking accept(). Client refused")
92 try:
93 conn.shutdown(socket.SHUT_RDWR)
94 except:
95 pass
96 conn.close()
97 continue
98 self.conn = conn
99 self.isClientConnected = True
100 self.socketHandler = ServerHandler(self)
101 self.socketHandler.setDaemon(True)
102 self.socketHandler.start()
103 try:
104 self.stateChanged(TCPServer.CONNECTED, self.addr[0])
105 except Exception, e:
106 print "Caught exception in TCPServer.CONNECTED:", e
107 self.conn.close()
108 self.serverSocket.close()
109 self.isClientConnected = False
110 try:
111 self.stateChanged(TCPServer.TERMINATED, "")
112 except Exception, e:
113 print "Caught exception in TCPServer.TERMINATED:", e
114 self.isServerRunning = False
115 TCPServer.debug("TCPServer thread terminated")
116
118 '''
119 Closes the connection and terminates the server thread.
120 Releases the IP port.
121 '''
122 TCPServer.debug("Calling terminate()")
123 if not self.isServerRunning:
124 TCPServer.debug("Server not running")
125 return
126 self.terminateServer = True
127 TCPServer.debug("Disconnect by a dummy connection...")
128 if self.conn != None:
129 self.conn.close()
130 self.isClientConnected = False
131 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
132 client_socket.connect(('localhost', self.port))
133
135 '''
136 Closes the connection with the client and enters
137 the LISTENING state
138 '''
139 TCPServer.debug("Calling Server.disconnect()")
140 if self.isClientConnected:
141 self.isClientConnected = False
142 try:
143 self.stateChanged(TCPServer.LISTENING, str(self.port))
144 except Exception, e:
145 print "Caught exception in TCPServer.LISTENING:", e
146 TCPServer.debug("Shutdown socket now")
147 try:
148 self.conn.shutdown(socket.SHUT_RDWR)
149 except:
150 pass
151 self.conn.close()
152
154 '''
155 Sends the information msg to the client (as String, the character \0 (ASCII 0) serves as end of
156 string indicator, it is transparently added and removed)
157 @param msg: the message to send
158 '''
159 TCPServer.debug("sendMessage() with msg: " + msg)
160 if not self.isClientConnected:
161 TCPServer.debug("Not connected")
162 return
163 try:
164 self.conn.sendall(msg + "\0")
165 except:
166 TCPClient.debug("Exception in sendMessage()")
167
169 '''
170 Returns True, if a client is connected to the server.
171 @return: True, if the communication link is established
172 '''
173 return self.isClientConnected
174
176 '''
177 Returns True, if the server is in TERMINATED state.
178 @return: True, if the server thread is terminated
179 '''
180 return self.terminateServer
181
182 @staticmethod
186
187 @staticmethod
189 '''
190 Returns the library version.
191 @return: the current version of the library
192 '''
193 return TCPCOM_VERSION
194
198 Thread.__init__(self)
199 self.server = server
200
202 TCPServer.debug("ServerHandler started")
203 bufSize = 4096
204 try:
205 while True:
206 data = ""
207 reply = ""
208 isRunning = True
209 while not reply[-1:] == "\0":
210 TCPServer.debug("Calling blocking conn.recv()")
211 reply = self.server.conn.recv(bufSize)
212 if reply == None or len(reply) == 0:
213 TCPServer.debug("conn.recv() returned None")
214 isRunning = False
215 break
216 data += reply
217 if not isRunning:
218 break
219 TCPServer.debug("Received msg: " + data + " len: " + str(len(data)))
220 junk = data.split("\0")
221
222 for i in range(len(junk) - 1):
223 try:
224 self.server.stateChanged(TCPServer.MESSAGE, junk[i])
225 except Exception, e:
226 print "Caught exception in TCPServer.MESSAGE:", e
227 except:
228 TCPServer.debug("Exception from blocking conn.recv(), Msg: " + str(sys.exc_info()[0]) + \
229 " at line # " + str(sys.exc_info()[-1].tb_lineno))
230
231 self.server.disconnect()
232 TCPServer.debug("ServerHandler terminated")
233
238 '''
239 Class that represents a TCP socket based client.
240 '''
241 isVerbose = False
242 CONNECTING = "CONNECTING"
243 SERVER_OCCUPIED = "SERVER_OCCUPIED"
244 CONNECTION_FAILED = "CONNECTION_FAILED"
245 CONNECTED = "CONNECTED"
246 DISCONNECTED = "DISCONNECTED"
247 MESSAGE = "MESSAGE"
248
249 - def __init__(self, ipAddress, port, stateChanged, isVerbose = False):
250 '''
251 Creates a TCP socket client prepared for a connection with a
252 TCPServer at given address and port.
253 @param host: the IP address of the host
254 @param port: the IP port where to listen (0..65535)
255 @param stateChanged: the callback function to register
256 @param isVerbose: if true, debug messages are written to System.out
257 '''
258 self.isClientConnected = False
259 self.isClientConnecting = False
260 self.ipAddress = ipAddress
261 self.port = port
262 self.stateChanged = stateChanged
263 self.checkRefused = False
264 self.isRefused = False
265
266 TCPClient.isVerbose = isVerbose
267
269 '''
270 Sends the information msg to the server (as String, the character \0
271 (ASCII 0) serves as end of string indicator, it is transparently added
272 and removed). For responseTime > 0 the method blocks and waits
273 for maximum responseTime seconds for a server reply.
274 @param msg: the message to send
275 @param responseTime: the maximum time to wait for a server reply (in s)
276 @return: the message or null, if a timeout occured
277 '''
278 TCPClient.debug("sendMessage() with msg = " + msg)
279 if not self.isClientConnected:
280 TCPClient.debug("sendMessage(): Connection closed.")
281 return None
282 reply = None
283 try:
284 msg += "\0";
285 rc = self.sock.sendall(msg)
286 if responseTime > 0:
287 reply = self._waitForReply(responseTime)
288 except:
289 TCPClient.debug("Exception in sendMessage()")
290 self.disconnect()
291
292 return reply
293
295 TCPClient.debug("Calling _waitForReply()")
296 self.receiverResponse = None
297 startTime = time.time()
298 while self.isClientConnected and self.receiverResponse == None and time.time() - startTime < responseTime:
299 time.sleep(0.01)
300 if self.receiverResponse == None:
301 TCPClient.debug("Timeout while waiting for reply")
302 else:
303 TCPClient.debug("Response = " + self.receiverResponse + " time elapsed: " + str(int(1000 * (time.time() - startTime))) + " ms")
304 return self.receiverResponse
305
307 '''
308 Creates a connection to the server (blocking until timeout).
309 @param timeout: the maximum time (in s) for the connection trial (0: for default timeout)
310 @return: True, if the connection is established; False, if the server
311 is not available or occupied
312 '''
313 if timeout == 0:
314 timeout = None
315 try:
316 self.stateChanged(TCPClient.CONNECTING, self.ipAddress + ":" + str(self.port))
317 except Exception, e:
318 print "Caught exception in TCPClient.CONNECTING:", e
319 try:
320 self.isClientConnecting = True
321 host = (self.ipAddress, self.port)
322 if self.ipAddress == "localhost" or self.ipAddress == "127.0.0.1":
323 timeout = None
324 self.sock = socket.create_connection(host, timeout)
325 self.sock.settimeout(None)
326 self.isClientConnecting = False
327 self.isClientConnected = True
328 except:
329 self.isClientConnecting = False
330 try:
331 self.stateChanged(TCPClient.CONNECTION_FAILED, self.ipAddress + ":" + str(self.port))
332 except Exception, e:
333 print "Caught exception in TCPClient.CONNECTION_FAILED:", e
334 TCPClient.debug("Connection failed.")
335 return False
336 ClientHandler(self)
337
338
339 self.checkRefused = True
340 self.isRefused = False
341 startTime = time.time()
342 while time.time() - startTime < 2 and not self.isRefused:
343 time.sleep(0.001)
344 if self.isRefused:
345 TCPClient.debug("Connection refused")
346 try:
347 self.stateChanged(TCPClient.SERVER_OCCUPIED, self.ipAddress + ":" + str(self.port))
348 except Exception, e:
349 print "Caught exception in TCPClient.SERVER_OCCUPIED:", e
350 return False
351
352 try:
353 self.stateChanged(TCPClient.CONNECTED, self.ipAddress + ":" + str(self.port))
354 except Exception, e:
355 print "Caught exception in TCPClient.CONNECTED:", e
356 TCPClient.debug("Successfully connected")
357 return True
358
360 '''
361 Closes the connection with the server.
362 '''
363 TCPClient.debug("Client.disconnect()")
364 if not self.isClientConnected:
365 TCPClient.debug("Connection already closed")
366 return
367 self.isClientConnected = False
368 TCPClient.debug("Closing socket")
369 try:
370 self.sock.shutdown(socket.SHUT_RDWR)
371 except:
372 pass
373 self.sock.close()
374
376 '''
377 Returns True during a connection trial.
378 @return: True, while the client tries to connect
379 '''
380 return self.isClientConnecting
381
383 '''
384 Returns True of client is connnected to the server.
385 @return: True, if the connection is established
386 '''
387 return self.isClientConnected
388
389 @staticmethod
393
394 @staticmethod
396 '''
397 Returns the library version.
398 @return: the current version of the library
399 '''
400 return TCPCOM_VERSION
401
405 Thread.__init__(self)
406 self.client = client
407 self.start()
408
410 TCPClient.debug("ClientHandler thread started")
411 while True:
412 try:
413 junk = self.readResponse().split("\0")
414
415
416 for i in range(len(junk) - 1):
417 try:
418 self.client.stateChanged(TCPClient.MESSAGE, junk[i])
419 except Exception, e:
420 print "Caught exception in TCPClient.MESSAGE:", e
421 except:
422 TCPClient.debug("Exception in readResponse() Msg: " + str(sys.exc_info()[0]) + \
423 " at line # " + str(sys.exc_info()[-1].tb_lineno))
424 if self.client.checkRefused:
425 self.client.isRefused = True
426 break
427 try:
428 self.client.stateChanged(TCPClient.DISCONNECTED, "")
429 except Exception, e:
430 print "Caught exception in TCPClient.DISCONNECTED:", e
431 TCPClient.debug("ClientHandler thread terminated")
432
434 TCPClient.debug("Calling readResponse")
435 bufSize = 4096
436 data = ""
437 while not data[-1:] == "\0":
438 try:
439 reply = self.client.sock.recv(bufSize)
440 if len(reply) == 0:
441 TCPClient.debug("recv returns null length")
442 raise Exception("recv returns null length")
443 except:
444 TCPClient.debug("Exception from blocking conn.recv(), Msg: " + str(sys.exc_info()[0]) + \
445 " at line # " + str(sys.exc_info()[-1].tb_lineno))
446 raise Exception("Exception from blocking sock.recv()")
447 data += reply
448 self.receiverResponse = data[:-1]
449 return data
450