• TCP和UDP


    目录:

        TCP流式协议

          TCP模板

          TCP聊天室

          TCP通信与连接循环

          TCP粘包

          socketserver实现并发

        UDP数据报协议

          UDP模板  

          UDP传输

          socketserver实现并发

    TCP  可靠的传输
        TCP传输数据前需要三次握手建立连接
    
    UDP  不可靠传输
        直接发送数据包,不关心对方是否接收成功

    TCP传输:流式协议

    TCP服务端:必须先启动服务端

    import socket
    # 1.创建一个代表服务器的socket对象
    s = socket.socket()
    
    # 2.绑定端口号和IP地址
    # 127.0.0.1 表示当前这个电脑的ip
    address = ("127.0.0.1", 8080)
    s.bind(address)
    print("服务器已启动!")
    
    # 3.开始监听这个端口
    # 5表示:可以有5个处于半连接状态的连接,即连接请求数,指的不是最大连接数
    s.listen(5)
    print("test")
    
    # 4.接受连接请求
    # 该函数是阻塞的 会卡主程序的执行,必须等到有一个客户端进来才会继续执行
    # 返回元组  第一个是代表客户端的socket对象 第二客户端的地址信息
    conn, c_address = s.accept()
    print("有一个连接已建立!")
    print(c_address)
    # 给客户端发送数据
    
    # 5.读写数据
    # 接受数据
    res = conn.recv(1024)
    print(res)
    
    # 6.关闭连接
    s.close()

    TCP客户端:建立连接之前一定要先启动服务端

    import socket
    
    # 1.创建客户端的socket对象
    c = socket.socket()
    
    # 2.建立连接
    c.connect("127.0.0.1",8080)
    # 3.读写数据 
    # 发送数据到服务器
    c.send("hello i,m client!".encode("utf-8"))

    # 5.关闭连接 c.close()

    TCP模板:

    服务端:
     1 import socket
     2 
     3 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     4 
     5 server.bind(("127.0.0.1",9999))
     6 
     7 server.listen(5)
     8 
     9 while True:
    10     c,addr = server.accept()
    11     while True:
    12         try:#window系统客户端关闭,会报错
    13             msg = c.recv(1024).decode("utf-8")
    14             if not msg:#规避Linux系统客户端异常关闭,会持续收空
    15                 c.close()
    16                 break
    17             c.send(msg.upper().encode("utf-8"))
    18         except BaseException:
    19             print("客户端异常断开")
    20             c.close()
    21             break
    22 server.close()
    客户端:
     1 import socket
     2 
     3 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     4 
     5 client.connect(("127.0.0.1",9999))
     6 
     7 while True:
     8         data = input(">>>:")
     9         if not data:continue#规避发空
    10         client.send(data.encode("utf-8"))
    11         msg = client.recv(1024).decode("utf-8")
    12         print(msg)
    13 
    14 client.close() 

    UDP传输:数据报协议(数据不管是不是空,都会包含头信息)

    UDP服务端:不用先启动

    import socket
    # 1.创建socket对象
    s = socket.socket(type=socket.SOCK_DGRAM)
    
    # 2.绑定端口和ip
    s.bind(("127.0.0.1",10000))
    
    while True:
    # 3.接受数据
        res = s.recv(1024)
        print(res)
    while True:
        msg = input(">>>:")
        # 需要获取对方的ip和端口
        # s.sendto(msg.encode("utf-8"), ("127.0.0.1", 10000))
    
    # 4.关闭资源
    s.close()

    UDP客户端:不用建立连接,直接发就行,不在乎对方是否收到

    import socket
    # 1.创建socket对象
    c = socket.socket(type=socket.SOCK_DGRAM)
    while True:
        msg = input(">>>:")#发空并不会卡住,因为数据包含报头,服务端接收到内容了
      #2.发送消息 c.sendto(msg.encode("utf-8"),("127.0.0.1",10000))
    c.close()

    UDP模板:

    服务端:
     1 import socket
     2 
     3 # 创建socket对象  指定type参数为socket.SOCK_DGRAM 表示使用UDP协议
     4 server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # datagram数据报的意思
     5 # 绑定ip和端口
     6 server.bind(("127.0.0.1",8888))
     7 
     8 while True:
     9     # 接收数据 返回一个元祖  数据和 发送方的地址
    10     msg,c_addr = server.recvfrom(1024)
    11     print("收到来自%s: 说:%s" % (c_addr[0] ,msg.decode("utf-8")))
    12     # 发送数据到指定ip和端口
    13     server.sendto(msg.upper(),c_addr)
    客户端:
    1 import socket
    2 
    3 client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # datagram数据报的意思
    4 
    5 while True:
    6     msg = input(">>>:")#UDP协议可以发空
    7     client.sendto(msg.encode("utf-8"),("127.0.0.1",8888))
    8     data,addr = client.recvfrom(1024)
    9     print(data.decode("utf-8")) 

    TCP简易聊天室:

    服务器端:

    import socket
    server = socket.socket()
    server.bind(("127.0.0.1",65535))
    server.listen(5)
    # 得到客户端的socket对象和客户端的地址 conn, c_address = server.accept() while True: data = conn.recv(1024).decode("utf-8") print("收到来自客户端的数据:",data) # 如果对方发来一个over 我就关闭连接 if data == "over": conn.close() break # 把对方传过来的数据转换为大写 在发回去! conn.send(data.upper().encode("utf-8")) # 关闭服务器端 server.close()

    客户端:

    import socket
    client = socket.socket()
    
    client.connect(("127.0.0.1",65535))
    
    while True:
        # 发送
        client.send(input(">>>:").encode("utf-8"))
    
        # 接收
        data = client.recv(1024).decode("utf-8")
        print(data)
        if len(data) == 0:
            client.close()
            break

    TCP连接与通信循环

    服务端:

    import socket
    #1、买手机
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp称为流式协议,udp称为数据报协议SOCK_DGRAM
    # print(phone)
    
    #2、插入/绑定手机卡
    # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8080))
    
    #3、开机
    phone.listen(5) # 半连接池,限制的是请求数
    
    #4、等待电话连接
    print('start....')
    while True: # 连接循环
        conn,client_addr=phone.accept() #(三次握手建立的双向连接,(客户端的ip,端口))
        # print(conn)
        print('已经有一个连接建立成功',client_addr)
    
        #5、通信:收发消息
        while True: # 通信循环
            try:
                print('服务端正在等待收数据...')
                data=conn.recv(1024) #最大接收的字节数,没有数据会在原地一直等待收,即发送者发送的数据量必须>0bytes
                if len(data) == 0:break #在客户端单方面断开连接,服务端才会出现收空数据的情况.windows系统报错(所以捕捉异常).Linux进入死循环(Linux会收空)
                print('来自客户端的数据',data)
                conn.send(data.upper())
            except ConnectionResetError:
                break
        #6、挂掉电话连接
        conn.close()
    
    #7、关机
    phone.close()

    客户端:

    import socket
    
    #1、买手机
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # print(phone)
    #2、拨电话
    phone.connect(('127.0.0.1',8080)) # 指定服务端ip和端口
    
    #3、通信:发收消息
    while True: # 通信循环
        msg=input('>>: ').strip() #msg=''
        if len(msg) == 0:continue #规避了发送数据为空
        phone.send(msg.encode('utf-8'))
        # print('has send----->')
        data=phone.recv(1024)
        # print('has recv----->')
        print(data)
    
    #4、关闭
    phone.close()

    UDP传输:没有粘包,缓冲区要>数据包,<512

    服务端:
     1 import socket
     2 
     3 server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #数据报协议-》udp
     4 server.bind(('127.0.0.1',8080))
     5 
     6 data,client_addr=server.recvfrom(1024) #b'hello'==>b'h'
     7 print('第一次:',client_addr,data)
     8 
     9 data,client_addr=server.recvfrom(1024) #b'world' =>b'world'
    10 print('第二次:',client_addr,data)
    11 
    12 data,client_addr=server.recvfrom(1024)
    13 print('第三次:',client_addr,data)
    14 
    15 server.close()
    16 
    17 # 第一次: ('127.0.0.1', 52259) b'hello'
    18 # 第二次: ('127.0.0.1', 52259) b'world'
    19 # 第三次: ('127.0.0.1', 52259) b''
    客户端:
    1 import socket
    2 
    3 client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #数据报协议-》udp
    4 
    5 client.sendto('hello'.encode('utf-8'),('127.0.0.1',8080))
    6 client.sendto('world'.encode('utf-8'),('127.0.0.1',8080))
    7 client.sendto(''.encode('utf-8'),('127.0.0.1',8080)) #发空也可以传输,数据报协议,发送报头
    8 
    9 client.close()

     TCP用socketserver实现并发:

    服务端:
     1 import socketserver
     2 from threading import current_thread
     3 # fork linux下一个多进程接口 windows没有这接口
     4 
     5 # 用于处理请求的类
     6 class MyHandler(socketserver.BaseRequestHandler):
     7     def handle(self):
     8         print(self)
     9         print(self.server)  # 获取封装的服务器对象
    10         print(self.client_address)# 客户端地址
    11         print(self.request)# 获取客户端的socket对象
    12         print(current_thread())
    13         #通讯循环
           while True: 14 data = self.request.recv(1024) 15 print(data.decode("utf-8")) 16 self.request.send(data.upper()) 17 18 server = socketserver.ThreadingTCPServer(("127.0.0.1",9999),MyHandler) 19 server.serve_forever()# 代表连接循环
       # 循环建立连接,每建立一个连接就会启动一个线程(服务员)+调用Myhanlder类产生一个对象,调用该对象下的handle方法,专门与刚刚建立好的连接做通信循环
    客户端1:
    1 import socket
    2 
    3 c = socket.socket()
    4 c.connect(("127.0.0.1",9999))
    5 
    6 while True:
    7     msg = input(">>>:")
    8     c.send(msg.encode("utf-8"))
    9     print(c.recv(1024).decode("utf-8")) 
    客户端2:
    相同代码

        图解:实现连接循环和通信循环

    UDP用socketserver实现并发:

     UDP本身可以实现并发,但是并发量大的情况下会变很慢

    服务端:
    ThreadingTCPServer
        handler 在连接成功时执行
        self.request 是客户端的socket对象  
        
    ThreadingUDPServer
        handler 接收到数据时执行
        self.request  数据和服务器端的socket对象  
     1 import socketserver
     2 from threading import current_thread
     3 
     4 # fork linux下一个多进程接口 windows没有这接口
     5 # 用于处理请求的类
     6 class MyHandler(socketserver.BaseRequestHandler):
     7     def handle(self):
           #通信循环,只有在客户端发来消息时才会得到client_address和request
    8 print(self) 9 print(self.server) # 获取封装的服务器对象 10 print(self.client_address) # 客户端地址 11 print(self.request) # 是一个元祖 包含收到的数据 和服务器端的socket 12 # data,client = self.request 13 14 data = self.request[0] 15 print(data.decode("utf-8")) 16 self.request[1].sendto(b"i am server", self.client_address) #self.request[1],拿到的是服务端的socket对象 17 19 server = socketserver.ThreadingUDPServer(("127.0.0.1", 9999), MyHandler) 20 server.serve_forever()
    客户端1:产生一个线程
     1 import socket
     2 
     3 c = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
     4 addr = ("127.0.0.1",9999)
     5 
     6 while True:
     7     msg = input(">>>:")
     8     c.sendto(msg.encode("utf-8"),addr)
     9 
    10     print(c.recvfrom(1024)[0].decode("utf-8")) 
    客户端2:再产生一个线程
    同上代码 

        图解:实现通信循环

  • 相关阅读:
    Java学习笔记二.2
    Java学习笔记二.1
    Java学习笔记一
    cookie和session笔记
    编码知识笔记
    新手前端笔记之--css盒子
    新手前端笔记之--初识css
    新手前端笔记之--必备的标签
    新手前端笔记之--初识html标签
    二叉树总结
  • 原文地址:https://www.cnblogs.com/xuechengeng/p/9910789.html
Copyright © 2020-2023  润新知