• 通过编写聊天程序来熟悉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=(10,))
        t.start();
        sum=0;
        for i in range(100):
            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(1024)
                time.sleep(1)
                msg=data.split('|')#分析消息
                if msg[0]=='login':
                    print 'user [%s] login' % msg[1]
                    usr.username=msg[1]
                    notice_other_usr(usr)
                if msg[0]=='talk':
                    print 'user[%s]to[%s]:%s' % (usr.username,msg[1],msg[2])
                    send_msg(msg[1],msg[2])#发送消息给目标用户,参数1:目标用户,参数2:消息内容
                if msg[0]=='exit':
                    print 'user [%s] exit' % msg[0]
                    isNormar=False
                    usr.close()
                    userlist.remove(usr)
        except:
            isNormar=False
            
    #通知其他用户以上的好友       
    def notice_other_usr(usr):
        if(len(userlist)>1):
            print 'The two users'
            userlist[0].skt.send(("login|%s" % userlist[1].username))
            userlist[1].skt.send(("login|%s" % userlist[0].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',9999))
        s.listen(5)
        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(1024)#阻塞线程,接受消息
            msg=data.split('|')
            if msg[0]=='login':
                print u'%s user has already logged in, start to chat' % msg[1]
                other_usr=msg[1]
            else:
                print msg[0]
                
    #程序入口
    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",9999))
            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的使用。该聊天的程序过于简单,仅仅只是实现了这客户端-服务端-客户端信息交互的一个流程,并不是很完善,在很多地方还存在很多异常。

      

  • 相关阅读:
    Valgrind使用转载 Sanny.Liu
    Caffe模型读取 Sanny.Liu
    JNI动态库生成、编译、查看相关简易资料 Sanny.Liu
    GDB调试,转载一位大牛的东西 Sanny.Liu
    Android处理图片工具(转载) Sanny.Liu
    添加可点击的imagebottom,有个点击动画效果 Sanny.Liu
    去OpenCVManager,大部分为转载,仅当自己学习使用 Sanny.Liu
    转载: vim使用技巧 Sanny.Liu
    结构体数组初始化三种方法,转载 Sanny.Liu
    AsyncTask机制学习 Sanny.Liu
  • 原文地址:https://www.cnblogs.com/mingjiatang/p/4905395.html
Copyright © 2020-2023  润新知