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:再产生一个线程
同上代码 

    图解:实现通信循环

 

posted @ 2018-11-05 18:49  ChuckXue  阅读(258)  评论(0编辑  收藏  举报