/
mc_con.py
executable file
·234 lines (213 loc) · 6.93 KB
/
mc_con.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
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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#!/usr/bin/python
from socket import socket, AF_INET,SOCK_DGRAM,IPPROTO_UDP,SOL_SOCKET,SO_REUSEADDR,IP_MULTICAST_TTL,IP_MULTICAST_LOOP,INADDR_ANY,inet_aton,IP_ADD_MEMBERSHIP,IPPROTO_IP
import random
import sys,signal
import os
import struct
from select import select
from threading import Thread
from mc_remote import remoteParser
from mc_local import localParser
#defaults
GROUP = '224.110.42.23'
PORT = 42023
CLIENTNAME = "Bob Ross"
MCMESSAGE="/hello \"%s\" Says Hello! \"%s\""
# end defaults
var = None
class chatter():
'''
initializes a chat socket
'''
def __init__ ( self, group=GROUP, port=PORT, nick=CLIENTNAME):
'''
the chatter class provides a number of parameters
first is the multicast group, second is the ip and port
the nick
iplist provides a dictionary for mapping ip-addresses to a nick name
a local parser will be instanciated
the variable beep tells if the client should beep or not
'''
self.group=group
self.ip=None
self.port=port
self.nick=nick
self.iplist = {}
self.localParser= localParser(self)
self.beep = False
self.espeak = True
self.encoding = "utf-8"
self.gram= {}
self.files = {}
self.currdir = os.curdir
def send_mc(self,arg):
try:
arg = arg.encode(self.encoding)
self.s.sendto("%s" %
arg,0,(self.group,self.port))
except Exception as e:
print "IN send_mc:%s"%e
def gset(self,key,value):
"""
here you may want to add some persistency stuff
"""
self.gram[key] = value
def gget(self,key):
"""
persistency goes here
"""
return self.gram.get(key,None)
pass
def initSocket (self,rcv=1):
'''
Initializes a Multicast socket
additionally it will send an ip_add_membership
a random seed will be generated for the /hello message
'''
host = ''
self.s = socket(AF_INET,SOCK_DGRAM, IPPROTO_UDP)
self.s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
#s.setsockopt(SOL_SOCKET,socket.SO_REUSEPORT,1)
self.s.setsockopt(IPPROTO_IP, IP_MULTICAST_TTL, 32)
self.s.setsockopt(IPPROTO_IP,IP_MULTICAST_LOOP,1)
# just send or also recieve
if rcv==1:
self.s.bind((host,PORT))
mreq = struct.pack("4sl", inet_aton(GROUP), INADDR_ANY)
self.s.setsockopt(IPPROTO_IP,IP_ADD_MEMBERSHIP,mreq)
# anouncement message
# seeds
random.seed()
# generate seed
self.rnd = random.randint(42,23000)
self.send_mc(MCMESSAGE % ( self.nick, self.rnd))
self.localParser.nick((self.nick,))
def send(self,msg=""):
if msg.startswith('/'):
ret= self.localParser.parse(msg)
if ret is not None:
print ret
else:
"""
this is the default case
"""
self.send_mc("/echo %s" %msg)
def resolveNick(self,ip):
'''
Tries to resolve an ip-addres to a nickname via the
iplist dictionary. if the ip-address is unkown to the
local host, the ip will be returned
'''
return self.iplist[ip].nick if ip in self.iplist else ip
#########################
# boring stuff from here:
##########################
def threadedRecv(self,cl=None,killthreadfunct=None):
'''
cl is the class which should be started ( shall be a class extending Thread )
killthreadfunction is the function which we use to kill the thread
We need to know which the function is to kill the thread
'''
# thread into receiveloop
if cl==None:
self.rcvthread=printThread(self.s,self)
else:
self.rcvthread=cl
if killthreadfunct==None:
self.rcvthread.killfunct=self.rcvthread.requeststop
else:
self.rcvthread.killfunct=killthreadfunct
self.rcvthread.start()
def cleanup(self):
"""
public function, wrapper for _cleanup, which is used for
the signal and does what we need to have
"""
self._cleanup(1,2)
def _cleanup(self,sig,frame):
'''
cleans up the socket and kills the read-thread
'''
print "cleaning up"
# we need this to stop the thread
self.rcvthread.killfunct()
self.rcvthread.join(1)
self.s.close()
sys.exit()
class printThread(Thread):
'''
Printing thread which is able to stop
'''
def __init__(self,sock,chat):
'''
constructor for printing Loop
the chat is the reference to the original chat object
a remote parser will be instanciated
'''
Thread.__init__(self)
self.s=sock
self.stop=0
self.chat=chat
self.remoteParser= remoteParser(self.chat)
def run(self,*args):
'''
Thread function
It is able to stop via setting self.stop to 1
It will wait for a max of 1 second on a socket
via select.
If the text received is a command ( /bla )
it will be evaulated and eventually executed
'''
while 1:
# break if we do not want to loop on
if self.stop == 1:
print "stopping"
break
ready,output,exception = select([self.s],[],[],1) # try every second
for r in ready:
if r == self.s:
(data,addr) = self.s.recvfrom(1024)
try:
data = str(data.decode(self.chat.encoding))
except Exception as e:
print "FAIL:%s"%e
if data.startswith('/'):
ret=self.remoteParser.parse(data,addr)
if ret is not None:
try:
print "%s" %(ret)
except:
print "malformed return value"
else:
"""
default case : no /command
"""
print "%s: %s"%(self.chat.resolveNick(addr[0]),
data)
def requeststop(self):
'''
sets stop to 1
'''
self.stop=1
def main():
chat = chatter()
signal.signal(signal.SIGINT,chat._cleanup)
registerChat(chat)
chat.initSocket()
chat.threadedRecv()
while 1:
try:
msg = raw_input()
chat.send(msg)
except KeyboardInterrupt:
chat.cleanup()
break
def registerChat(chat):
""" registers ONE global variable, needed for the chat """
global glob
glob = chat
def cleanup(chat):
global glob
glob.cleanup()
if __name__ == "__main__":
main()