通过编写聊天程序来熟悉python中多线程及socket的用法
1、引言
Python中提供了丰富的开源库,方便开发者快速就搭建好自己所需要的应用程序。本文通过编写基于tcp/ip协议的通信程序来熟悉python中socket以及多线程的使用。
2、python中的多线程以及socket的使用
在编写聊天程序程序之前,我们先熟悉一下python中多线程以及socket的使用方法。
2.1、多线程使用方法
在python中提供了Thread这个类来实现多线程程序的开发。
Thread类的原型如下:
class Thread(group=None, target=None, name=None, args=(), kwargs={})
构造函数能带有关键字参数被调用。这些参数是:
group 应当为 None,为将来实现Python Thread类的扩展而保留。
target 是被 run()方法调用的回调对象. 默认应为None, 意味着没有对象被调用。
name 为线程名字。默认,形式为'Thread-N'的唯一的名字被创建,其中N 是比较小的十进制数。
args是目标调用参数的tuple,默认为()。
kwargs是目标调用的参数的关键字dictionary,默认为{}。
而Thread类还提供了很多方法,而本文只讲述程序中所需要的1个方法,其他的方法读者可以根据需要去查阅python的官方帮助文档。
start():开启一个线程
下面将通过一段简单的程序来实验Thread的使用。
程序如下:
import threading def print_work(cunt):
for i in range(cunt):
print 'new thread print:',i def main():
t=threading.Thread(target=print_work,args=(,))
t.start();
sum=;
for i in range():
sum=sum+i
print 'sum=%s' % sum if __name__=="__main__":
main()
程序比较简单,就不多做解释,不过有2点需要值得注意。
注意:
1、在使用Thread类的时候需要import threading
2、当多线程启动的方法的参数只有一个参数的时候,实例化Thread的args的参数要表示为(param1,)需要在参数后面打一个逗号,这是因为tuple元组只有一个元素的时候需要在后面加一个逗号,防止歧义。
2.2、socket的使用方法
下面介绍python中socket的使用方法。
注意:
1 在python中使用socket时要import socket
2 在使用socket中又服务器端和客户端之分
服务器:
1、建立一个基于tcp协议的socket类
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
其中AF_INET指定的ipv4的协议,也可以使用AF_INET6指定ipv6的协议,而STREAM是指定面向流的tcp协议。
2、s.bind(‘', 8089))
绑定一个端口号,其中'127.0.0.1'是客户端的ip地址,可以使用’0.0.0.0’来绑定网络中所有的ip,8089是指定的端口,其中端口在小于1024的时候需要有管理员的权限才能绑定。
3、s.listen(5)
开始实行监听参数:代表连接的最大数量
4、sock, addr = s.accept()
接受一个客户端的连接,返回的是一个与客户端保持连接的socket对象以及客户端的ip地址和端口。该方法也会阻塞线程,直到获得客户端的连接。
客户端:
1、s.connect(('127.0.0.1', 80))
连接到服务器,其中'www.baidu.com’也可以是服务器的ip地址。
2、s.send('hello')
发送数据’hello’。TCP连接创建的是双向通道,双方都可以同时给对方发数据。但是谁先发谁后发,怎么协调,要根据具体的协议来决定。
3、s. recv(1024)
接受连接的对方发来的数据。该方法会阻塞当前线程,所以需要一个专门的线程来接受数据。
注意:
同一个端口,被一个Socket绑定了以后,就不能被别的Socket绑定了。
3、基于python的聊天程序的流程设计
在第二点讲述了聊天程序需要用到的知识点,以及需要注意的地方,现在我们就开始来设计程序的流程吧。
把程序分为即服务器与客户端两个部分。
服务器端的流程如下图:

其中user对象代表一个客户端的连接。
类结构如下图所示:

客户端的流程设计如下图:

4、聊天程序的编码过程
该程序实现的是一个相对比较简单的聊天程序,由于是基于控制台实现的,所只设计容许两个人聊天,另外消息的编码的分割符为|。
4.1、服务器端
首先建立一个User的数据结构,代码如下:
import socket class User:
def __init__(self,skt,username='none'):
self.skt=skt
self.username=username
def send_msg(self,msg):
self.skt.send(msg)
def logout(self):
self.skt.close()
服务端的编码如下:
import sys
import socket
import threading,time
import User #global variable
userlist=[] def hand_user_con(usr):
try:
isNormar=True
while isNormar:
data=usr.skt.recv()
time.sleep()
msg=data.split('|')#分析消息
if msg[]=='login':
print 'user [%s] login' % msg[]
usr.username=msg[]
notice_other_usr(usr)
if msg[]=='talk':
print 'user[%s]to[%s]:%s' % (usr.username,msg[],msg[])
send_msg(msg[],msg[])#发送消息给目标用户,参数1:目标用户,参数2:消息内容
if msg[]=='exit':
print 'user [%s] exit' % msg[]
isNormar=False
usr.close()
userlist.remove(usr)
except:
isNormar=False #通知其他用户以上的好友
def notice_other_usr(usr):
if(len(userlist)>):
print 'The two users'
userlist[].skt.send(("login|%s" % userlist[].username))
userlist[].skt.send(("login|%s" % userlist[].username))
else:
print 'The one users' def send_msg(username,msg):
for usr in userlist:
if(usr.username==username):
usr.skt.send(msg) #程序入口
def main():
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('0.0.0.0',))
s.listen()
print u'waiting for connection...'
while True:
sock,addr=s.accept()#等待用户连接
user=User.User(sock)
userlist.append(user)
t=threading.Thread(target=hand_user_con,args=(user,));
t.start()
s.close() if(__name__=="__main__"):
main()
4.2、客户端
客户端的编码如下:
import sys
import socket
import threading,time #global variable
isNormar=True
other_usr='' def recieve_msg(username,s):
global isNormar,other_usr
print 'Please waiting other user login...'
s.send('login|%s' %username)
while(isNormar):
data= s.recv()#阻塞线程,接受消息
msg=data.split('|')
if msg[]=='login':
print u'%s user has already logged in, start to chat' % msg[]
other_usr=msg[]
else:
print msg[] #程序入口
def main():
global isNormar,other_usr
try:
print 'Please input your name:'
usrname=raw_input()
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("127.0.0.1",))
t=threading.Thread(target=recieve_msg,args=(usrname,s))
t.start()
except:
print 'connection exception'
isNormar=False
finally:
pass
while isNormar:
msg=raw_input()#接受用户输入
if msg=="exit":
isNormar=False
else:
if(other_usr!=''):
s.send("talk|%s|%s" % (other_usr,msg))#编码消息并发送
s.close() if __name__=="__main__":
main()
其效果截图如下:

