转载请声明出处:http://www.cnblogs.com/kevince/p/3941728.html ——By Kevince
ii系列工具第三弹,命令行下的局域网聊天程序
原理:
程序启动时向全网(255.255.255.255)BACKPORT端口广播自己的主机名以及状态(上线)。
如果接受收到的上线状态,则将其加入通信列表,同时返还一个数据包,使自己也将对面加入其通信列表。
程序退出时向全网广播自己的下线状态,如果收到该下线状态则将其从自己的通信列表中删除
为了防止在输入过程中被新输出的消息打断,可以用readlines中的get_line_buffer函数获取缓冲区内的字符并储存,清空原先行,输出新结果,并在下面输出刚刚输入的内容(可用curses改进,to be continued...)
缺陷:
使用UDP协议,未添加消息到达确认机制;
跨平台支持需要修改代码(readlines只支持linux, windows下要用pyreadline,MAC OS要用edlitline来代替)
实用性不强,功能单一,学习程序
未能实现GUI图形界面的开发(目前还木有学会……)
刚学Python没多久 且开发仓促,有Bug还请多多指教~
1 #!/usr/bin/python 2 #coding:utf8 3 #python 2.7.6 4 5 import threading 6 import socket 7 import time 8 import os 9 import sys 10 import signal 11 from readline import get_line_buffer 12 BUFSIZE = 1024 13 BACKPORT = 7789 #状态监听端口 14 CHATPORT = 7788 #聊天信息发送窗口 15 START = '>>' 16 INIT = '>>' 17 users = {} 18 ips = {} 19 #起到双向字典的作用,ip和name互相映射 20 21 #数据处理类(消息封装、分解) 22 class Data(): 23 def gettime(self): 24 return time.strftime('%Y-%m-%d %H:%M', time.localtime(time.time())) 25 def getip(self): 26 ip = os.popen("/sbin/ifconfig | grep 'inet addr' | awk '{print $2}'").read() 27 ip = ip[ip.find(':')+1:ip.find('\n')] 28 return ip 29 def handlebc(self, data): 30 data = data[5:] 31 res = data.split('#opt:') 32 return res 33 def makebc(self, name, switch): 34 data = 'name:%s#opt:%d' % (name, switch) 35 return data 36 def handlechat(self, data): 37 msg = '\n' + self.gettime() + '\n' +'from '+ data + '\n' 38 return msg 39 def makechat(self, data, name): 40 return name + ':' + data 41 42 #后台监听类 43 class Back(threading.Thread): 44 def __init__(self): 45 threading.Thread.__init__(self) 46 self.data = Data() 47 self.addrb = ('255.255.255.255', BACKPORT) 48 self.addrl = ('', BACKPORT) 49 self.name = socket.gethostname() 50 self.ip = self.data.getip() 51 self.thread_stop = False 52 def status(self, name, switch): 53 if switch == 0: 54 status = 'offline' 55 elif switch == 1: 56 status = 'online' 57 #用来处理输入过程中被线程返回消息打乱的情况 58 if outmutex.acquire(1): 59 sys.stdout.write('\r'+' '*(len(get_line_buffer())+len(START))+'\r') 60 print '[status] '+name+' '+status 61 sys.stdout.write(START+get_line_buffer()) 62 sys.stdout.flush() 63 outmutex.release() 64 def broadcast(self, switch): 65 bsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 66 bsock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) 67 data = self.data.makebc(self.name, switch) 68 bsock.sendto(data, self.addrb) 69 bsock.close() 70 def response(self, addr, switch): 71 rsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 72 data = self.data.makebc(self.name, switch) 73 rsock.sendto(data, (addr, BACKPORT)) 74 rsock.close() 75 def check(self): 76 if usermutex.acquire(): 77 ips.clear() 78 users.clear() 79 usermutex.release() 80 self.broadcast(1) 81 def run(self): 82 lsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 83 lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 84 lsock.bind(self.addrl) 85 self.broadcast(1) 86 while not self.thread_stop: 87 data, addr = lsock.recvfrom(BUFSIZE) 88 datalist = self.data.handlebc(data) 89 if usermutex.acquire(1): 90 if datalist[1] == '0': 91 if ips.has_key(addr[0]): 92 if anoun == 1: 93 self.status(datalist[0], 0) 94 del ips[addr[0]] 95 del users[datalist[0]] 96 elif datalist[1] == '1': 97 if anoun == 1 and datalist[0] != self.name: 98 self.status(datalist[0], 1) 99 users[datalist[0]] = addr[0] 100 ips[addr[0]] = datalist[0] 101 self.response(addr[0], 2) 102 elif datalist[1] == '2': 103 if anoun == 1 and datalist[0] != self.name: 104 self.status(datalist[0], 1) 105 users[datalist[0]] = addr[0] 106 ips[addr[0]] = datalist[0] 107 usermutex.release() 108 lsock.close() 109 def stop(self): 110 self.broadcast(0) 111 self.thread_stop = True 112 113 #聊天类 114 class Listen(threading.Thread): 115 def __init__(self): 116 threading.Thread.__init__(self) 117 self.addr = ('', CHATPORT) 118 self.name = socket.getfqdn(socket.gethostname()) 119 self.data = Data() 120 self.thread_stop = False 121 def ans(self, addr):#to be added 用来确认消息报的接受 122 return 123 def run(self): 124 lsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 125 lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 126 lsock.bind(self.addr) 127 while not self.thread_stop: 128 data, addr = lsock.recvfrom(BUFSIZE) 129 msg = self.data.handlechat(data) 130 if outmutex.acquire(1): 131 sys.stdout.write('\r'+' '*(len(get_line_buffer())+len(START))+'\r') 132 print msg 133 sys.stdout.write(START+get_line_buffer()) 134 sys.stdout.flush() 135 outmutex.release() 136 lsock.close() 137 def stop(self): 138 self.thread_stop = True 139 140 #启动入口类 141 class Start(): 142 def __init__(self): 143 self.name = socket.getfqdn(socket.gethostname()) 144 self.data = Data() 145 self.listen = Listen() 146 self.back = Back() 147 print '******* iichats ********' 148 print ' Written by Kevince \n' 149 print 'This is ' + self.name 150 print self.data.gettime()+'\n' 151 #帮助信息 152 def helpinfo(self): 153 if outmutex.acquire(1): 154 print "use ':' to use options" 155 print "\t:exit\t\t\texit iichats" 156 print "\t:list\t\t\tlist online users" 157 print "\t:quit\t\t\tquit the chat mode" 158 print "\t:chat [hostname]\tchatting to someone" 159 print "\t:set status [on|off]\tturn on/of status alarms" 160 outmutex.release() 161 def refresh(self): 162 if outmutex.acquire(1): 163 print '\n******Onlinelist******' 164 for key in users: 165 print key 166 print '**********************\n' 167 outmutex.release() 168 def chatting(self): 169 csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 170 if outmutex.acquire(1): 171 print "use ':help' to get help information" 172 outmutex.release() 173 name = '' 174 address = '' 175 global anoun 176 global START 177 while True: 178 arg = raw_input(START) 179 if arg[0:5] == ':quit' and START != INIT: 180 name = '' 181 address = '' 182 START = INIT 183 elif arg[0] == ':' and START == INIT: 184 if arg[1:] == 'exit': 185 break 186 elif arg[1:5] == 'list': 187 self.refresh() 188 continue 189 elif arg[1:12] == 'set status ': 190 if arg[12:] == 'on': 191 anoun = 1 192 elif arg[12:] == 'off': 193 anoun = 0 194 continue 195 elif arg[1:5] == 'help': 196 self.helpinfo() 197 continue 198 elif arg[1:6] == 'check': 199 self.back.check() 200 print 'checking the list...' 201 time.sleep(3) 202 if outmutex.acquire(1): 203 outmutex.release() 204 self.refresh() 205 elif arg[1:6] == 'chat ': 206 name = arg[6:] 207 if usermutex.acquire(1): 208 userlist = users.keys() 209 usermutex.release() 210 if name not in userlist: 211 if outmutex.acquire(1): 212 print 'this host does not exist' 213 outmutex.release() 214 continue 215 address = (users.get(name), CHATPORT) 216 if outmutex.acquire(1): 217 print 'now chatting to ' + name+" ,use ':quit' to quit CHAT mode" 218 START = name + INIT 219 outmutex.release() 220 else: 221 if outmutex.acquire(1): 222 print "invalid input, use ':help' to get some info" 223 outmutex.release() 224 else: 225 if not len(address): 226 if outmutex.acquire(1): 227 print "you can CHAT to someone, or use ':help'" 228 outmutex.release() 229 continue 230 data = arg 231 msg = self.data.makechat(data, self.name) 232 csock.sendto(msg, address) 233 csock.close() 234 def start(self): 235 self.back.setDaemon(True) 236 self.back.start() 237 self.listen.setDaemon(True) 238 self.listen.start() 239 self.chatting() 240 self.back.stop() 241 self.listen.stop() 242 sys.exit() 243 244 usermutex = threading.Lock() 245 outmutex = threading.Lock() 246 #控制status on和off的情况 247 anoun = 1 248 s = Start() 249 s.start()