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程序有问题

原文地址:https://www.cnblogs.com/qingqing-919/p/8620416.html