• socket编程了解


    Socket 编程

    Socket通讯原理描述:

    套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象。它们允许程序接受并进行连接,如发送和接受数据。为了建立通信通道,网络通信的每个端点拥有一个套接字对象极为重要。和大多数语言一样,Python 支持面向连接和无连接,实现接口功能与步骤也大致相同。

     

    面向连接即需要先连接然后通讯, 面向连接主要协议就是传输控制协议(tcp),要创建tcp套接字时需要指定套接字类型为 SOCK_STRAM,表达了他作为流套接字的特点。

    无连接,顾名思义无需建立连接就可以进行通讯,这时数据到达顺序、可靠性就无法保证了。实现这种连接的协议就是用户数据包协议(udp)。创建UDP时需要指定套接字类型为 SOCK_DGRAM。

    服务器端通常是用来处理数据的,客户端是用来请求数据的。服务端可以同时处理多条数据。

    同一个80端口

     

    TCP服务器端:

    1. 第一步是创建socket对象。调用socket构造函数。如:

    socket = socket.socket( family, type )

    family参数代表地址家族,可为AF_INET或AF_UNIX。AF_INET家族包括Internet地址,AF_UNIX家族用于同一台机器上的进程间通信。

    type参数代表套接字类型,可为SOCK_STREAM(流套接字)和SOCK_DGRAM(数据报套接字)。

    2. 第二步是将socket绑定到指定地址。这是通过socket对象的bind方法来实现的:

    socket.bind( address )

    由AF_INET所创建的套接字,address地址必须是一个双元素元组,格式是(host,port)。host代表主机,port代表端口号。如果端口号正在使用、主机名不正确或端口已被保留,bind方法将引发socket.error异常。

    3. 第三步是使用socket套接字的listen方法接收连接请求。

    socket.listen( backlog )

    backlog指定最多允许多少个客户连接到服务器。它的值至少为1。收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求。

    4. 第四步是服务器套接字通过socket的accept方法等待客户请求一个连接。

    connection, address = socket.accept()

    调 用accept方法时,socket会时入“waiting”状态。客户请求连接时,方法建立连接并返回服务器。accept方法返回一个含有两个元素的 元组(connection,address)。第一个元素connection是新的socket对象,服务器必须通过它与客户通信;第二个元素 address是客户的Internet地址。

    5. 第五步是处理阶段,服务器和客户端通过send和recv方法通信(传输 数据)。服务器调用send,并采用字符串形式向客户发送信息。send方法返回已发送的字符个数。服务器使用recv方法从客户接收信息。调用recv 时,服务器必须指定一个整数,它对应于可通过本次方法调用来接收的最大数据量。recv方法在接收数据时会进入“blocked”状态,最后返回一个字符 串,用它表示收到的数据。如果发送的数据量超过了recv所允许的,数据会被截短。多余的数据将缓冲于接收端。以后调用recv时,多余的数据会从缓冲区 删除(以及自上次调用recv以来,客户可能发送的其它任何数据)。

    6. 传输结束,服务器调用socket的close方法关闭连接

    伪代码大致如下:

    1 创建套接字,绑定套接字到当地地址,然后开始监听连接。就是socket,bind,listen。

    2 进入循环,不断接受客户端的连接请求,然后接收传来的数据,当然也可以发送给对方数据。就是accept一个连接,然后recv数据。

    3 接收完毕可以关闭套接字,close。

    ss.socket(Socket.AF_INET,Socket.SOCK_STRAM)  #创建服务器套接字

    ss.bind() #把本地地址绑到套接字上

    ss.listen() #监听连接

    inf_loop: #服务器无限循环

    cs=ss.accept() #接受客户端的连接

    comm._loop: #通信循环

    cs.recv()/cs.send() #对话

    cs.close() #关闭客户套接字

    ss.close() #关闭服务器套接字

     

    TCP客户端:

    1.  第一步是创建一个socket以连接服务器:socket = socket.socket( family, type )

    2.  第二步是使用socket的connect方法连接服务器。对于AF_INET家族,连接格式如下:

    socket.connect( (host,port) )

    host代表服务器主机名或IP,port代表服务器进程所绑定的端口号。如连接成功,客户就可通过套接字与服务器通信,如果连接失败,会引发socket.error异常。

    3.  第三步是处理阶段,客户和服务器将通过send方法和recv方法通信。

    4.  传输结束,客户通过调用socket的close方法关闭连接。

    伪代码如下:

    1 创建套接字,然后连接远端地址,socket ,connect。

    2 建立连接之后开始发送数据。Send(data),当然可以从缓冲区读取服务器发来的数据。Recv(BUFF)

    3 完毕后,关闭套接字。Close

    cs=socket(Socket.AF_INET,Socket.SOCK_DGRAM)

    #创建客户套接字

    cs.connect() #尝试连接服务器

    comm._loop: #通信循环

    cs.send()/cs.recv() #对话

    cs.close() #关闭套接字

    简单的server 端和客户端(tcp协议)

    服务端

    import socket   #socket模块

    import commands   #执行系统命令模块

    HOST='127.0.0.1'

    PORT=8085

    s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)   #定义socket类型,网络通信,TCP协议,前面是ipv4协议,后面的tcp协议。

    s.bind((HOST,PORT))   #套接字绑定的IP与端口,host用0.0.0.1就会包含本机的所有ip

    s.listen(1)         #开始TCP监听,listen中的参数表示可以多少客户端来进行连接

    while 1:

        conn,addr=s.accept()   #接受TCP连接,并返回新的套接字与IP地址

        print'Connected by',addr    #输出客户端的IP地址

        while 1:

            data=conn.recv(1024)    #把接收的数据实例化

            print data

            conn.sendall('got message from server:'+data.upper())

    conn.close()     #关闭连接

     

    客户端:

    import socket

    HOST='127.0.0.1'

    PORT=8085

    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)      #定义socket类型,网络通信,TCP

    s.connect((HOST,PORT))       #要连接的IP与端口

    while 1:

        cmd=raw_input("Please input cmd:")       #与人交互,输入命令

        s.sendall(cmd)      #把命令发送给对端

        data=s.recv(1024)     #把接收的数据定义为变量

        print data         #输出变量

    s.close()   #关闭连接

     

     

     

    变形:把上面的代码改成只能发3次

     

    netstat -ano|findstr "1104"  查看windows上某个端口是否被占用

    netstat -anp|grep 8080   linux使用的

     

    服务端:

    import socket   #socket模块

    import commands   #执行系统命令模块

    HOST='127.0.0.1'

    PORT=8085

    s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)   

    #定义socket类型,网络通信,TCP

    s.bind((HOST,PORT))   #套接字绑定的IP与端口

    s.listen(5)         

    #开始TCP监听,listen中的参数表示可以多少客户端来进行连接

    while 1:

        conn,addr=s.accept()   #接受TCP连接,并返回新的套接字与IP地址

        print'Connected by',addr    #输出客户端的IP地址

        while 1:

            try:

                data=conn.recv(1024)    #把接收的数据实例化

                print data

                conn.sendall('got message from server:'+data.upper())

            except Exception:

                conn.close()     #关闭连接

                break

    客户端:

    import socket

    HOST='127.0.0.1'

    PORT=8085

    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)      

    #定义socket类型,网络通信,TCP

    s.connect((HOST,PORT))       #要连接的IP与端口

    times=3

    while times>0:

        cmd=raw_input("Please input cmd:")       #与人交互,输入命令

        s.sendall(cmd)      #把命令发送给对端

        data=s.recv(1024)     #把接收的数据定义为变量

        print data         #输出变量

        times-=1

    s.close()   #关闭连接

     

     

     

    稍微复杂一点的客户端例子

    服务端:

    import time

    if __name__ == '__main__':  

        import socket  

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

        sock.bind(('localhost', 8009))  

        sock.listen(5)  

        while True:  

            time.sleep(0.1)

            connection,address = sock.accept()

            while 1:  

                try:  

                    connection.settimeout(5)  

                    buf = connection.recv(1024) 

                    print "got message from client:",buf 

                    if buf == '1': 

                        print "1" 

                        connection.send('welcome to server!')  

                    elif buf == '2':  

                        connection.send('please go out!')

                    elif buf == "close":

                        connection.send('bye!') 

                        connection.close()

                        break

                except socket.timeout:  

                    print 'time out'

                    connection.close()

                    break

                except Exception,e:

                    print e 

                    connection.close()

                    break

     

    客户端:

    if __name__ == '__main__':  

        import socket  

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

        sock.connect(('localhost', 8009))  

        import time  

        time.sleep(2)  

        sock.send('2')  

        print sock.recv(1023) 

        sock.send('1')

        print sock.recv(1024)  

        sock.send('close')

        print sock.recv(1024)  

        print "Done!" 

        sock.close()   

     

    变形练习:服务端把客户端传来的消息全部存入文件中,然后给客户端回传一句话。

    自己:

    服务端:

    import time
    if __name__ == '__main__':
        import socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('localhost', 10015))
        sock.listen(5)
        while True:
            time.sleep(0.1)
            connection,address = sock.accept()
            while 1:
                try:
                    connection.settimeout(5)
                    buf = connection.recv(1024)
                    print "got message from client:",buf
                    fp = open("e:\test1.txt", "a")
                    if buf == "close":
                        connection.send('bye!')
                        connection.close()
                        break
                    else
    :
                        fp.write(buf+" ")
                        #fp.write(" ")
                   
    fp.close()
                except socket.timeout:
                    print 'time out'
                   
    connection.close()
                    break
                except
    Exception,e:
                    print e
                    connection.close()
                    break

    客户端:

    if __name__ == '__main__':
        import socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect(('localhost', 10015))
        import time
        time.sleep(2)
        sock.send('2')
        sock.send('1')
        sock.send('close')
        print "Done!"
       
    sock.close()

     

     

    老师:

    >>> import traceback
    >>> try:
    ...     1/0
    ... except:
    ...     print "error"
    ...
    error
    >>> try:
    ...     1/0
    ... except Exception,e:
    ...     print e
    ...
    integer division or modulo by zero
    >>> try:
    ...     1/0
    ... except:
    ...     traceback.print_exc()
    ...
    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
    ZeroDivisionError: integer division or modulo by zero

     

    服务端:

    # encoding=utf-8
    import time
    import random
    import traceback
    if __name__ == '__main__':
        import socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('localhost', 8009))
        sock.listen(5)
        AI_robot =[u'吃了么?',u"最近涨工资了么?",u"记得找对象",u"记得减肥"]
        while True:
            time.sleep(0.1)
            connection,address = sock.accept()
            while 1:
                try:
                    connection.settimeout(5)
                    buf = connection.recv(1024)
                    fp=open("e:\chat_record.txt","a")
                    print "got message from client:",buf.decode("utf-8")
                    if buf == "close":
                        connection.send('bye!')
                        connection.close()
                        break
                    elif
    buf=='q':
                        connection.close()
                        break
                    else
    :
                        fp.write(buf+" ")
                        connection.send(str(time.ctime())+AI_robot[random.randint(0,3)].encode("utf-8"))
                    fp.close()
                except socket.timeout:
                    print 'time out'
                   
    traceback.print_exc()
                    connection.close()
                    break
                except
    Exception,e:
                    traceback.print_exc()
                    print e
                    connection.close()
                    break

    客户端:

    # encoding=utf-8
    if __name__ == '__main__':
        import socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect(('localhost', 8009))
        import time
        while 1:
            try:
                data=raw_input("say to server:")
                sock.send(data.decode("gbk").encode("utf-8"))
                print sock.recv(1023).decode("utf-8")
                if data == "q":
                    sock.close()
                    break
            except
    :
                print "exception"
       
    print "Done!"

     

     

     

     

    实现一个类似ssh的功能(仅支持Linux操作系统)

     

    ip:39.106.41.11 
    username:student
    password:gloryroad987! 

     

     

    服务器端:

    import commands

    if __name__ == '__main__':

        import socket

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        sock.bind(('localhost', 8008))

        sock.listen(5)

        while True:

            connection,address = sock.accept()

            try:

                connection.settimeout(5)

                command = connection.recv(1024)

                print command

                code,result=commands.getstatusoutput(command)

                print code

                print result

            except socket.timeout:

                print 'time out'

            connection.close()

     

     

    客户端:

    if __name__ == '__main__':

        import socket

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        sock.connect(('localhost', 8008))

        import time

        time.sleep(2)

        sock.send('ls -al /home/wxh')

        print sock.recv(1024)

        sock.close() 

     

    服务器端:
    import commands
    if __name__ == '__main__':
        import socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('localhost', 8001))
        sock.listen(5)
        while True:
            connection,address = sock.accept()
            try:
                connection.settimeout(5)
                command = connection.recv(1024)
                print command
                code,result=commands.getstatusoutput(command)
                print code
                print result
                connection.send(str(code))
                connection.send(result)
            except socket.timeout:
                print 'time out'
            connection.close()

    客户端:
    if __name__ == '__main__':
        import socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect(('localhost', 8001))
        import time
        time.sleep(2)
        sock.send('ls -al /home/wxh')
        print "command status:",sock.recv(1)
        print "command result:",sock.recv(1024)
        sock.close()

     

    使用Socket 传送一个文件

    服务器端

    #-*- coding: UTF-8 -*-

    import socket,time,SocketServer,struct,os,thread

    host='127.0.0.1'

    port=12307

    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义socket类型

    s.bind((host,port)) #绑定需要监听的Ip和端口号,tuple格式

    s.listen(1)

     

     

    def conn_thread(connection,address):  

        while True:

            try:

                connection.settimeout(600) #设置连接时间

                fileinfo_size=struct.calcsize('128sl')  #文件信息

                buf = connection.recv(fileinfo_size)  #接收此文件的信息在buf里面

                if buf: #如果不加这个if,第一个文件传输完成后会自动走到下一句,需要拿到文件大小信息才可以继续执行

                    filename,filesize =struct.unpack('128sl',buf)  #解包,解开后是两个值

                    filename_f = filename.strip('0') #解出文件名,去掉C语言中的字符串0结束

                    filenewname = os.path.join('e:\',('new_'+ filename_f))

                    print 'file new name is %s, filesize is %s' %(filenewname,filesize)

                    recvd_size = 0 #定义接收了的文件大小

                    file = open(filenewname,'wb')

                    print 'stat receiving...'

                    while not recvd_size == filesize: #判断收到的和原有文件是不是一样大,不一样继续接收

                        if filesize - recvd_size > 1024:

                            rdata = connection.recv(1024)

                            recvd_size += len(rdata)

                        else:

                            rdata = connection.recv(filesize - recvd_size) #接收到的文件和告知的一样大了,停止接收,判定为接收完毕

                            recvd_size = filesize

                        file.write(rdata)

                    file.close()

                    print 'receive done'

                    #connection.close()

            except socket.timeout:

                connection.close()

     

     

    while True:

        connection,address=s.accept()

        print('Connected by ',address)

        #thread = threading.Thread(target=conn_thread,args=(connection,address)) #使用threading也可以

        #thread.start()

        thread.start_new_thread(conn_thread,(connection,address)) 

     

    s.close()

     

    客户端

    #-*- coding: UTF-8 -*-

    import socket,os,struct

    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    s.connect(('127.0.0.1',12307))

    while True:

        

        filepath = raw_input('Please Enter chars: ')

        if os.path.isfile(filepath):

            fileinfo_size=struct.calcsize('128sl') #定义打包规则

            #定义文件头信息,包含文件名和文件大小

            fhead = struct.pack('128sl',os.path.basename(filepath),os.stat(filepath).st_size)

            s.send(fhead) 

            print 'client filepath: ',filepath

            # with open(filepath,'rb') as fo: 这样发送文件有问题,发送完成后还会发一些东西过去

            fo = open(filepath,'rb')

            while True:

                filedata = fo.read(1024)

                if not filedata:

                    break

                s.send(filedata)

            fo.close()

            print 'send over...'

            #s.close()

    SocketServer模块的Fork方式(进程模式)linux 下执行

    Server 端代码

    #!/usr/bin/python  

    #encoding=utf-8  

      

    from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler  

    import time  

      

    class Server(ForkingMixIn, TCPServer): #自定义Server类  ,以进程模式启动

        pass  

      

    class MyHandler(StreamRequestHandler):  

          

        def handle(self): #重载handle函数  

            addr = self.request.getpeername()  

            print 'Get connection from', addr #打印客户端地址  

            data= self.rfile.readline().strip()  #客户端发送的信息必须带有回车,否则会一直等待客户端继续发送数据

            print data

            time.sleep(1) #休眠5秒钟 

            if data: 

                self.wfile.write('This is a ForkingMixIn tcp socket server') #给客户端发送信息  

      

    host = '127.0.0.1'  

    port = 8009

    server = Server((host, port), MyHandler)  

      

    server.serve_forever() #开始侦听并处理连接

     

    客户端:

    if __name__ == '__main__':  

        import socket  

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

        sock.connect(('localhost', 8009))  

        import time  

        time.sleep(2)  

        sock.send('ls -al /home/wxh'+" ")  

        print sock.recv(1024)  

    sock.close()  

     

    注释:

     多个连接同时到达服务器端的时候,每个连接主进程都生成一个子进程专门用来处理此连接,而主进程则依旧保持在侦听状态。因主进程和子进程是同时进行的,所以不会阻塞新的连接。但由于生成进程消耗的资源比较大,这种处理方式在有很多连接的时候会带来性能问题。Server类须继承ForkingMixIn和TCPServer两个类。

     

    SocketServer模块的Fork方式(线程模式)linux 下执行

    服务器端代码:

    #!/usr/bin/python  

    #encoding=utf-8  

      

    from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler  

    import time  

      

    class Server(ThreadingMixIn, TCPServer): #自定义Server类  ,以线程方式启动

        pass  

      

    class MyHandler(StreamRequestHandler):  

          

        def handle(self): #重载handle函数  

            addr = self.request.getpeername()  

            print 'Get connection from', addr #打印客户端地址  

            data= self.rfile.readline().strip()  #客户端发送的信息必须带有回车,否则会一直等待客户端继续发送数据

            print data

            time.sleep(1) #休眠5秒钟 

            if data: 

                self.wfile.write('This is a ForkingMixIn tcp socket server') #给客户端发送信息  

      

    host = '127.0.0.1'  

    port = 8001  

    server = Server((host, port), MyHandler)  

      

    server.serve_forever() #开始侦听并处理连接

     

     

     线程是一种轻量级的进程,比Fork消耗的资源更少,而且主线程和子线程之间具有相同的地址空间,处理效率高。但大量的使用线程会带来线程之间的数据同步问题,处理不好可能使服务程序失去响应。上述与Fork方式中代码基本相同,仅仅是采用的ThreadingMixIn类不同。

     

     

    SocketServer模块的Threading线程池方式

     

    import SocketServer  

    import threading

    class MyTCPHandler(SocketServer.BaseRequestHandler):  

        def handle(self):  

            while True:  

              self.data = self.request.recv(1024).strip()  

              cur_thread = threading.current_thread()  

              print cur_thread  

              if not self.data:  

                  print "client:%s leave!" % self.client_address[0]  

                  break  

              print "%s wrote:%s" % (self.client_address[0], self.data)  

              self.request.sendall(self.data.upper())  

      

    if __name__ == "__main__":  

        HOST, PORT = "127.0.0.1",8044  

        server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)  

        server.serve_forever()    

    if __name__ == "__main__":  

        HOST, PORT = "127.0.0.1",8001  

        server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)  

    server.serve_forever()  

     

    #服务器可以自动判断客户端是否还会发送数据

    客户端程序:

    if __name__ == '__main__':  

        import socket  

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

        sock.connect(('localhost', 8044))  

        import time  

        time.sleep(2)  

        sock.send('ls -al /home/wxh'+" ")  

        print sock.recv(1024)  

    sock.close()  

     

    使用socketserver 传送一个文件

    服务端:

    #-*- coding: UTF-8 -*-

    import socket,time,SocketServer,struct,os

    host='127.0.0.1'

    port=12307

    ADDR=(host,port)

     

    class MyRequestHandler(SocketServer.BaseRequestHandler):    

        def handle(self):      

            print('connected from:', self.client_address)

            while True:

                fileinfo_size=struct.calcsize('128sl') #定义文件信息。128s表示文件名为128bytes长,l表示一个int或log文件类型,在此为文件大小

                self.buf = self.request.recv(fileinfo_size)

                if self.buf: #如果不加这个if,第一个文件传输完成后会自动走到下一句

                    self.filename,self.filesize =struct.unpack('128sl',self.buf) #根据128sl解包文件信息,与client端的打包规则相同

                    print 'filesize is: ',self.filesize,'filename size is: ',len(self.filename) #文件名长度为128,大于文件名实际长度

                    self.filenewname = os.path.join('e:\',('new_'+ self.filename).strip('0')) #使用strip()删除打包时附加的多余空字符

                    print self.filenewname,type(self.filenewname)

                    recvd_size = 0 #定义接收了的文件大小

                    file = open(self.filenewname,'wb')

                    print 'stat receiving...'

                    while not recvd_size == self.filesize:

                        if self.filesize - recvd_size > 1024:

                            rdata = self.request.recv(1024)

                            recvd_size += len(rdata)

                        else:

                            rdata = self.request.recv(self.filesize - recvd_size) 

                            recvd_size = self.filesize

                        file.write(rdata)

                    file.close()

                    print 'receive done'

            #self.request.close()

     

    tcpServ = SocketServer.ThreadingTCPServer(ADDR, MyRequestHandler)  

    print('waiting for connection...' )

    tcpServ.serve_forever()

    客户端:

    #-*- coding: UTF-8 -*-

    import socket,os,struct

    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    s.connect(('127.0.0.1',12307))

    while True:

        

        filepath = raw_input('Please Enter chars: ')

        if os.path.isfile(filepath):

            fileinfo_size=struct.calcsize('128sl') #定义打包规则

            #定义文件头信息,包含文件名和文件大小

            fhead = struct.pack('128sl',os.path.basename(filepath),os.stat(filepath).st_size)

            s.send(fhead) 

            print 'client filepath: ',filepath

            # with open(filepath,'rb') as fo: 这样发送文件有问题,发送完成后还会发一些东西过去

            fo = open(filepath,'rb')

            while True:

                filedata = fo.read(1024)

                if not filedata:

                    break

                s.send(filedata)

            fo.close()

            print 'send over...'

            #s.close()

    Select :

    I/O多路复用是在单线程模式下实现多线程的效果,实现一个多I/O并发的效果。

    进程指定内核监听哪些文件描述符(最多监听1024fd)的哪些事件,当没有文件描述符事件发生时,进程被阻塞;当一个或者多个文件描述符事件发生时,进程被唤醒。

    当我们调用select()时:

      1、上下文切换转换为内核态

      2、将fd从用户空间复制到内核空间

      3、内核遍历所有fd,查看其对应事件是否发生

      4、如果没发生,将进程阻塞,当设备驱动产生中断或者timeout时间后,将进程唤醒,再次进行遍历

      5、返回遍历后的fd

    6、将fd从内核空间复制到用户空间

     

     

    一个任务:
    1 www.sohu.com 

    2 www.sogou.com 

    3 www.gloryroad.cn 

    4 www.baidu.com 


    单进程或者单线程:
    1->2->3-4

    多进程或者多线程:
    一个线程、一个进程:去执行一个访问

    select:
    1 个线程:
    1 www.sohu.com 

      5秒
    2 www.sogou.com 

     4秒
    3 www.gloryroad.cn 

     3秒
    4 www.baidu.com 

     2秒  select不会等,一直发请求,哪个先返回用哪个。

     

     

    fd:file descriptor 文件描述符

     

    fd_r_list, fd_w_list, fd_e_list = select.select(rlist, wlist, xlist, [timeout])

     

    参数: 可接受四个参数(前三个必须)

    rlist: wait until ready for reading

    wlist: wait until ready for writing(一般不使用)

    xlist: wait for an “exceptional condition”

    timeout: 超时时间

     

    Server端:

    import socket

    import select

     

    s = socket.socket()

    s.bind(('127.0.0.1',8888))

    s.listen(5)

    r_list = [s,]

    num = 0

    while True:

     rl, wl, error = select.select(r_list,[],[],10)   #r_list读的句柄,[][]后面两个是异常的句柄,10是超时

    执行过程:

     #第一次执行循环体:客户端建立的连接的时候,rl和r_list分别是[s,]和[s,]

     #                  执行连接之后,r_list变为了[s,conn]

     #第二次执行循环体:rl和r_list分别是[conn,]和[s,conn]

     

     

     num+=1

     print('counts is %s'%num)

     print("rl's length is %s"%len(rl))

     for fd in rl:

      if fd == s:

       conn, addr = fd.accept()

       r_list.append(conn)

       msg = conn.recv(200)

       conn.sendall(('first----%s'%conn.fileno()).encode())

      else:

       try:

        msg = fd.recv(200)

        fd.sendall('second'.encode())

       except ConnectionAbortedError:

        r_list.remove(fd)

     

     

    s.close()

     

    客户端:

    import socket

     

    flag = 1

    s = socket.socket()

    s.connect(('127.0.0.1',8888))

    while flag:

     input_msg = input('input>>>')

     if input_msg == '0':

      break

     s.sendall(input_msg.encode())

     msg = s.recv(1024)

     print(msg.decode())

     

    s.close()

     

    #注意客户端输入的数据需要外带引号,例如:’hi’

    Twisted 框架

     

     

    服务器端:

    # coding=utf-8

    from twisted.internet.protocol import Protocol

    from twisted.internet.protocol import Factory

    from twisted.internet.endpoints import TCP4ServerEndpoint

    from twisted.internet import reactor

     

     

    clients = []

     

     

    class Spreader(Protocol):

        def __init__(self, factory):

            self.factory = factory

     

        def connectionMade(self):

            self.factory.numProtocols = self.factory.numProtocols + 1

            self.transport.write(

                "欢迎来到Spread Site, 你是第%s个客户端用户! " % (self.factory.numProtocols)

            )

            print "new connect: %d" % (self.factory.numProtocols)

            clients.append(self)

     

        def connectionLost(self, reason):

            self.factory.numProtocols = self.factory.numProtocols - 1

            clients.remove(self)

            print "lost connect: %d" % (self.factory.numProtocols)

     

        def dataReceived(self, data):

            print "received data:",data

            self.transport.write(data)

     

     

    class SpreadFactory(Factory):

        def __init__(self):

            self.numProtocols = 0

     

        def buildProtocol(self, addr):

            return Spreader(self)

     

     

    endpoint = TCP4ServerEndpoint(reactor, 8007)

    endpoint.listen(SpreadFactory())

    reactor.run()

     

     

    客户端:

    # coding=utf-8

    from twisted.internet.protocol import Protocol, ClientFactory

    from twisted.internet import reactor

    import threading

    import time

    import sys

    import datetime

     

     

    class Echo(Protocol):

        def __init__(self):

            self.connected = False

     

        def connectionMade(self):

            self.connected = True

     

        def connectionLost(self, reason):

            self.connected = False

     

        def dataReceived(self, data):

            print "data from server:",data.decode("utf-8")

     

     

    class EchoClientFactory(ClientFactory):

        def __init__(self):

            self.protocol = None

     

        def startedConnecting(self, connector):

            print "Start to Connect..."

     

        def buildProtocol(self, addr):

            print "Connected..."

            self.protocol = Echo()

            return self.protocol

     

        def clientConnectionLost(self, connector, reason):

            print "Lost connection. Reason: ", reason

     

        def clientConnectionFailed(self, connector, reason):

            print "Connection is failed, Reason: ", reason

     

     

    bStop = False

     

     

    def routine(factory):

        while not bStop:

            if factory.protocol and factory.protocol.connected:

                factory.protocol.transport.write("hello, I'm %s %s" % (

                    sys.argv[0], datetime.datetime.now()        

                ))

     

                print sys.argv[0], datetime.datetime.now()

                

            time.sleep(5)

     

     

    host = '127.0.0.1'

    port = 8007

    factory = EchoClientFactory()

    reactor.connectTCP(host, port, factory)

    threading.Thread(target=routine, args=(factory,)).start()

    reactor.run()

    bStop = True

     

    ——————————————————————————————————————

     

    #!/usr/bin/python   

    #encoding=utf-8   

       

    from twisted.internet import reactor   

    from twisted.internet.protocol import Protocol, Factory   

       

    class EchoServer(Protocol):   

       

        def connectionMade(self):             #连接建立的时候   

            print 'Get connection from', self.transport.client   

            self.factory.numProtocols = self.factory.numProtocols+1   

            if self.factory.numProtocols > 2: #当连接超过2个的时候,断开连接   

                self.transport.write("Too many connections, try later ")   

                self.transport.loseConnection()  

                return   

            print 'Get connection from', self.transport.client  

            

       

        def connectionLost(self, reason):     #断开连接   

            self.factory.numProtocols = self.factory.numProtocols-1   

       

        def dataReceived (self, data):        #将收到的数据返回给客户端   

            self.transport.write(data)  

            print data 

            self.transport.loseConnection()     #收到数据后就断开 

       

    factory = Factory()  

    factory.numProtocols = 0   

    factory.protocol = EchoServer   

       

    port = 1200   

    reactor.listenTCP(port, factory)   

    reactor.run() #进入循环

     

    客户端程序:

    if __name__ == '__main__':  

        import socket  

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

        sock.connect(('localhost', 8001))  

        import time  

        time.sleep(2)  

        sock.send('ls -al /home/wxh'+" ")  

        print sock.recv(1024)  

    sock.close()  

     

     

    twisted 官网的例子,保存为echoServ.py:

    #!/usr/bin/env python  

    # coding: utf-8  

      

    from twisted.internet.protocol import Protocol  

    from twisted.internet.protocol import Factory  

    from twisted.internet import reactor  

      

    class Echo(Protocol):  

        '''协议类实现用户的服务协议,例如 http,ftp,ssh 等'''  

        def __init__(self, factory):  

            self.factory = factory  

      

        def connectionMade(self):  

            '''连接建立时被回调的方法'''  

            self.factory.numProtocols = self.factory.numProtocols + 1  

            self.transport.write("Welcome! There are currently %d open connections. " %(self.factory.numProtocols,))  

      

        def connectionLost(self, reason):  

            '''连接关闭时被回调的方法'''  

            self.factory.numProtocols = self.factory.numProtocols - 1  

      

        def dataReceived(self, data):  

            '''接收数据的函数,当有数据到达时被回调'''

            print data  

            self.transport.write(data)  

      

      

    class EchoFactory(Factory):  

        '''协议工厂类,当客户端建立连接的时候,创建协议对象,协议对象与客户端连接一一对应'''  

        numProtocols = 0  

        #protocol = Echo  

        def buildProtocol(self, addr):  

            return Echo(self)  

      

    if __name__ == '__main__':  

          # 创建监听端口  

        FACTORY = EchoFactory()  

        reactor.listenTCP(1200, FACTORY)  

          # 开始监听事件  

    reactor.run()

     

      协议工厂继承自twisted.internet.protocol.Factory,需实现buildProtocol方法,协议工厂负责实例化协议类,不应该保存于连接相关的状态信息,因为协议工厂类仅创建一个。协议类继承自twisted.internet.protocol.Protocol,需实现dataReceived等方法,在协议类中实现应用协议,每一个客户端连接都会创建一个新的协议类对象。transport就是连接对象,通过它进行网络写数据。

     

    客户端:

    if __name__ == '__main__':  

        import socket  

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

        sock.connect(('localhost', 1200))  

        import time  

        time.sleep(2)  

        sock.send('ls -al /home/wxh'+" ")  

        print sock.recv(1024)  

        sock.send('ls -al /home/wxh'+" ")  

        print sock.recv(1024) 

        sock.close()

    使用守护进程来运行服务器端程序

     使用守护进程的方式运行服务,需要提供一个tac配置文件(这就是一个python文件,只是扩展名不同),并且在这个文件中需要创建一个应用程序对象,对象名必须是application。
           首先创建一个echo.tac文件:

    #!/usr/bin/env python  

    # coding: utf-8  

      

    from twisted.application import service, internet  

    import sys

    sys.path.append("e:\")   #echoServ.py保存在哪个目录,就把这个路径设定到哪里

    from echoServ import EchoFactory  

      

    # 创建应用程序对象  

    application = service.Application('Echo 服务程序')  

      

    # 创建 service 对象  

    myServices = internet.TCPServer(1200, EchoFactory())  

      

    # 设置 application 为 service 的父元素  

    myServices.setServiceParent(application)

     

     

    然后用守护进程方式运行服务,运行命令:#twistd -y echo.tac。
          若想要停止服务:#kill -15 (pid)。

     

    客户端程序:

    if __name__ == '__main__':  

        import socket  

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

        sock.connect(('localhost', 1200))  

        import time  

        time.sleep(2)  

        sock.send('ls -al /home/wxh'+" ")  

        print sock.recv(1024)  

    sock.close()

    聊天的服务器版本:

    服务器端:

    from twisted.internet import protocol, reactor

     

    class Echo(protocol.Protocol):

        def dataReceived(self, data):

            # As soon as any data is received, write it back

            self.transport.write(data)

            print data

     

    class EchoFactory(protocol.Factory):

        def buildProtocol(self, addr):

            return Echo()

     

    reactor.listenTCP(8000, EchoFactory())

    reactor.run()

     

    客户端:

    from twisted.internet import reactor, protocol

     

    class EchoClient(protocol.Protocol):

        def connectionMade(self):

            self.transport.write("hello, world!")

     

        def dataReceived(self, data):

            print "Server said:", data

            self.transport.loseConnection()

     

        def connectionLost(self, reason):

            print "connection lost"

     

    class EchoFactory(protocol.ClientFactory):

        def buildProtocol(self, addr):

            return EchoClient()

     

        def clientConnectionFailed(self, connector, reason):

            print "Connection failed - goodbye!"

            reactor.stop()

     

        def clientConnectionLost(self, connector, reason):

            print "Connection lost - goodbye!"

            reactor.stop()

     

    reactor.connectTCP("localhost", 8000, EchoFactory())

    reactor.run()

    写一个聊天的服务器:

    服务器端:

    #!/usr/bin/python  

    #encoding=utf-8  

    from twisted.internet.protocol import Factory  

    from twisted.protocols.basic import LineReceiver  

    from twisted.internet import reactor  

      

    class Chat(LineReceiver):  

        def __init__(self, users):  

            self.users = users  

            self.name = None  

            self.state = "GETNAME"  

      

        def connectionMade(self):  

            self.sendLine("What's your name? ")  

      

        def connectionLost(self, reason):  

            if self.users.has_key(self.name):  

                del self.users[self.name]  

      

        def lineReceived(self, line):  

            if self.state == "GETNAME":  

                self.handle_GETNAME(line)  

            else:  

                self.handle_CHAT(line)  

      

        def handle_GETNAME(self, name):  

            if self.users.has_key(name):  

                self.sendLine("Name taken, please choose another."+" ")  

                return  

            self.sendLine("Welcome, %s! " % (name,))  

            self.name = name  

            self.users[name] = self  

            self.state = "CHAT"  

      

        def handle_CHAT(self, message):  

            message = "<%s> %s " % (self.name, message)  

            for name, protocol in self.users.iteritems():  

                if protocol == self:                

                    self.sendLine(message+" ") 

     

      

    class ChatFactory(Factory):  

      

        def __init__(self):  

            self.users = {}    # maps user names to Chat instances  

      

        def buildProtocol(self, addr):  

            return Chat(self.users)  

      

    if __name__ == '__main__':  

        reactor.listenTCP(1200, ChatFactory())  

        reactor.run()  

     

    客户端1:

    #socket client end  

    from socket import *  

      

    s=socket(AF_INET,SOCK_STREAM)  

    remote_host=gethostname()  

    print 'remote_host:',remote_host  

    port=1200  

    s.connect((remote_host,port)) #发起连接  

    print("Connected from",s.getsockname()) ##返回本地IP和端口  

    print("Connected to",s.getpeername())  ##返回服务端IP和端口  

    s.send('Jack ')#发送一行字符串(以 结束)到服务器端   

    s.send('hi ')  

    s.send('hello ') 

      

    print 'the msg i got from select server is:'  

    print s.recv(1200)

    客户端2:

    if __name__ == '__main__':  

        import socket  

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

        sock.connect(('localhost', 1200))  

        import time  

        time.sleep(2)

        sock.send('fosterwu ')  

        print sock.recv(1020) 

        sock.send('hi ')  

        print sock.recv(1020) 

        sock.send('good ') 

        print sock.recv(120) 

        sock.send('gloryroad ') 

        print sock.recv(120) 

        sock.close()

    Twisted框架中对文件的操作
    服务端:       

    工厂有startFactory和stopFactory两种方式来执行相关应用的创建与销毁,下述代码从客户端接收到的信息都会被写入文件中。
    #!/usr/bin/env python  

    # coding: utf-8  

      

    from twisted.internet.protocol import Factory  

    from twisted.protocols.basic import LineReceiver  

    from twisted.internet import reactor  

      

    class LoggingProtocol(LineReceiver):  

        def lineReceived(self, line):  

            self.factory.fp.write(line+' ')  

            self.factory.fp.flush()

            self.sendLine("got it!") 

      

    class LogfileFactory(Factory):  

        protocol = LoggingProtocol  

        def __init__(self, fileName):  

            self.file = fileName  

        def startFactory(self):  

            self.fp = open(self.file, 'a')  

        def stopFactory(self):  

            self.fp.close() 

            print "close" 

      

    if __name__ == '__main__':  

          # 创建监听端口  

        FACTORY = LogfileFactory("e:\a.txt")  

        reactor.listenTCP(8007, FACTORY)  

          # 开始监听事件  

    reactor.run()

     

    客户端:

     

    if __name__ == '__main__':  

        import socket  

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

        sock.connect(('localhost', 8007))  

        import time  

        time.sleep(2)

        sock.send('fosterwu ')  

        print sock.recv(1020) 

        sock.send('hi ')  

        print sock.recv(1020) 

        sock.send('good ') 

        print sock.recv(120) 

        sock.send('gloryroad ') 

        print sock.recv(120) 

        sock.close()

     

     

    Udp Server 端:

    from socket import *  

    from time import ctime  

      

    HOST = ''  

    PORT = 1200

    BUFSIZ = 128  

    ADDR = (HOST, PORT)  

      

    #创建一个服务器端UDP套接字  

    udpServer = socket(AF_INET, SOCK_DGRAM)  

    #绑定服务器套接字  

    udpServer.bind(ADDR)  

      

    while True:  

        print 'waiting for message...'  

    #接收来自客户端的数据  

        data, addr = udpServer.recvfrom(BUFSIZ) 

        print "got message:",data 

    #向客户端发送数据  

        udpServer.sendto('[%s] %s' % (ctime(), data), addr)  

        print '...received from and returned to:', addr  

          

    udpServer.close()

     

    Udp 的客户端:

     

    #encoding=utf-8

    from socket import *

     

    HOST = 'localhost'

    PORT = 1200

    BUFSIZ = 128

    ADDR = (HOST, PORT)

     

    # 创建客户端UDP套接字

    udpClient = socket(AF_INET, SOCK_DGRAM)

     

    while True:

      data = raw_input('>')

      if not data:

        break

      # 向服务器端发送数据

      udpClient.sendto(data, ADDR)

      # 接收来自服务器端的数据

      data, ADDR = udpClient.recvfrom(BUFSIZ)

      print data

      if not data:

        break

     

    udpClient.close()

     

     

    参考文献:

    http://blog.csdn.net/taiyang1987912/article/details/40376067

    https://www.cnblogs.com/earendil/p/7411115.html

     

     

    基于广播的聊天程序

    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    SO_LINGER选项用来设置当调用closesocket时是否马上关闭socket; 

    SO_REUSEADDR用于对TCP套接字处于TIME_WAIT状态下的socket,才可以重复绑定使用。server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。TCP,先调用close()的一方会进入TIME_WAIT状态

     

    服务器端:Linux可以执行

    import socket, select

     

    #Function to broadcast chat messages to all connected clients

    def broadcast_data (sock, message):

        #Do not send the message to master socket and the client who has send us the message

        for socket in CONNECTION_LIST:

            if socket != server_socket and socket != sock :

                try :

                    socket.send(message)

                except :

                    # broken socket connection may be, chat client pressed ctrl+c for example

                    socket.close()

                    CONNECTION_LIST.remove(socket)

     

    if __name__ == "__main__":

         

        # List to keep track of socket descriptors

        CONNECTION_LIST = []

        RECV_BUFFER = 4096 # Advisable to keep it as an exponent of 2

        PORT = 6001

         

        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # this has no effect, why ?

        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        server_socket.bind(("0.0.0.0", PORT))

        server_socket.listen(10)

     

        # Add server socket to the list of readable connections

        CONNECTION_LIST.append(server_socket)

     

        print "Chat server started on port " + str(PORT)

     

        while 1:

            # Get the list sockets which are ready to be read through select

            read_sockets,write_sockets,error_sockets = select.select(CONNECTION_LIST,[],[])

     

            for sock in read_sockets:

                #New connection

                if sock == server_socket:

                    # Handle the case in which there is a new connection recieved through server_socket

                    sockfd, addr = server_socket.accept()

                    CONNECTION_LIST.append(sockfd)

                    print "Client (%s, %s) connected" % addr

                     

                    broadcast_data(sockfd, "[%s:%s] entered room " % addr)

                 

                #Some incoming message from a client

                else:

                    # Data recieved from client, process it

                    try:

                        #In Windows, sometimes when a TCP program closes abruptly,

                        # a "Connection reset by peer" exception will be thrown

                        data = sock.recv(RECV_BUFFER)

                        if data:

                            broadcast_data(sock, " " + '<' + str(sock.getpeername()) + '> ' + data)                

                     

                    except:

                        broadcast_data(sock, "Client (%s, %s) is offline" % addr)

                        print "Client (%s, %s) is offline" % addr

                        sock.close()

                        CONNECTION_LIST.remove(sock)

                        continue

         

    server_socket.close()

     

    客户端:

    import socket, select, string, sys

     

    def prompt() :

        

        sys.stdout.write('<You> ')

        sys.stdout.flush()

     

    #main function

    if __name__ == "__main__":

         

        if(len(sys.argv) < 3) :

            print 'Usage : python telnet.py hostname port'

            sys.exit()

         

        host = sys.argv[1]

        port = int(sys.argv[2])

         

        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        s.settimeout(6)

         

        # connect to remote host

        try :

            s.connect((host, port))

        except :

            print 'Unable to connect'

            sys.exit()

         

        print 'Connected to remote host. Start sending messages'

        prompt()

         

        while 1:

            rlist = [sys.stdin, s]

            #s.connect((host, port)) 

            # Get the list sockets which are readable

            read_list, write_list, error_list = select.select(rlist , [], [])

             

            for sock in read_list:

                #incoming message from remote server

                if sock == s:

                    data = sock.recv(4096)

                    

                    if not data :

                        print ' Disconnected from chat server'

                        sys.exit()

                    else :

                        #print data

                        sys.stdout.write(data)

                        prompt()

                 

                #user entered a message

                else :

                    

                    msg = sys.stdin.readline()

                    s.send(msg)

                    prompt()

     

    执行:

    需要多开几个客户端,才能看到广告效果,执行如下:

    python client.py 127.0.0.1 6001

     

    Unix下是sock,windows下是winsock,中间语法有很多是不同的,所以windows运行client程序有问题

  • 相关阅读:
    python之Selenium
    Python常用集锦(upgrading...)
    豆瓣爬虫
    poj 2299 Ultra-QuickSort(求逆序对)
    nyoj117 求逆序数
    codeforces 644A Parliament of Berland
    codeforces 659A Round House
    poj 3264 Balanced Lineup(RMQ裸题)
    nyoj 119 士兵杀敌(三)(RMQ)
    hdu 5655 CA Loves Stick
  • 原文地址:https://www.cnblogs.com/qingqing-919/p/8620416.html
Copyright © 2020-2023  润新知