5、结论
通过编写该聊天的程序,了解了python中多线程以及socket的使用。该聊天的程序过于简单,仅仅只是实现了这客户端-服务端-客户端信息交互的一个流程,并不是很完善,在很多地方还存在很多异常。
通过编写聊天程序来熟悉python中多线程及socket的用法的更多相关文章
- 简单说明Python中的装饰器的用法
简单说明Python中的装饰器的用法 这篇文章主要简单说明了Python中的装饰器的用法,装饰器在Python的进阶学习中非常重要,示例代码基于Python2.x,需要的朋友可以参考下 装饰器对与 ...
- Python中【__all__】的用法
Python中[__all__]的用法 转:http://python-china.org/t/725 用 __all__ 暴露接口 Python 可以在模块级别暴露接口: __all__ = [&q ...
- Python中logging模块的基本用法
在 PyCon 2018 上,Mario Corchero 介绍了在开发过程中如何更方便轻松地记录日志的流程. 整个演讲的内容包括: 为什么日志记录非常重要 日志记录的流程是怎样的 怎样来进行日志记录 ...
- Python中zip()与zip(*)的用法
目录 Python中zip()与zip(*)的用法 zip() 知识点来自leetcode最长公共前缀 Python中zip()与zip(*)的用法 可以看成是zip()为压缩,zip(*)是解压 z ...
- Python 中多线程之 _thread
_thread模块是python 中多线程操作的一种模块方式,主要的原理是派生出多线程,然后给线程加锁,当线程结束的 时候取消锁,然后执行主程序 thread 模块和锁对象的说明 start_new_ ...
- 简单的聊天程序,主要用到的是Socket
服务端: import java.io.*; import java.net.*; import java.util.*; public class ChatServer { boolean stat ...
- python中enumerate()函数用法
python中enumerate()函数用法 先出一个题目:1.有一 list= [1, 2, 3, 4, 5, 6] 请打印输出:0, 1 1, 2 2, 3 3, 4 4, 5 5, 6 打印输 ...
- Python中try...except...else的用法
Python中try...except...else的用法: try: <语句>except <name>: <语句> #如果在try ...
- (转)Python中的split()函数的用法
Python中的split()函数的用法 原文:https://www.cnblogs.com/hjhsysu/p/5700347.html Python中有split()和os.path.split ...
随机推荐
- 七,mysql优化——表的垂直划分和水平划分
1,表的水平划分 如果一个表的记录数太多,比如成千上万条,而且需要经常检索,那么我们有必要化整为零.如果我拆成100个表,那么每个表只有10万条记录.当然需要数据在逻辑上可以划分.一个好的划分依据,有 ...
- 转载:不定义JQuery插件,不要说会JQuery
转载:http://www.cnblogs.com/xcj26/p/3345556.html 一:导言 有些WEB开发者,会引用一个JQuery类库,然后在网页上写一写$("#") ...
- Elasticsearch java API (23)查询 DSL Geo查询
地理查询编辑 Elasticsearch支持两种类型的地理数据: geo_point纬度/经度对字段的支持,和 geo_shape领域,支持点.线.圆.多边形.多等. 这组查询: geo_shape ...
- php文件每隔几秒执行一次
说实话,linux 下面的crontab 任务完全可以实现所有的定时任务脚本,但是有些脚本只需要在一段时间内执行,过了这段时间之后,就不再执行定时脚本了.在使用crontab的时候,就需要人为的关闭掉 ...
- 错误处理Cannot load JDBC driver class 'oracle.jdbc.drive
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create PoolableConnec是由于<Resource name=&qu ...
- 字蛛fontSpider的使用
字蛛官方文档 http://font-spider.org/index.html 首先安装全局包 npm install font-spider -g 然后下载字体 ,本次需要的是 "造 ...
- php内核为变量的值分配内存的几个宏
在php5.3之前,为某变量分配内存是用宏 MAKE_STD_ZVAL; 737 #define MAKE_STD_ZVAL(zv) \ # /Zend/zend.h738 ALLOC_ZVAL(zv ...
- 【bzoj3028】 食物 生成函数+隔板法
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3028 这题的推导很妙啊,裸的推母函数的题. 我们首先构造出每种食物的母函数: 汉堡:$ ...
- easyUI datebox 日期空间斜杠格式化。例如将日期空间中显示2017-03-13,改为2017/03/13
easyUI datebox 日期空间斜杠格式化 将日期空间中显示2017-03-13,改为2017/03/13 //日期控件斜杠格式化 function formatDate(date){ if( ...
- Java 并发编程——volatile与synchronized
一.Java并发基础 多线程的优点 资源利用率更好 程序设计在某些情况下更简单 程序响应更快 这一点可能对于做客户端开发的更加清楚,一般的UI操作都需要开启一个子线程去完成某个任务,否者会容易导致客户 ...