python学习笔记8

Socket网络编程:

  确保数据发送全部接收方法:先将待发送的数据长度发送给对方,对方根据数据长度写一个while循环接收数据,直到接收的数据长度与发送的数据长度相等。

  socket 粘包:同一个客户端或服务器端两次发送的命令靠的很近,容易一起全进入缓冲区,接收到的数据整到一起。避免粘包,交替使用send()和recv()。

  FTP文件传输步骤:

    server端:
      1.读取文件名
      2.检测文件是否存在
      3.打来文件
      4.检测文件大小
      5.发送文件大小
      6.等待客户端确认
      7.开始边读边发数据
      8.发送MD5确认

    client端:     

      1.发送命令
      2.接收文件大小
      3.发送等待接收
      4.接收文件并保存
      5.接收MD5值并校验

socket内置方法:

    Socket 参数介绍

    socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

    socket.socketpair([family[, type[, proto]]])

    socket.create_connection(address[, timeout[, source_address]])

    socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) #获取要连接的对端主机地址 必会

    sk.setblocking(bool) 必会
      是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

    sk.accept() 必会
      接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
      接收TCP 客户的连接(阻塞式)等待连接的到来

    sk.connect(address) 必会
      连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

    sk.connect_ex(address)
      同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

    sk.close() 必会
      关闭套接字

    sk.recv(bufsize[,flag]) 必会
      接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略

    sk.recvfrom(bufsize[.flag])
      与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

    sk.send(string[,flag]) 必会
      将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送.

    sk.sendall(string[,flag]) 必会
      将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
      内部通过递归调用send,将所有内容发送出去。

    sk.sendto(string[,flag],address)
      将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

    sk.settimeout(timeout) 必会
      设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

    sk.getpeername() 必会
      返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

    sk.getsockname()
      返回套接字自己的地址。通常是一个元组(ipaddr,port)

    sk.fileno()
      套接字的文件描述符

    socket.sendfile(file, offset=0, count=None)
      发送文件 ,但目前多数情况下并无什么卵用。

socket_ssh_client示例:

#socket_ssh_client
# -*- coding:utf-8 -*-
import socket
client=socket.socket()
client.connect(('127.0.0.1',8003))
while True:
    msg=input('>>:').strip()
    if msg=='quit':
        break
    if len(msg)==0:#不能发送空信息,会造成死机
        continue
    client.send(msg.encode('utf-8'))
    receive_size=client.recv(1024)#接收总数据长度
    client.send('避免粘包'.encode('utf-8'))#目的为避免粘包,将服务器端两次send分开。
    print(receive_size)
    receive_data=b''
    while int(receive_size.decode('utf-8'))!=len(receive_data):#循环接收所有缓冲区数据
        data=client.recv(1024)#接受服务器过来的数据,当数据太多超过1024时,剩余的留在缓冲区,等待下次接收
        receive_data+=data#数据存储
        print(len(receive_data))
    else:
        print(receive_data.decode())#打印数据
        print('receive finish')
client.close()

socket_ssh_server示例:

#socket_ssh_server
# -*- coding:utf-8 -*-
#SSH 为 Secure Shell 的缩写,;SSH 为建立在应用层和传输层基础上的安全协议,专为远程登录会话和其他网络服务提供安全性的协议。
import socket , os,time
server=socket.socket()
#server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)允许地址重用
server.bind(('localhost',8003))
server.listen(5)
while True:
    conn,addr=server.accept()
    print(conn,addr)
    while True:
        try:
            data=conn.recv(1024)
            if len(data)==0:
                print('The client disconnect automaticlly ')
                break
            print(data.decode())
            cmd_res=os.popen(data.decode()).read()#执行cmd命令data,执行结果赋给cmd_res。
            if len(cmd_res)==0:
                cmd_res='cmd_res has no output...'
            conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8'))#字符串才能编码成二进制。先发送需要传输的数据长度。
            #time.sleep(0.5)会卡顿0.5秒的写法
            client_ack=conn.recv(1024)
            conn.send(cmd_res.encode('utf-8'))
        except ConnectionResetError as e:
            print('Client abnormal end open',e)
            break
    conn.close()
server.close()

socket文件传输client:

import socket,hashlib
client=socket.socket()
client.connect(('localhost',8004))
while True:
    msg=input('>>:')
    if msg=='quit':break
    if msg.startswith('get'):
        client.send(msg.encode())
        FileSize=client.recv(1024)
        if FileSize!='这不是一个文件'.encode('utf-8'):
            client.send('可以开始传输文件'.encode('utf-8'))
            receive_data = b''
            f=open('test.txt','wb')
            m=hashlib.md5()
            while int(FileSize.decode('utf-8')) > len(receive_data):
                if int(FileSize.decode('utf-8')) - len(receive_data)>1024:#确保文件不会粘包,刚好接受完
                    size=1024
                else:
                    size=int(FileSize.decode('utf-8')) - len(receive_data)
                data = client.recv(size)
                m.update(data)
                receive_data += data
                f.write(data)
            else:
                print('单次接收完成')
            f.close()
            raw_md5=client.recv(1024).decode('utf-8')
            new_md5=m.hexdigest()
            print(raw_md5,new_md5)
            if raw_md5==new_md5:
                print('文件准确传输')
            else:
                print('传输失败')

