zoukankan      html  css  js  c++  java
  • socket-自我总结(2)

    这里总结下一个服务端与多个客户端之间的通信。

    先看demo:

    #/usr/bin/env python
    #_*_coding:utf-8_*_
    __author__ = 'ganzl'
    import time
    import socket
    import select
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sk.bind(('127.0.0.1',6666))
    sk.listen(5)
    sk.setblocking(False)
    inputs = [sk,] #sk就是一个监听的sorcket,放入inputs这个列表中。
    
    while True:
        readable_list, writeable_list, error_list = select.select(inputs,[],[],1)  #把第一个参数设为列表动态的添加
        time.sleep(2)
        print "inputs :",inputs     #打印inputs列表,查看执行变化
        print "readable_list:",readable_list #打印readable_list ,查看执行变化
    
        for r in readable_list:
            if r == sk:  #sk是一个描述符,
                conn,address = r.accept()
                inputs.append(conn)#conn是连接socket之后的那根通信管道。
                #sk监控的是socket描述符是否有变化,conn监控的是连接后的socket之间两者服务器之间的通信是否有变化
                print address
            else:
            #如果是客户端,接受和返回数据
                client_data = r.recv(1024)
                if client_data:
                    r.sendall(client_data)
                else:
                    inputs.remove(r)#如果没有收到客户端端数据,则移除客户端句柄 因为,不管是正常关闭还是异常关闭,client端的系统底层都会发送一个消息
                                    #异常关闭的时候会发送一个空格。
    客户端(1#!/usr/bin/env python
    #_*_coding:utf-8 _*_
    __author__ = 'ganzl'
    import socket
    import time
    #创建socket对象
    ck = socket.socket()
    #通过IP&端口连接server
    ck.connect(('127.0.0.1',6666))
    #发送消息给服务端
    time.sleep(10)
    ck.sendall('hello server, this is  ganzl---test3---1-')
    ck.sendall('hello server, this is  ganzl---test3----')
    #接收服务端的消息并打印
    server_answer = ck.recv(1024)
    print server_answer
    #关闭连接
    ck.close()
    客户端(2#!/usr/bin/env python
    #_*_coding:utf-8 _*_
    __author__ = 'ganzl'
    
    import socket
    ck = socket.socket()
    ck.connect(('127.0.0.1',6666))
    ck.sendall('hello server, this is  ganzl----2----')
    server_answer = ck.recv(1024)
    print server_answer
    ck.close()
    客户端(3

      通过http访问:http://127.0.0.1:6666/

     ------------------------------------------------------------------------

    以上就是用到了IO的多路复用中的select模块。

    Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。其他两个方法暂时还没弄懂,空了再看下源码,再百度,再请教其他人。。。

    select方法总结:

    句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)
     
    参数: 可接受四个参数(前三个必须)
    返回值:三个列表
     
    select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
    1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
    2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
    3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
    4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
       当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。

    写上面这个demo的目的是啥?其实就是SocketServe源码中的最几句核心代码。

    SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。

    用一个SocketServer的实际例子来过过代码。方便以后查看,理解。

    SocketServer的demo:

    服务端:

    #!/usr/bin/env python
    #_*_coding:utf-8 _*_
    __author__ = 'ganzl'
    import SocketServer
    class MyServer(SocketServer.BaseRequestHandler):
    
        def handle(self):
            # print self.request,self.client_address,self.server
            conn = self.request
            conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
            Flag = True
            while Flag:
                data = conn.recv(1024)
                if data == 'exit':
                    Flag = False
                elif data == '0':
                    conn.sendall('通过可能会被录音.balabala一大推')
                else:
                    conn.sendall('请重新输入.')
    if __name__ == '__main__':
        server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer)
        server.serve_forever()

    客服端:

    #!/usr/bin/env python
    #_*_coding:utf-8 _*_
    __author__ = 'ganzl'
    import socket
    
    ip_port = ('127.0.0.1',8009)
    sk = socket.socket()
    sk.connect(ip_port)
    sk.settimeout(5)
    while True:
        data = sk.recv(1024)
        print 'receive:',data
        inp = raw_input('please input:')
        sk.sendall(inp)
        if inp == 'exit':
            break
    
    sk.close()

    代码实现多个客户端可以同时跟服务器端通信的原因如下:(我自己的类继承了SocketServer.BaseRequestHandler这个类,而它封装了一些基础的类,我自己重写它的方法就好了)

    我就不文字描述。 图片就是太难看。有时间再弄个好看的

    上图的精简demo如下:

    源码精简:(SocketServer)
    
    import socket
    import threading
    import select
    
    
    def process(request, client_address):
        print request,client_address
        conn = request
        conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
        flag = True
        while flag:
            data = conn.recv(1024)
            if data == 'exit':
                flag = False
            elif data == '0':
                conn.sendall('通过可能会被录音.balabala一大推')
            else:
                conn.sendall('请重新输入.')
    
    sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sk.bind(('127.0.0.1',8002))
    sk.listen(5)
    
    while True:
        r, w, e = select.select([sk,],[],[],1)
        print 'looping'
        if sk in r:
            print 'get request'
            request, client_address = sk.accept()
            t = threading.Thread(target=process, args=(request, client_address))
            t.daemon = False
            t.start()
    
    sk.close()
    View Code

    这里socket用到的都是线程。一个进程中产生的多线程。至于进程线程协程,过段时间搞搞明白。

  • 相关阅读:
    allocator类
    智能指针shared_ptr
    字面值常量类
    转换构造函数
    委托构造函数
    访问说明符&封装
    const成员函数
    函数指针
    constexper和常量表达式
    函数返回数组指针
  • 原文地址:https://www.cnblogs.com/shoug/p/5121587.html
Copyright © 2011-2022 走看看