Python中UDP和TCP编程

Python中UDP和TCP编程

UDP和TCP区别:

  1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
  3. TCP面向字节流实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文, 且UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)。
  4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
  5. TCP首部开销20字节,开销较大;而UDP的首部开销小,只有8个字节。
  6. TCP的逻辑通信信道全双工的可靠信道,UDP则是不可靠信道

UDP编程

  • UDP属于无连接协议,在UDP编程是不需要首先建立连接,而是直接向接收方发送信息
UDP编程常用方法:
  • UDP编程常用到的socket模块方法有3个:
    • socket模块是对Socket模块进行了二次封装,支持Socket接口的访问,大幅度简化了程序开发步骤,提高开发效率。
    1. socket([family[,type[,proto]]]):
      • 创建一个Socket对象,即创建UDP套接字
      • 其中fimily为:
        • socket.AF_INTE 表示 IPV4
        • socket.AF_INTE6 表示 IPV6
      • type为:
        • SOCK_DGRAM 表示 UDP
        • SOCK_STREAM 表示 TCP
          # 创建套接字,使用IPV4协议,使用UDP协议传输数据
          udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
          
    2. sendto(date:bytes, address):
      • 用来发送数据
      • 把date指定的内容发送给address指定的地址
      • 其中address地址是包含接受方主机IP地址应用程序端口号的元组
      • 其中参数date的类型为bytes类型,不能为str类型,否则报错
        • 两种方式将str转换成bytes类型:
          1. string类型前面加一个英文字母b
            • 使用前提:参数string不能为一个变量名,而是字符串
            • 例如
              sendto(==b=="你好啊", ("xxx.xxx.xxx.xxx", 8080))
              
          2. 调用encode()方法:
            • 将str类型转换成为bytes类型。
            • str类型变量名.encode(“utf-8”)或者"你好".encode(“utf-8”)
            • 例如
              sendto(==str_msg.encode("utf-8")==, ("xxx.xxx.xxx.xxx", 8080))
              
    3. recv(bufsize[,flags]):
      • 从套接字接受数据
      • recv()返回接收到的数据
      • 参数bufsize表示本次接收的最大字节数
      • 参数若为1024,则表示1K
      • 参数若为1024*1024,则表示1M
    4. recvfrom(bufsize[,flags]):
      • 从套接字接受数据
      • recvfrom()返回的是(数据, 客户端地址),可以用来接收对端的地址信息,这个对于udp这种无连接的,可以很方便地进行回复
      • 参数bufsize表示本次接收的最大字节数
      • 参数若为1024,则表示1K
      • 参数若为1024*1024,则表示1M
        # 1014表示本次接收的最大字节数
        data, addr = udp_socket.recvfrom(1024)
        
      • 注意
        • recv和recvfrom是可以替换使用的。
        • 而换过来如果你在udp当中也使用recv,那么就不知道该回复给谁了。
        • 如果你不需要回复的话,也是可以使用的。
        • 另外就是对于tcp是已经知道对端的,就没必要每次接收还多收一个地址,没有意义,要取地址信息,在accept当中取得就可以加以记录了。
    5. bind(address)方法
      • 参数address为一个元组(‘本地IP’, 端口号)
      • 绑定本地信息
      • '本地IP参数’若为空字符串,表示本机任何可用IP
        # 绑定本地信息(端口和端口号,空字符串表示本机任何可用IP地址)
        udp_socket.bind(('',5000))
        
    6. gethostname()方法
      • 该方法可以用来获得当前的主机名
      • 返回值为str类型
        import socket
        
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        name = socket.gethostname()
        print(name)
        print(type(name))
        
        结果如下:
            LAPTOP-2B15DENO
            <class 'str'>
        
    7. gethostbyname(name)方法
      • 参数name为主机名(str类型)
      • 该方法可以获取IP地址
      • 返回值为str类型
        import socket
        
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 获取主机名
        name = socket.gethostname()
        # 参数name表示本主机名
        name_ip = socket.gethostbyname(name)
        
        print(name)
        print(type(name))
        print(name_ip)
        print(type(name_ip))
        
        结果如下:
            LAPTOP-2B15DENO
            <class 'str'>
            192.168.43.120
            <class 'str'>
        
