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

    一、网络编程

    二、实现网络通讯的条件

      1、物理连接介质

      2、需要统一通讯标准

        OSI七层协议

      3、TCP建立连接的三次握手和四次挥手

    三、什么是socket

    Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,

    对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

    所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

    也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,
    而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序 而程序的pid是同一台机器上不同进程或者线程的标识

      socket 套接字

      是介于应用层和传输层之间的封装了 网络层 数据层 物理成 可以直接调用socket接口 帮我们实现通讯

    五、解决TCP的粘包问题

    该模块可以把一个类型,如数字,转成固定长度的byte

    1 服务端 

    同过导入 import Struct 模块

      制作报头

      发送报头

      发送字典

      发送真实信息

    客户端f

      接收报头

      解析报头 获取head_size 获取字典长度 =struct(''i", head_dic)[0]  

      获取真实数据的字典

      head_bytes = json.loads(head_bytes.decode('utf-8'))

      # 获取的字典的大小的key  文件名和文件的大小

      # 进行文件的操作

      打开文件的 ‘wb’

      while recv_size < fiel_size :

        data = conn/client .recv(1024)

        #别忘了写入文件的方法

        # f.write(data)

        recv_size += len(data)

      

    六、基于socket 上传和下载电影

      1、基于Socket大文件传输

      客户上传电影

    客户端上传电影
    import socket
    import os
    import json
    import struct
    
    #
    client = socket.socket()
    #
    # # 建立与服务端的链接
    client.connect(('127.0.0.1', 8080))
    #
    # # 通讯循环
    while True:
        # 获取电影列表
        Movie_path = r'F:day29视频'
        movie_list = os.listdir(Movie_path)
        for index, movie in enumerate(movie_list,1):
            # 0,1,2,3  1 2 3 4
            print(index, movie)
        choice = input('输入电影的编号:').strip()
        if choice.isdigit():
            choice = int(choice)-1 # 0 1 2 3
            # len() 4          0  4 v  0 1 2 3
            if choice in range(0,len(movie_list)):
                file_name = movie_list[choice]
                # 拼接电影文件的路径
                movie_path = os.path.join(Movie_path,file_name)
    
                # 获取每一步电影文件大小
                file_size = os.path.getsize(movie_path)
                # 产生一个字典
                file_title = '精彩电影100部'
    
                my_dic = {
                    'file_name': file_name,
                    'file_size': file_size,
                    'file_title': file_title,
                    'other': '待续'
                }
                # 序列化字典
                json_dic = json.dumps(my_dic)
                # 转成二进制字典
                json_bytes = json_dic.encode('utf-8')
                # 制作字典报头
    
                head = struct.pack('i', len(json_bytes))
                # 发送报头
                client.send(head)
                # 发送二进制字典
                client.send(json_bytes)
    
                # 发送真实数据
                with open(movie_path, 'rb')as f:
                    for line in f:
                        client.send(line)
    
            else:
                print('序号不在范围内')
    
        else:
            print('请输入数字')

      服务端保存电影

    # 从服务端接收文件
    import struct
    import json
    
    
    import socket
    server = socket.socket()
    
    # 接收电影
    server.bind(('127.0.0.1', 8080))
    # 设置半链接池
    server.listen(5)
    # 链接通讯
    while True:
        # 等待链接
    
        conn, addr = server.accept()  # 等待链接
            # 通讯循环
    
        while True:
            try:  # 捕获文件
                # 开始接收信息
                # 接收报头
                head_dic = conn.recv(4)
                # 解析报头 获取大小
                head_size = struct.unpack('i',head_dic)[0]
                # 接收字典数据
                head_bytes = conn.recv(head_size)
                # 反序列化获取真实字典
                json_dic = json.loads(head_bytes.decode('utf-8'))
                print(json_dic)
                # {'file_name': '精彩电影100部',
                # 'file_size': 50835588, 'other': '待续'}
                file_name = json_dic.get('file_name')
                file_size = json_dic.get('file_size')
    
                recv_size = 0
                with open(file_name, 'wb')as f:
                    while recv_size < file_size:
                        data = conn.recv(1024)
                        f.write(data)
                        recv_size += len(data)
                    # print(recv_size.decode('utf-8'))
                    print('上传成功%s' % file_name)
    
            except ConnectionRefusedError as e:
                print(e)
                break
        conn.close()

    七、TCP 和UDP的区别 

      1.TCP和UDP 的特点

      

    
    
    
    1 有无连接:
    TCP面向连接协议, 必须建立双向通道 传输数据的可靠性 eg:打电话 相互传输数据)
    Web浏览器;电子邮件、文件传输程序。
    ——UDP是无连接服务,数据不可靠 eg: 发短信 不需要回复
    

     

    2. 传输方式:>>>(粘包的问题)
       TCP的特点是介于流式协议 会将内容比较小和 时间间隔较短的 一次性发送 (产生粘包的问题)
     ——UDP是基于数据报传输自带报头 不会产生粘包的问题
    传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。
    使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
    
    
    3.传输效率:
    TCP 需要建立双向通道 传输效率低
    --UDP 无连接服务 传输效率个高

     UDP的特点


    1.udp协议客户端允许发空
    2.udp协议不会粘包
    3.udp协议服务端不存在的情况下,客户端照样不会报错?
    4.udp协议支持并发

      

    八、实现UDP 简易版QQ通(并发)

      服务端

    # 服务端
    
    import socket
    
    server = socket.socket(type=socket.SOCK_DGRAM)
    
    server.bind(('127.0.0.1', 8080))
    
    while True:
        # 通讯
        # 接受信息
        data , addr = server.recvfrom(1024)
        print(data.decode('utf-8'))
    
        # 发送信息
        cmd = input('>>>:').encode('utf-8')
        server.sendto(cmd, addr)
    
        # 记得发送对方的地址

    服务端1

    #  客户端
    import socket
    
    # 获取对像的ip 地址
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    server_addrs = ('127.0.0.1', 8080)
    
    while True:
        # 发送信息
        msg = input('输入的信息:')
        msg = '来自客户端1的消息:%s' % msg
        client.sendto(msg.encode('utf-8'), server_addrs)
        # 接收信息
        data, addr = client.recvfrom(1024)
        print(data.decode('utf-8'))

    客户端2

    #  客户端
    import socket
    
    # 获取对像的ip 地址
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    server_addrs = ('127.0.0.1', 8080)
    
    while True:
        # 发送信息
        msg = input('输入的信息:')
        msg = '来自客户端2的消息:%s' % msg
        client.sendto(msg.encode('utf-8'), server_addrs)
        # 接收信息
        data, addr = client.recvfrom(1024)
        print(data.decode('utf-8'))

    客户端3

    #  客户端
    import socket
    
    # 获取对像的ip 地址
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    server_addrs = ('127.0.0.1', 8080)
    
    while True:
        # 发送信息
        msg = input('输入的信息:')
        msg = '来自客户端3的消息:%s' % msg
        client.sendto(msg.encode('utf-8'), server_addrs)
        # 接收信息
        data, addr = client.recvfrom(1024)
        print(data.decode('utf-8'))

    七、如何实现TCP的并发

      1、Socketserve模块的运用

      客服端

    # 基于TCP如何 实现并发
    
    # 发送消息给服务端
    import socket
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    
    while True:
        msg = input('输入信息:').strip()
        # 发送信息
        client.send(msg.encode('utf-8'))
    
        data = client.recv(1024)
        print(data.decode('utf-8'))


    # 基于TCP如何 实现并发
    
    # 发送消息给服务端
    import socket
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    
    while True:
        msg = input('输入信息:').strip()
        # 发送信息
        client.send(msg.encode('utf-8'))
    
        data = client.recv(1024)
        print(data.decode('utf-8'))



    # 基于TCP如何 实现并发
    
    # 发送消息给服务端
    import socket
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    
    while True:
        msg = input('输入信息:').strip()
        # 发送信息
        client.send(msg.encode('utf-8'))
    
        data = client.recv(1024)
        print(data.decode('utf-8'))
    可以同时发送信息 服务端 并发量

      服务端

    # 基于的socketserver 实现并发
    
    import socketserver
    # class TCPServer(BaseServer):
    
    
    class Myserver(socketserver.BaseRequestHandler):
        def handle(self):
            while True:
                data = self.request.recv(1024)
                print(data.decode('utf-8'))
                print(self.client_address)  # 客户端的地址
                self.request.send(data.upper())  # 发送信息
    
                # HSHSH  客户端发送过来的信息
                # ('127.0.0.1', 14657)
                # TTT
                # ('127.0.0.1', 14659)
                # YYYY
                # ('127.0.0.1', 14660)
                # UUU
                # ('127.0.0.1', 14661)
                # OOO
                # ('127.0.0.1', 14663)
    
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), Myserver)  # 创建一个基于TCP的server对象
        server.serve_forever()  # 启动该服务对对象

      2、UDP基于Sockserver实现并发

    UDP服务端

    # UDP实现的并发
    
    import socketserver
    
    
    # 继承一个类
    class Myserver(socketserver.BaseRequestHandler):
    
        def handle(self):
            while True:
                data, sock = self.request
                print(data.decode('utf-8'))
                print(self.client_address)
                # 发送信息
                # msg = input('输入信息:').encode('utf-8')
                sock.sendto(data, self.client_address)
    
    
    if __name__ == '__main__':
        # 产生一个TCP 对象
        server = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), Myserver)
        # 启动改对象
        server.serve_forever()

    多个客户端

    # UDP的并发实现
    
    import socket
    # 产生一个对象
    import time
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    # 需要获取服务端的通信地址‘
    server_addrs = ('127.0.0.1',8080)
    
    # 开始通讯
    while True:
        # 发送信息
        client.sendto(b'hello baby ', server_addrs)
    
        # 接收信息
        data, addr = client.recvfrom(1024)
        print(data.decode('utf-8'))
        print(addr)
        time.sleep(1)

     客户端2

    # UDP的并发实现
    
    import socket
    # 产生一个对象
    import time
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    # 需要获取服务端的通信地址‘
    server_addrs = ('127.0.0.1',8080)
    
    # 开始通讯
    while True:
        # 发送信息
        client.sendto(b'hello baby ba', server_addrs)
    
        # 接收信息
        data, addr = client.recvfrom(1024)
        print(data.decode('utf-8'))
        print(addr)
        time.sleep(1)

    客户端

    # UDP的并发实现
    
    import socket
    # 产生一个对象
    import time
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    # 需要获取服务端的通信地址‘
    server_addrs = ('127.0.0.1',8080)
    
    # 开始通讯
    while True:
        # 发送信息
        client.sendto(b'hello', server_addrs)
    
        # 接收信息
        data, addr = client.recvfrom(1024)
        print(data.decode('utf-8'))
        print(addr)
        time.sleep(1)

    八、异常处理(异常捕获)

      1、 常见异常

    # try:
    #     name
    #     l = [1,2,3]
    #     l[111]
    #     d = {'name':'jason'}
    #     d['password']
    # except NameError:
    #     print('NameError')
    # except IndexError:
    #     print('indexerror')
    # except KeyError:
    #     print('keyerror')
    """
    错误发生之后  会立刻停止代码的运行
    执行except语句 比对错误类型
    """
    
    # try:
    #     # name
    #     # l = [1,2,3]
    #     # l[111]
    #     d = {'name':'jason'}
    #     d['password']
    #     # Exception
    # except BaseException:  # 万能异常  所有的异常类型都被捕获
    #     print('老子天下无敌')
    
    
    
    
    
    # try:
    #     # name
    #     l = [1,2,3]
    #     l[111]
    #     # d = {'name':'jason'}
    #     # d['password']
    # except Exception:  # 万能异常  所有的异常类型都被捕获
    #     print('老子天下无敌')
    # else:
    #     print('被检测的代码没有任何的异常发生 才会走else')
    # finally:
    #     print('无论被检测的代码有没有异常发生 都会在代码运行完毕之后执行我')
    
    
    # 主动抛异常
    # if 'egon' == 'DSB':
    #     pass
    # else:
    #     raise TypeError('尽说大实话')
    # 关键字raise就是主动抛出异常
    
    
    # l = [1,2,3]
    # assert len(l) < 0  # 断言  预言
    # 猜某个数据的状态 猜对了 不影响代码执行 正常走
    # 猜错了  直接报错
    
    # 自定义异常
    #9 自定义异常
    class MyError(BaseException):
         def __init__(self,msg):
             super().__init__()
             self.msg=msg
         def __str__(self):
             return '<dfsdf%ssdfsdaf>' %self.msg
    
    raise MyError('我自己定义的异常')  # 主动抛出异常其实就是将异常类的对象打印出来,会走__str__方法

    补充如果 端口号 已被使用 already use 

    #加入一条socket配置,重用ip和端口
    
    server=socket(AF_INET,SOCK_STREAM)
    server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
    server.bind(('127.0.0.1',8080))
  • 相关阅读:
    项目中docker swarm实践
    Spring的分模块开发的配置
    单点登录
    在服务器搭建git服务器
    Learn Git Lesson06
    kafka Poll轮询机制与消费者组的重平衡分区策略剖析
    gulp初体验
    vue-cli3中axios如何跨域请求以及axios封装
    vue-cli3中怎么配置vue.config.js文件
    svn的下载与安装,使用,包教包会!!!
  • 原文地址:https://www.cnblogs.com/mofujin/p/11324102.html
Copyright © 2011-2022 走看看