File: comm.py

package info (click to toggle)
mgltools-viewerframework 1.5.7-3
  • links: PTS, VCS
  • area: non-free
  • in suites: buster
  • size: 1,580 kB
  • sloc: python: 13,681; sh: 78; makefile: 10
file content (219 lines) | stat: -rw-r--r-- 8,177 bytes parent folder | download | duplicates (4)
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#############################################################################
#
# Author: Michel F. SANNER
#
# Copyright: M. Sanner TSRI 2000
#
#############################################################################
#
# $Header: /opt/cvs/python/packages/share1.5/ViewerFramework/comm.py,v 1.6 2009/03/24 18:11:18 sanner Exp $
#
# $Id: comm.py,v 1.6 2009/03/24 18:11:18 sanner Exp $
#


"""
COMM module

Author: Michel F. Sanner
Date: Oct 11 2000

This module implements the Comm class that provides bi-directional communication over sockets. The Comm object provides server functionality, accepting connections from multiple clients (each client is handled by a separate thread).  the comm object also provides client side functionality, allowing a Comm object to connect to an existing server.

A - Server side
After a Comm object has been created, the startServer method can be called to
create a socket (self.serverSocket) and find a free port to which this socket
will be bound. The port is stored in self.port

By calling the acceptClients(func, maxConnections) one can allow the server to accept connection from clients. 'func' will be called for each message sreceived from clients. FIXME: calling func should probably set a lock.
acceptClients works in its own thread. When a client connects, a new thread is started to handle input from this client (listenToClient()) and the client is added to the Comm's clients dictionary.

The client dictionary uses the client's name as a key and stores the socket and the address created by accept.

hangupClient(name, client) can be called to terminate a connection with a specific client.

sendToClients(message) can be used to send a string to all connected clients

B - Client side

A comm object can be used to connect to a running server using the connectToServer(self, host, port, func). host cane be a host name or an IP address (as string). If the connection is successful a new thread is started to listen to the server and 'func' will be called with all messages comming from that server.

disconnectFromServer(self, clientSocket) can be called to disconnect fropm a server. 'clientSocket' can be a socket or a 
"""
import sys, time
from socket import *
import thread, types

class Comm:

    def __init__(self):
        self.verbose = 1   # set to 0 to get rid of printing

        # server related members
        self.clients = {}  # dictionary of clients,
                           # key is the host name+address bound to the socket
                           # on the other end of the connection
                           # value is (conn, addr) tuple
        self.port = None   # port used by server to accept connections
        self.serverSocket = None  # socket used by server to accpet conenctions
        self.maxConnections = 5   # maximum number of conenctions allowed

        # client related members
        self.serverSockets = {}  # dictionary of servers, key is the server's
                                 #  name, value is the socket created for this
                                 # connection

    def getPort(self, socket, base=50000):
        """find the first free port above base"""
        port = base
        while(1):
            try:
                socket.bind( ( '', port))
                return port
            except:
                import traceback
                traceback.print_exc()
                port = port + 1
                print port
    ##
    ## server side
    def startServer(self, port=None):
        self.serverSocket = s = socket(AF_INET, SOCK_STREAM)
        if port is None:
            self.port = self.getPort(s)
        else:
            self.port = port

    def acceptClients(self, func, maxConnections=5):
        self.maxConnections = maxConnections
        self.serverSocket.listen(maxConnections)
        if self.verbose:
            print "server ready, listening to port ", self.port
        while 1:
            conn, addr = self.serverSocket.accept()
            name = gethostbyaddr(conn.getpeername()[0])[0] + str(addr[1])
            self.clients[name] = ( conn, addr )
            if self.verbose:
                print 'Connected by', name, '\n'
            thread.start_new(self.listenToClient, (name, conn, func))
            
    def listenToClient(self, name, client, func):
        while (1):
            data = client.recv(1024)
            if data=='':
                if self.verbose:
                    print 'Connection closed by client'
                self.hangupClient(client)
                return

            MSGLEN = int(data)
            #print 'FFFFF  client receving', MSGLEN
            msg = ''
            while len(msg) < MSGLEN:
                chunk = client.recv(MSGLEN-len(msg))
                if chunk == '':
                    if self.verbose:
                        print 'Connection closed by client'
                    self.hangupClient(client)
                    return
                msg = msg + chunk
            #print 'client received', len(msg)
            func(name, msg)

    def hangupClient(self, client):
        if type(client) == types.StringType:
            cl = self.clients[client][0]
        else:
            cl = client
            client = None
            for key, value in self.clients.items():
                if value[0]==cl:
                    client = key
                    break
            if client is None:
                raise ValueError, "client not found"
            
        cl.close()
        del self.clients[client]
        
    def sendToClients(self, message):
        """send a messaqe to all clients"""
        #print 'server sending to client', message
        for c in self.clients.values():
            print 'server sending', len(message), '%020d'%len(message)
            c[0].send('%020d'%len(message))
            c[0].send(message)
        

    def getClients(self):
        """send a messaqe to all clients"""
        return self.clients.keys()

    ##
    ## client side
    def connectToServer(self, host, port, func):
        """become a client of a server specified using host and port,
        func will be called to handle messages from server
        """
        host = gethostbyname(host)
        serverSocket = socket(AF_INET, SOCK_STREAM)
        serverSocket.connect( ( host, port))
        name = gethostbyaddr(serverSocket.getpeername()[0])[0]+str(port)
        self.serverSockets[name] = serverSocket
        thread.start_new(self.listenToServer, (name, serverSocket, func))

        
    def listenToServer(self, name, client, func):
        while (1):
            data = client.recv(20)
            print 'client receives', data
            MSGLEN = int(data)
            #print 'FFFFF  client receving', MSGLEN
            msg = ''
            while len(msg) < MSGLEN:
                chunk = client.recv(MSGLEN-len(msg))
                if chunk == '':
                    if self.verbose:
                        print 'Connection closed by server'
                        self.disconnectFromServer(client)
                        return
                msg = msg + chunk
            #print 'client received', len(msg)
            func(name, msg)


    def disconnectFromServer(self, server):
        if type(server) == types.StringType:
            cl = self.serverSockets[server]
        else:
            cl = server
            server = None
            for key, values in self.serverSockets.items():
                if values==cl:
                    server = key
                    break
            if server is None:
                raise ValueError, "server not found"
        cl.shutdown(2)
        cl.close()
        del self.serverSockets[server]

    def getServers(self):
        """send a messaqe to all servers"""
        return self.serverSockets.keys()


if __name__ == '__main__':
    com = Comm()
    com.startServer()
    #com.acceptClients()

    def foo1(client, data):
        print 'client %s sent> %s'%(client,data)

    thread.start_new(com.acceptClients, (foo1,))

    def foo(server, data):
        print 'server %s sent> %s'%(server,data)

    #com.connectToServer('', 50008, foo)