• 网络编程---socket模块


    内容中代码都是先写  server端, 再写 client端

    1 TCP和UDP对比

    TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;文件传输程序。

    UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文(数据包),尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。

    2 TCP协议下的socket

    个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理。

      先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

    细说socket()模块函数用法

    import socket
    socket.socket(socket_family,socket_type,protocal=0)
    socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。

    获取tcp/ip套接字
    tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    获取udp/ip套接字
    udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。
    例如tcpSock = socket(AF_INET, SOCK_STREAM)
    服务端套接字函数
    s.bind()    绑定(主机,端口号)到套接字
    s.listen()  开始TCP监听
    s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来

    客户端套接字函数
    s.connect()     主动初始化TCP服务器连接
    s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

    公共用途的套接字函数
    s.recv()            接收TCP数据
    s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
    s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
    s.recvfrom()        接收UDP数据
    s.sendto()          发送UDP数据
    s.getpeername()     连接到当前套接字的远端的地址
    s.getsockname()     当前套接字的地址
    s.getsockopt()      返回指定套接字的参数
    s.setsockopt()      设置指定套接字的参数
    s.close()           关闭套接字

    面向锁的套接字方法
    s.setblocking()     设置套接字的阻塞与非阻塞模式
    s.settimeout()      设置阻塞套接字操作的超时时间
    s.gettimeout()      得到阻塞套接字操作的超时时间

    面向文件的套接字的函数
    s.fileno()          套接字的文件描述符
    s.makefile()        创建一个与该套接字相关的文件

     第一版,单个客户端与服务端通信(low版)

    # 网络通信与打电话(诺基亚)是一样的。

    import socket

    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # 买电话

    phone.bind(('127.0.0.1',8080))  # 0 ~ 65535  1024之前系统分配好的端口 绑定电话卡

    phone.listen(5)  # 同一时刻有5个请求,但是可以有N多个链接。 开机。


    conn, client_addr = phone.accept()  # 接电话
    print(conn, client_addr, sep=' ')

    from_client_data = conn.recv(1024)  # 一次接收的最大限制  bytes
    print(from_client_data.decode('utf-8'))

    conn.send(from_client_data.upper())

    conn.close()  # 挂电话

    phone.close() # 关机
    import socket

    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # 买电话

    phone.connect(('127.0.0.1',8080))  # 与客户端建立连接, 拨号

    phone.send('hello'.encode('utf-8'))

    from_server_data = phone.recv(1024)

    print(from_server_data)

    phone.close()  # 挂电话

    第二版,通信循环

    import socket

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

    phone.bind(('127.0.0.1',8080))

    phone.listen(5)


    conn, client_addr = phone.accept()
    print(conn, client_addr, sep=' ')

    while 1:  # 循环收发消息
        try:
            from_client_data = conn.recv(1024)
            print(from_client_data.decode('utf-8'))
       
            conn.send(from_client_data + b'SB')
       
        except ConnectionResetError:
            break

    conn.close()
    phone.close()
    import socket

    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # 买电话

    phone.connect(('127.0.0.1',8080))  # 与客户端建立连接, 拨号


    while 1:  # 循环收发消息
        client_data = input('>>>')
        phone.send(client_data.encode('utf-8'))
       
        from_server_data = phone.recv(1024)
       
        print(from_server_data.decode('utf-8'))

    phone.close()  # 挂电话

    第三版, 通信,连接循环

    import socket

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

    phone.bind(('127.0.0.1',8080))

    phone.listen(5)

    while 1 : # 循环连接客户端
        conn, client_addr = phone.accept()
        print(client_addr)
       
        while 1:
            try:
                from_client_data = conn.recv(1024)
                print(from_client_data.decode('utf-8'))
           
                conn.send(from_client_data + b'SB')
           
            except ConnectionResetError:
                break

    conn.close()
    phone.close()
    import socket

    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # 买电话

    phone.connect(('127.0.0.1',8080))  # 与客户端建立连接, 拨号


    while 1:
        client_data = input('>>>')
        phone.send(client_data.encode('utf-8'))
       
        from_server_data = phone.recv(1024)
       
        print(from_server_data.decode('utf-8'))

    phone.close()  # 挂电话

    远程执行命令的示例:

    import socket
    import subprocess

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

    phone.bind(('127.0.0.1',8080))

    phone.listen(5)

    while 1 : # 循环连接客户端
        conn, client_addr = phone.accept()
        print(client_addr)
       
        while 1:
            try:
                cmd = conn.recv(1024)
                ret = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                correct_msg = ret.stdout.read()
                error_msg = ret.stderr.read()
                conn.send(correct_msg + error_msg)
            except ConnectionResetError:
                break

      conn.close()
    phone.close()
    import socket

    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # 买电话

    phone.connect(('127.0.0.1',8080))  # 与客户端建立连接, 拨号


    while 1:
        cmd = input('>>>')
        phone.send(cmd.encode('utf-8'))
       
        from_server_data = phone.recv(1024)
       
        print(from_server_data.decode('gbk'))

    phone.close()  # 挂电话

    3UDP协议下的socket

    udp是无链接的,先启动哪一端都不会报错

    UDP下的socket通讯流程

      先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),recvform接收消息,这个消息有两项,消息内容和对方客户端的地址,然后回复消息时也要带着你收到的这个客户端的地址,发送回去,最后关闭连接,一次交互结束

    上代码感受一下,需要创建两个文件,文件名称随便起,为了方便看,我的两个文件名称为udp_server.py(服务端)和udp_client.py(客户端),将下面的server端的代码拷贝到udp_server.py文件中,将下面cliet端的代码拷贝到udp_client.py的文件中,然后先运行udp_server.py文件中的代码,再运行udp_client.py文件中的代码,然后在pycharm下面的输出窗口看一下效果。

     sever端代码示例

    import socket
    udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
    udp_sk.bind(('127.0.0.1',9000))        #绑定服务器套接字
    msg,addr = udp_sk.recvfrom(1024)
    print(msg)
    udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
    udp_sk.close()                         # 关闭服务器套接字

    client端代码示例

    import socket
    ip_port=('127.0.0.1',9000)
    udp_sk=socket.socket(type=socket.SOCK_DGRAM)
    udp_sk.sendto(b'hello',ip_port)
    back_msg,addr=udp_sk.recvfrom(1024)
    print(back_msg.decode('utf-8'),addr)

    类似于qq聊天的代码示例:

    #_*_coding:utf-8_*_
    import socket
    ip_port=('127.0.0.1',8081)
    udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #DGRAM:datagram 数据报文的意思,象征着UDP协议的通信方式
    udp_server_sock.bind(ip_port)#你对外提供服务的端口就是这一个,所有的客户端都是通过这个端口和你进行通信的

    while True:
        qq_msg,addr=udp_server_sock.recvfrom(1024)# 阻塞状态,等待接收消息
        print('来自[%s:%s]的一条消息:33[1;44m%s33[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
        back_msg=input('回复消息: ').strip()

        udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
    import socket
    BUFSIZE=1024
    udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

    qq_name_dic={
        'taibai':('127.0.0.1',8081),
        'Jedan':('127.0.0.1',8081),
        'Jack':('127.0.0.1',8081),
        'John':('127.0.0.1',8081),
    }


    while True:
        qq_name=input('请选择聊天对象: ').strip()
        while True:
            msg=input('请输入消息,回车发送,输入q结束和他的聊天: ').strip()
            if msg == 'q':break
            if not msg or not qq_name or qq_name not in qq_name_dic:continue
            udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])# 必须带着自己的地址,这就是UDP不一样的地方,不需要建立连接,但是要带着自己的地址给服务端,否则服务端无法判断是谁给我发的消息,并且不知道该把消息回复到什么地方,因为我们之间没有建立连接通道

            back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)# 同样也是阻塞状态,等待接收消息
            print('来自[%s:%s]的一条消息:33[1;44m%s33[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))

    udp_client_socket.close()

      接下来,给大家说一个真实的例子,也就是实际当中应用的,那么这是个什么例子呢?就是我们电脑系统上的时间,windows系统的时间是和微软的时间服务器上的时间同步的,而mac本是和苹果服务商的时间服务器同步的,这是怎么做的呢,首先他们的时间服务器上的时间是和国家同步的,你们用我的系统,那么你们的时间只要和我时间服务器上的时间同步就行了,对吧,我时间服务器是不是提供服务的啊,相当于一个服务端,我们的电脑就相当于客户端,就是通过UDP来搞的。

    自制时间服务器的代码示例:

    from socket import *
    from time import strftime
    import time
    ip_port = ('127.0.0.1', 9000)
    bufsize = 1024

    tcp_server = socket(AF_INET, SOCK_DGRAM)
    tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    tcp_server.bind(ip_port)

    while True:
        msg, addr = tcp_server.recvfrom(bufsize)
        print('===>', msg)
        stru_time = time.localtime()  #当前的结构化时间
        if not msg:
            time_fmt = '%Y-%m-%d %X'
        else:
            time_fmt = msg.decode('utf-8')
        back_msg = strftime(time_fmt,stru_time)
        print(back_msg,type(back_msg))
        tcp_server.sendto(back_msg.encode('utf-8'), addr)

    tcp_server.close()
    from socket import *
    ip_port=('127.0.0.1',9000)
    bufsize=1024

    tcp_client=socket(AF_INET,SOCK_DGRAM)

    while True:
        msg=input('请输入时间格式(例%Y %m %d)>>: ').strip()
        tcp_client.sendto(msg.encode('utf-8'),ip_port)

        data=tcp_client.recv(bufsize)
        print('当前日期:',str(data,encoding='utf-8'))
  • 相关阅读:
    饮冰三年-人工智能-Python-19 Python网络编程
    饮冰三年-人工智能-Python-18Python面向对象
    饮冰三年-人工智能-Python-17Python基础之模块与包
    饮冰三年-人工智能-Python-16Python基础之迭代器、生成器、装饰器
    饮冰三年-人工智能-Python-15Python基础之文件处理
    饮冰三年-人工智能-Python-14Python基础之变量与函数
    饮冰三年-人工智能-Python-12之利其器pycharm
    饮冰三年-人工智能-Python-13Python基础之运算符与数据类型
    饮冰三年-人工智能-Python-11之HelloWorld
    前端后端获取ip以及位置以及判断客户端
  • 原文地址:https://www.cnblogs.com/YZL2333/p/10408966.html
Copyright © 2020-2023  润新知