【Python】iichats —— 命令行下的局域网聊天程序
转载请声明出处: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还请多多指教~
- #!/usr/bin/python
- #coding:utf8
- #python 2.7.6
- import threading
- import socket
- import time
- import os
- import sys
- import signal
- from readline import get_line_buffer
- BUFSIZE = 1024
- BACKPORT = 7789 #状态监听端口
- CHATPORT = 7788 #聊天信息发送窗口
- START = '>>'
- INIT = '>>'
- users = {}
- ips = {}
- #起到双向字典的作用,ip和name互相映射
- #数据处理类(消息封装、分解)
- class Data():
- def gettime(self):
- return time.strftime('%Y-%m-%d %H:%M', time.localtime(time.time()))
- def getip(self):
- ip = os.popen("/sbin/ifconfig | grep 'inet addr' | awk '{print $2}'").read()
- ip = ip[ip.find(':')+1:ip.find('\n')]
- return ip
- def handlebc(self, data):
- data = data[5:]
- res = data.split('#opt:')
- return res
- def makebc(self, name, switch):
- data = 'name:%s#opt:%d' % (name, switch)
- return data
- def handlechat(self, data):
- msg = '\n' + self.gettime() + '\n' +'from '+ data + '\n'
- return msg
- def makechat(self, data, name):
- return name + ':' + data
- #后台监听类
- class Back(threading.Thread):
- def __init__(self):
- threading.Thread.__init__(self)
- self.data = Data()
- self.addrb = ('255.255.255.255', BACKPORT)
- self.addrl = ('', BACKPORT)
- self.name = socket.gethostname()
- self.ip = self.data.getip()
- self.thread_stop = False
- def status(self, name, switch):
- if switch == 0:
- status = 'offline'
- elif switch == 1:
- status = 'online'
- #用来处理输入过程中被线程返回消息打乱的情况
- if outmutex.acquire(1):
- sys.stdout.write('\r'+' '*(len(get_line_buffer())+len(START))+'\r')
- print '[status] '+name+' '+status
- sys.stdout.write(START+get_line_buffer())
- sys.stdout.flush()
- outmutex.release()
- def broadcast(self, switch):
- bsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- bsock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
- data = self.data.makebc(self.name, switch)
- bsock.sendto(data, self.addrb)
- bsock.close()
- def response(self, addr, switch):
- rsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- data = self.data.makebc(self.name, switch)
- rsock.sendto(data, (addr, BACKPORT))
- rsock.close()
- def check(self):
- if usermutex.acquire():
- ips.clear()
- users.clear()
- usermutex.release()
- self.broadcast(1)
- def run(self):
- lsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- lsock.bind(self.addrl)
- self.broadcast(1)
- while not self.thread_stop:
- data, addr = lsock.recvfrom(BUFSIZE)
- datalist = self.data.handlebc(data)
- if usermutex.acquire(1):
- if datalist[1] == '':
- if ips.has_key(addr[0]):
- if anoun == 1:
- self.status(datalist[0], 0)
- del ips[addr[0]]
- del users[datalist[0]]
- elif datalist[1] == '':
- if anoun == 1 and datalist[0] != self.name:
- self.status(datalist[0], 1)
- users[datalist[0]] = addr[0]
- ips[addr[0]] = datalist[0]
- self.response(addr[0], 2)
- elif datalist[1] == '':
- if anoun == 1 and datalist[0] != self.name:
- self.status(datalist[0], 1)
- users[datalist[0]] = addr[0]
- ips[addr[0]] = datalist[0]
- usermutex.release()
- lsock.close()
- def stop(self):
- self.broadcast(0)
- self.thread_stop = True
- #聊天类
- class Listen(threading.Thread):
- def __init__(self):
- threading.Thread.__init__(self)
- self.addr = ('', CHATPORT)
- self.name = socket.getfqdn(socket.gethostname())
- self.data = Data()
- self.thread_stop = False
- def ans(self, addr):#to be added 用来确认消息报的接受
- return
- def run(self):
- lsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- lsock.bind(self.addr)
- while not self.thread_stop:
- data, addr = lsock.recvfrom(BUFSIZE)
- msg = self.data.handlechat(data)
- if outmutex.acquire(1):
- sys.stdout.write('\r'+' '*(len(get_line_buffer())+len(START))+'\r')
- print msg
- sys.stdout.write(START+get_line_buffer())
- sys.stdout.flush()
- outmutex.release()
- lsock.close()
- def stop(self):
- self.thread_stop = True
- #启动入口类
- class Start():
- def __init__(self):
- self.name = socket.getfqdn(socket.gethostname())
- self.data = Data()
- self.listen = Listen()
- self.back = Back()
- print '******* iichats ********'
- print ' Written by Kevince \n'
- print 'This is ' + self.name
- print self.data.gettime()+'\n'
- #帮助信息
- def helpinfo(self):
- if outmutex.acquire(1):
- print "use ':' to use options"
- print "\t:exit\t\t\texit iichats"
- print "\t:list\t\t\tlist online users"
- print "\t:quit\t\t\tquit the chat mode"
- print "\t:chat [hostname]\tchatting to someone"
- print "\t:set status [on|off]\tturn on/of status alarms"
- outmutex.release()
- def refresh(self):
- if outmutex.acquire(1):
- print '\n******Onlinelist******'
- for key in users:
- print key
- print '**********************\n'
- outmutex.release()
- def chatting(self):
- csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- if outmutex.acquire(1):
- print "use ':help' to get help information"
- outmutex.release()
- name = ''
- address = ''
- global anoun
- global START
- while True:
- arg = raw_input(START)
- if arg[0:5] == ':quit' and START != INIT:
- name = ''
- address = ''
- START = INIT
- elif arg[0] == ':' and START == INIT:
- if arg[1:] == 'exit':
- break
- elif arg[1:5] == 'list':
- self.refresh()
- continue
- elif arg[1:12] == 'set status ':
- if arg[12:] == 'on':
- anoun = 1
- elif arg[12:] == 'off':
- anoun = 0
- continue
- elif arg[1:5] == 'help':
- self.helpinfo()
- continue
- elif arg[1:6] == 'check':
- self.back.check()
- print 'checking the list...'
- time.sleep(3)
- if outmutex.acquire(1):
- outmutex.release()
- self.refresh()
- elif arg[1:6] == 'chat ':
- name = arg[6:]
- if usermutex.acquire(1):
- userlist = users.keys()
- usermutex.release()
- if name not in userlist:
- if outmutex.acquire(1):
- print 'this host does not exist'
- outmutex.release()
- continue
- address = (users.get(name), CHATPORT)
- if outmutex.acquire(1):
- print 'now chatting to ' + name+" ,use ':quit' to quit CHAT mode"
- START = name + INIT
- outmutex.release()
- else:
- if outmutex.acquire(1):
- print "invalid input, use ':help' to get some info"
- outmutex.release()
- else:
- if not len(address):
- if outmutex.acquire(1):
- print "you can CHAT to someone, or use ':help'"
- outmutex.release()
- continue
- data = arg
- msg = self.data.makechat(data, self.name)
- csock.sendto(msg, address)
- csock.close()
- def start(self):
- self.back.setDaemon(True)
- self.back.start()
- self.listen.setDaemon(True)
- self.listen.start()
- self.chatting()
- self.back.stop()
- self.listen.stop()
- sys.exit()
- usermutex = threading.Lock()
- outmutex = threading.Lock()
- #控制status on和off的情况
- anoun = 1
- s = Start()
- s.start()
【Python】iichats —— 命令行下的局域网聊天程序的更多相关文章
- Linux命令行下如何终止当前程序
Linux命令行下如何终止当前程序 快捷键: Ctrl+c 在命令行下起着终止当前执行程序的作用, Ctrl+d 相当于exit命令,退出当前shell Ctrl+s 挂起当前shell(保护作用很明 ...
- [兴趣使然]用python在命令行下画jandan像素超载鸡
下午刷煎蛋的时候看到 Dthalo 蛋友发的系列像素超载鸡,就想自己试试用python脚本画一个,老男孩视频里的作业真没兴趣,弄不好吧没意思,往好了写,自己控制不好,能力不够. 所以还是找自己有兴趣的 ...
- python命令行下tab键补全命令
在python命令行下不能使用tab键将命令进行补全,手动输入又很容易出错. 解决:tab.py #/usr/bin/env python # -*- coding:utf-8 -*- ''' 该模块 ...
- Python安装后在CMD命令行下出现“应用程序无法启动.............”问题
问题存在之一:系统是刚刚重做的精简版服务器系统(阉割版) AN就是在阿里云上刚开的Windows Server 2008 系统上碰到的 吓尿了都 症状: 正常安装python环 ...
- 命令行下查看python和numpy的版本和安装位置
命令行下查看python和numpy的版本和安装位置 1.查看python版本 方法一: python -V 注意:‘-V‘中‘V’为大写字母,只有一个‘-’ 方法二: python --versio ...
- 修复python命令行下接收不到参数的问题
由于之前安装过多个python版本,导致出现了在命令行下直接给py文件传递参数的时候接收不到,即使重新卸载安装也没有解决. 解决办法: 修改如下图路径下的键值为:"D:\Python27\p ...
- windows下安装python、环境设置、多python版本的切换、pyserial与多版本python安装、windows命令行下切换目录
1.windows下安装python 官网下载安装即可 2.安装后的环境设置 我的电脑--属性--高级--设置path的地方添加python安装目录,如C:\Python27;C:\Python33 ...
- DAY2 Python 标准库 -> Getpass 模块 -> 命令行下输入密码的方法.
getpass 模块 getpass 模块提供了平台无关的在命令行下输入密码的方法. getpass(prompt) 会显示提示字符串, 关闭键盘的屏幕反馈, 然后读取密码. 如果提示参数省略, 那么 ...
- Python Trick —— 命令行显示
1 应用场景 在命令行展示下,有以下两种场景. 进度条显示.在同一行展示不断的更新的进度条. 信息显示/隐藏控制.比如希望向多个用户展示不同信息,各个用户彼此保密. 2 进度条展示 跟c语言类似,打印 ...
随机推荐
- 一步一步学python(五) -条件 循环和其他语句
1.print 使用逗号输出 - 打印多个表达式也是可行的,但要用逗号隔开 >>> print 'chentongxin',23 SyntaxError: invalid synta ...
- MEMS市场介绍
惠普第一.德州仪器第二 市场观察发展报告说,MEMS市场在2007年增长百分之九,达到70亿美元,其中前30名制造商的收入总和有56亿美元,平均增长7个百分点. 惠普(HP)打印机使用MEMS喷墨头, ...
- java中内存结构及堆栈详解
一. java内存结构 1. Heap(堆):实例分配的地方,通过-Xms与-Xmx来设置 2. MethodArea(方法区域):类的信息及静态变量. 对应是Permanet Generation, ...
- linux命令之ls命令的简明讲解
我详细熟悉linux操作系统的同学对ls命令是再熟悉不过了因为我们通常使用该命令查看某个目录下的文件 其命令形式是:ls [选项] 目录或者文件 本文中主要讲解两个最常用的选项: -l:用于列出文件的 ...
- linux的7种运行级别<学习笔记>
Linux系统有7个运行级别(runlevel) 运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动 运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆 运行级别 ...
- .net面试问答(大汇总)
用.net做B/S结构的系统,您是用几层结构来开发,每一层之间的关系以及为什么要这样分层? 答:从下至上分别为:数据访问层.业务逻辑层(又或成为领域层).表示层 数据访问层:有时候也称为是持久层,其功 ...
- 你知道为什么Xcode6中Swift没有智能提示和自己主动补全功能吗 ?
你知道为什么Xcode6中Swift没有智能提示和自己主动补全功能吗 ? 长沙戴维营教育将为你解开这个巨大的谜团大BUG! http://www.ubuntucollege.cn/course/29/ ...
- Java基础笔记-String类
String 类(被final修饰) 字符串是一种特殊的对象,一旦字符串被初始化就不可以被改变了.(内容不变) 例如: String s = “abc”; String s1 = new Stri ...
- HTTP请求的基本概念 HTTP请求头和响应头的含义
1,HTTP请求的基本概念 TCP/UPD/HTTP *2,HTTP请求头和响应头的含义 请求头: Accept: text/html,image/*(浏览器可以接收的类型) Acc ...
- UVA 1599 Ideal Path
题意: 给出n和m,n代表有n个城市.接下来m行,分别给出a,b,c.代表a与b之间有一条颜色为c的道路.求最少走几条道路才能从1走到n.输出要走的道路数和颜色.保证颜色的字典序最小. 分析: bfs ...