UDP发送数据:
  1. 创建套接字
  2. 发送数据
  3. 关闭
    import socket
    
    def main():
       # 创建套接字,使用IPV4协议,使用UDP协议传输数据
       udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
       
       # 输入发送者信息
       receiver_ip = input("请输入接受者IP:")
       receiver_port = int(input("请输入接受者端口号:"))
       
       # 发送数据
       while True:
           udp_msg = input("请输入您要发送的信息(输入exit退出):")
           if udp_msg == "exit":
               break
           udp_socket.sendto(udp_msg.encode("gbk"), (receiver_ip, receiver_port))
       
       # 关闭套接字
       udp_socket.close()
       
       
    if __name__ == "__main__":
       main()
    
UDP接收数据
  1. 创建套接字
  2. 绑定本地信息(IP+端口号)
  3. 接收数据
  4. 关闭
    import socket
    
    def main():
        # 创建套接字,使用IPV4协议,使用UDP传输协议
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
        # 绑定本地接收信息(IP+端口号)
        name = socket.gethostname()
        print("本机名:" + name)
        ip_addr = socket.gethostbyname(name)
        print("本机IP" + ip_addr)
        receive_port = int(input("请输入接收数据的端口号:"))
        print("正在接收数据……")
        # 空字符串表示本机任何可用IP
        udp_socket.bind((ip_addr, receive_port))
    
        # 接收数据
        while True:
            # 2019表示本次接收数据的最大字节数
            udp_msg, udp_addr = udp_socket.recvfrom(2019)
            print("%s : %s" % (udp_addr, udp_msg.decode("gbk")))
    
        # 关闭套接字
        udp_socket.close()
    
    
    if __name__ == "__main__":
    main()
    

TCP编程

  • TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
  • 应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,然后TCP把数据流分区成适当长度的报文段(通常受该计算机连接的网络的数据链路层的最大传输单元( MTU)的限制)。
  • 之后TCP把结果包传给IP层,由它来通过网络将包传送给接收端实体 的TCP层。
  • TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收
  • 然后接收端实体对已成功收到的包发回一个相应的确认(ACK)
    • 如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传
  • TCP用一个校验函数检验数据是否有错误;在发送接收都要计算校验和
服务器端和客户端的区别:
  • 服务器端:就是提供服务的一方
  • 客服端:就是被服务的一方
TCP编程常用方法:
  1. socket([family[,type[,proto]]]):

    • 创建一个Socket对象,即创建UDP套接字
    • 其中fimily为:
      • socket.AF_INTE 表示 IPV4
      • socket.AF_INTE6 表示 IPV6
    • type为:
      • SOCK_DGRAM 表示 UDP
      • SOCK_STREAM 表示 TCP
        # 创建套接字,使用IPV4协议,使用TCP协议传输数据
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
  2. connect(address)方法:

    • 参数address为一个元组(‘本地IP’, 端口号)
    • 连接服务器
      # 连接服务器
      server_ip = input("请输入您要连接的服务器IP:")
      server_port = int(input("请输入服务器port:"))
      tcp_socket.connect((server_ip, server_port))
      
      # 或者:
      tcp_socket.connect(("192.168.43.120", 8080))
      
  3. send(data:bytes)方法:

    • 用来发送数据
    • 其中参数date的类型为bytes类型,不能为str类型,否则报错
      tcp_msg = input("请输入您要发送的数据:")
      tcp_socket.send(tcp_msg.encode("gbk"))
      
  4. recv(bufsize[,flags]):

    • 从套接字接受数据
    • recv()只返回接收到的数据
    • 参数bufsize表示本次接收的最大字节数。
    • 参数若为1024,则表示1K
    • 参数若为1024*1024,则表示1M
  5. bind(address)方法

    • 参数address为一个元组(‘本地IP’, 端口号)
    • 绑定本地信息
    • '本地IP参数’若为空字符串,表示本机任何可用IP
      # 绑定本地信息(端口和端口号,空字符串表示本机任何可用IP地址)  
      tcp_socket.bind(('',5000))
      
  6. listen(backlog)方法

    • backlog指定最多允许多少个客户连接到服务器

    • 它的值至少为1。

    • 收到连接请求后,这些请求需要排队。

    • 如果队列满,就拒绝请求

    • backlog应该理解为阻塞队列的长度,总共与服务器连接的客户端一共有 backlog + 1 个。

    • 阻塞队列FIFO,当连接客户端结束后阻塞队列里的第一个客服端与服务器连接成功。

  7. accept()方法

    • accept()接受一个客户端的连接请求
    • 并返回一个元组,里面包含一个新的套接字和链接则信息的元组(IP+端口号)
    • 返回值为(新的套接字,(IP, 端口号))
    • 新的套接字,不同于以上socket()返回的用于监听和接受客户端的连接请求的套接字;与此客户端通信是通过这个新的套接字发送和接收数据来完成的。