client.close()

socket文件传输server:

import socket,os,hashlib
server=socket.socket()
server.bind(('127.0.0.1',8004))
server.listen(5)
while True:
    print('等待客户端连接')
    conn,addr=server.accept()
    while True:
        try:
            news=conn.recv(1024)
            if not news:
                print('客户端正常断开')
                break
            filename=news.split()[1]
            if os.path.isfile(filename):
                f=open(filename,'rb')
                m=hashlib.md5()
                FileSize=os.stat(filename).st_size#读取文件大小
                conn.send(str(FileSize).encode('utf-8'))
                conn.recv(1024)#防止粘包
                for line in f:
                    conn.send(line)
                    m.update(line)
                f.close()
                conn.send(m.hexdigest().encode('utf-8'))
            else:
                conn.send('这不是一个文件'.encode('utf-8'))
        except ConnectionResetError as e:
            print('客户端异常断开',e)
            break
    conn.close()
server.close()

socketserver模块及功能:

  socketserver支持多用户并发处理,是对socket的封装,模块名socketserver。 

  模块中包含以下几个类:
    1.class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
    2.class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)用法同上
    3.class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)比较不常见
    4.class socketserver.UnixDatagramServer(server_address, RequestHandlerClass,bind_and_activate=True)比较不常见
    以上四个称为server classes 

  之间的继承关系:
    BaseServer-->TCPServer-->UDPServer-->UnixDatagramServer
    BaseServer-->TCPServer-->UnixStreamServer 

  创建socketserver的步骤:
    1.创建一个请求处理类,并且这个类要继承BaseRequstHandle,而且还要重写父类的handle()
    2.必须实例化一个server class(例如TCPServer),并且传递IP和你上面创建的请求处理类给这个server class(TCPServer)
    3.调用实例的handle_request()或server_forever()方法来处理一个或多个请求
      实例.handle_request()#只处理一个请求
      实例.server_forever()#可处理多个请求,永远执行,处理一个等待下一个。
    4.调用server.close()关闭服务器

  多线程并发服务器示例:

# -*- coding:utf-8 -*-
import socketserver
class MyTCPHandle(socketserver.BaseRequestHandler):#每一个请求过来,都会实例化MyTCPHandle()
    '''1.自定义一个请求处理类,继承BaseRequestHandle,重写Handle方法'''
    def handle(self):#和客户端的所有交互在Handle里写
        'self.request is the TCP socket connected to client'
        while True:
            try:
                self.data=self.request.recv(1024).strip()#self.requesrt类似于conn,但是此处必须是self.request
                if not self.data:
                    print('客户端主动断开')
                    break
                print('{} wrote:'.format(self.client_address[0]))#打印客户端IP地址
                print(self.data.decode('utf-8'))
                self.request.sendall(self.data.upper())#sendall等于重复调用send
            except ConnectionResetError as e:
                print('客户端异常断开',e)
                break

addr,port='127.0.0.1',8005
#实例化一个server class(例如TCPServer),并且传递IP和你上面创建的请求处理类给这个server class(TCPServer)
# server=socketserver.TCPServer((addr,port),MyTCPHandle)#此时不支持多并发
server=socketserver.ThreadingTCPServer((addr,port),MyTCPHandle)#支持多并发,多线程
#每来一个请求,开启一个新的线程。
# class ThreadingTCPServer(ThreadingMixIn, TCPServer):pass
#server=socketserver.ForkingTCPServer((addr,port),MyTCPHandle)#多进程,没进来一个请求,开启一个新的进程。
# 在linix系统上,forkingTCPServer()效果与TheradingTCPServer()相同。
server.serve_forever()
server.server_close()

  对应服务器客户端示例:

# -*- coding:utf-8 -*-
import socket
client=socket.socket()
client.connect(('localhost',8005))
while True:
    msg=input('>>:')
    if msg=='quit':break
    if len(msg)==0:continue
    client.send(msg.encode('utf-8'))
    data=client.recv(1024)
    print(data.decode('utf-8'))
client.close()

  BaseServer的内置方法:   

    # socketserver.BaseServer.
    # fileno()返回文件描述符,不常用
    #handle_request()#处理单个请求
    #server_forever(poll_interval=0.5)#处理多个请求
    #allow_reuse_address允许地址重用
    #timeout超时时间
    #setup()在handle()之前调用
    #handle()请求内容
    #finish()zaihandle()之后调用
    #get_request()获得请求的实例和IP地址

猜你喜欢

转载自www.cnblogs.com/wulihui/p/9394812.html