zoukankan      html  css  js  c++  java
  • 网络编程

    Python3 网络编程

    Python 提供了两个级别访问的网络服务。:

    • 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。

    • 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。


    什么是 Socket?

    Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。


    socket()函数

    Python 中,我们用 socket()函数来创建套接字,语法格式如下:

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

    参数

    • family: 套接字家族可以使AF_UNIX或者AF_INET

    • type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAMSOCK_DGRAM

    • protocol: 一般不填默认为0.

    Socket 对象(内建)方法

    函数
    描述
    服务器端套接字
    s.bind()
    绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。
    s.listen()
    开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
    s.accept()
    被动接受TCP客户端连接,(阻塞式)等待连接的到来
    客户端套接字
    s.connect()
    主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
    s.connect_ex()
    connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    公共用途的套接字函数
    s.recv()
    接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
    s.send()
    发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
    s.sendall()
    完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
    s.recvform()
    接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
    s.sendto()
    发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
    s.close()
    关闭套接字
    s.getpeername()
    返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
    s.getsockname()
    返回套接字自己的地址。通常是一个元组(ipaddr,port)
    s.setsockopt(level,optname,value)
    设置给定套接字选项的值。
    s.getsockopt(level,optname[.buflen])
    返回套接字选项的值。
    s.settimeout(timeout)
    设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
    s.gettimeout()
    返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
    s.fileno()
    返回套接字的文件描述符。
    s.setblocking(flag)
    如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
    s.makefile()
    创建一个与该套接字相关连的文件

    简单实例

    服务端

    我们使用 socket 模块的 socket 函数来创建一个 socket 对象。socket 对象可以通过调用其他函数来设置一个 socket 服务。

    现在我们可以通过调用 bind(hostname, port) 函数来指定服务的 port(端口)

    接着,我们调用 socket 对象的 accept 方法。该方法等待客户端的连接,并返回 connection 对象,表示已连接到客户端。

    完整代码如下:

    #!/usr/bin/env python
    # -*-coding:utf-8 -*-
    
    #导入socket块
    import socket
    
    #指定服务器ip,端口
    ip_port = ('127.0.0.1',9999)
    
    #邦定端口
    s = socket.socket()
    s.bind(ip_port)
    s.listen(5)    #设置最大连接数,超过后排队
    
    while True:
        #建立客户端连接
        conn,addr = s.accept()
        #收消息
        while True:
            try:
                recv_data = conn.recv(1024)
                if len(recv_data) == 0:break
                #发消息
                send_data = recv_data.upper()  #把收到的数据转化为大写反回
                print(send_data)
                conn.send(send_data)
            except Exception:
                break
        #断一连接
        conn.close()

     

     

     

    客户端

    接下来我们写一个简单的客户端实例连接到以上创建的服务。端口号为 9999。

    socket.connect(hosname, port ) 方法打开一个 TCP 连接到主机为 hostname 端口为 port 的服务商。连接后我们就可以从服务端后期数据,记住,操作完成后需要关闭连接。

    完整代码如下:

    import socket
    ip_port = ('127.0.0.1',9999)
    
    
    s = socket.socket()
    
    
    s.connect(ip_port)
    
    while True:
        send_data = input(">>>").strip()
        if send_data == 'exit':break   #退出机制
        if len(send_data) == 0:break #防止传递空数据,造成服务器阻塞
        s.send(bytes(send_data,encoding='utf-8'))
        recv_data = s.recv(1024)
        print(str(recv_data))
    
    s.close()

     

     

    注意,启动时先把服务器的程序先启动,后再启动客户端,断开时先关闭客房端再关闭服务器,要不然有可能造成服务器端口被占用等情况

    粘包的解决:

    注意,上面的数据只是每次接收1024个字节,比如当发送的数据为1500字节时,就会产生粘包的问题;要解决这个问题只有一个办法,就是让服务器知道自己需要发送多少长度的数据给客户端

    server:

    import socket
    import subprocess #导入执行命令模块
    ip_port=('127.0.0.1',9999) #定义元祖
    #买手机
    s=socket.socket()  #绑定协议,生成套接字
    s.bind(ip_port)    #绑定ip+协议+端口:用来唯一标识一个进程,ip_port必须是元组格式
    s.listen(5)        #定义最大可以挂起胡链接数
    #等待电话
    while True:  #用来重复接收新的链接
        conn,addr=s.accept()   #接收客户端胡链接请求,返回conn(相当于一个特定胡链接),addr是客户端ip+port
        #收消息
        while True: #用来基于一个链接重复收发消息
                try: #捕捉客户端异常关闭(ctrl+c)
                    recv_data=conn.recv(1024) #收消息,阻塞
                    if len(recv_data) == 0:break #客户端如果退出,服务端将收到空消息,退出
    
                    #发消息
                    p=subprocess.Popen(str(recv_data,encoding='utf8'),shell=True,stdout=subprocess.PIPE) #执行系统命令,windows平
                                                                                                          # 台命令的标准输出是gbk编码,需要转换
                    res=p.stdout.read()   #获取标准输出
                    if len(res) == 0:   #执行错误命令,标准输出为空,
                        send_data='cmd err'
                    else:
                        send_data=str(res,encoding='gbk')  #命令执行ok,字节gbk---->str---->字节utf-8
    
                    send_data=bytes(send_data,encoding='utf8')
    
    
                    #解决粘包问题
                    ready_tag='Ready|%s' %len(send_data)
                    conn.send(bytes(ready_tag,encoding='utf8')) #发送数据长度
                    feedback=conn.recv(1024)  #接收确认信息
                    feedback=str(feedback,encoding='utf8')
    
                    if feedback.startswith('Start'):
                        conn.send(send_data)  #发送命令的执行结果
                except Exception:
                    break
        #挂电话
        conn.close()

    client:

    import socket
    ip_port=('127.0.0.1',9999)
    #买手机
    s=socket.socket()
    #拨号
    s.connect(ip_port)  #链接服务端,如果服务已经存在一个好的连接,那么挂起
    
    while True:        #基于connect建立的连接来循环发送消息
        send_data=input(">>: ").strip()
        if send_data == 'exit':break
        if len(send_data) == 0:continue
        s.send(bytes(send_data,encoding='utf8'))
    
        #解决粘包问题
        ready_tag=s.recv(1024) #收取带数据长度的字节:Ready|9998
        ready_tag=str(ready_tag,encoding='utf8')
        if ready_tag.startswith('Ready'):#Ready|9998
            msg_size=int(ready_tag.split('|')[-1])  #获取待接收数据长度
        start_tag='Start'
        s.send(bytes(start_tag,encoding='utf8')) #发送确认信息
    
        #基于已经收到的待接收数据长度,循环接收数据
        recv_size=0
        recv_msg=b''
        while recv_size < msg_size:
            recv_data=s.recv(1024)
            recv_msg+=recv_data
            recv_size+=len(recv_data)
            print('MSG SIZE %s RECE SIZE %s' %(msg_size,recv_size))
    
        print(str(recv_msg,encoding='utf8'))
        #挂电话
    s.close()
  • 相关阅读:
    洛谷 P1875 佳佳的魔法药水
    洛谷 P4822 [BJWC2012]冻结
    洛谷 P6175 无向图的最小环问题
    洛谷 P1312 Mayan游戏
    洛谷 P1311 选择客栈
    洛谷 T150024 矩形面积并(扫描线)
    洛谷 P1311 选择客栈
    洛谷 P1514 引水入城
    洛谷 P1310 表达式的值
    求和(团队题目)
  • 原文地址:https://www.cnblogs.com/zcx-python/p/5646614.html
Copyright © 2011-2022 走看看