TCP客户端构建流程:
  1. 创建套接字
  2. 链接服务器
  3. 发送数据
  4. 关闭套接字
    import socket
    
    def main():
        # 创建套接字,使用IPV4协议,使用TCP传输协议
        tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
        # 连接服务器
        server_ip = input("请输入您要连接的服务器IP:")
        server_port = int(input("请输入服务器port:"))
        tcp_socket.connect((server_ip, server_port))
    
        # 发送信息
        tcp_msg = input("请输入您要发送的数据:")
        tcp_socket.send(tcp_msg.encode("gbk"))
    
        # 接收数据:
        receive_msg = tcp_socket.recv(1024)
        print("接收到的数据为:" + receive_msg.decode("gbk"))
    
        # 关闭套接字
        tcp_socket.close()
    
    
    if __name__ == "__main__":
        main()
    
TCP服务器端构建流程
  1. 创建套接字
  2. bind绑定本地信息
  3. listen让默认的套接字由主动变为可以被动连接
  4. accept等待客户端的链接
  5. recv/send接收、发送数据
    import socket
    
    def main():
        # 创建套接字,使用IPV4协议,使用TCP传输协议
        tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
        # 绑定本地信息
        tcp_socket.bind(("192.168.43.120", 6666))
    
        # 让默认套接字由主动变成被动
        # 这里的参数表示服务器阻塞队列的长度!
        tcp_socket.listen(128)
    
        # 调用多次accept,从而为多个客户端服务
        while True:
            # 等待客户端的链接
            print("等待客户端链接……")
            new_tcp_socket, client_addr = tcp_socket.accept()
            print("客户端链接成功!")
            print("客户端信息:" + str(client_addr))
    
            # 循环带刺为同一个客户端服务多次
            while True:
    
                # 接收客户端发送的数据
                recv_msg = new_tcp_socket.recv(1024)
                print(recv_msg.decode("gbk"))
    
                '''
                如果recv解堵塞,那么有两种方式:
                1. 客户端发送过来了数据
                2. 客户端调用了close导致了 recv解堵塞
                '''
                if recv_msg:
                    # 会送一部分数据给客服端
                    new_tcp_socket.send("----ok----".encode("gbk"))
                else:
                    break
    
            # 关闭新的套接字
            '''
            关闭accept()返回的套接字
            则意味着 为该客户端服务结束
            '''
            new_tcp_socket.close()
    
        # 关闭监听套接字
        '''
        如果将监听套接字关闭了
        那么会导致不能在等待新的客户端的到来
        即tcp_socket.accept()会出现异常
        '''
        tcp_socket.close()
    
    if __name__ == "__main__":
        main()
    
当客户端的套接字调用close的时候,服务器端会recv解堵塞,并返回的长度为0,因为,服务器可以通过recv返回值的长短来判断客户端是否下线!
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值