码迷,mamicode.com
首页 > 其他好文 > 详细

socket用法(TCP, UDP)

时间:2020-06-05 21:17:12      阅读:78      评论:0      收藏:0      [点我收藏+]

标签:客户端   font   适合   数字   back   阅读   建立   挥手   cli   

socket : 通络通信过程中,信息拼接的工具(中文:套接字)
开发中,一个端口只对一个程序生效,在测试时,允许端口重复捆绑 (开发时删掉), 在bind方法之前加上这句话,可以让一个端口重复使用: sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

TCP: 服务端和客户端简单的一次性通信

# 服务端
import socket
# 1. 创建socket对象
sk = socket.socket()

# 2. 绑定对应IP和端口(注册网络,让其他主机能够找到)
sk.bind(("127.0.0.1",9000))  # 127.0.0.1 代表本地IP

# 3. 开启监听
sk.listen()

# 4. 建立三次握手 addr接收客户端的IP和端口号
conn,addr = sk.accept()

# 5. 收发数据(recv里面的参数单位是字节,表示一次最多接收多少数据)
res = conn.recv(1024)
print(res)
print(res.decode("utf-8"))

# 6. 四次挥手
conn.close()

# 7. 退还端口
sk.close()



# 客户端
import socket

# 1. 创建一个socket对象
sk = socket.socket()

# 2. 与服务器进行连接
sk.connect(("127.0.0.1",9000))

# 3. 发送数据
sk.send("今天下了一天的雨".encode("utf-8"))

# 4. 关闭连接
sk.close()

 

TCP: 服务端和客户端循环通信

# TCP循环通信:
# 服务端
import socket
# 1. 创建一个socket对象
sk = socket.socket()
# 2. 在网络中注册主机(绑定IP和端口)
sk.bind(("127.0.0.1",9000))
# 3. 监听
sk.listen()
# 4. 建立三次握手
# 5. 接收数据
while True:   # 保证服务端永不关闭,随时能跟客户端连接
    conn, addr = sk.accept()
    while True:
        res = conn.recv(1024)
        print(res.decode("utf-8"))
        str1 = input("服务端想给客户端发送: ")
        conn.send(str1.encode("utf-8"))
        if str1.upper() == "Q":
            break

# 6.四次挥手
conn.close()
# 7.退还端口
sk.close()



# 客户端
import socket
# 1. 建立一个socket对象
sk= socket.socket()
# 2. 连接服务端
sk.connect(("127.0.0.1",9000))
# 3. 发送数据
while True:
    strvar = input("客户端想给服务端发送的消息为: ")
    sk.send(strvar.encode("utf-8"))
    res = sk.recv(1024)
    if res.upper() == b"Q":
        break
    print(res.decode("utf-8"))

# 4.断开连接
sk.close()

 

TCP的黏包现象: 两种原因, 一种是数据发得太快,一种是数据收得太慢

# TCP的黏包现象
# 服务端
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
coon,addr = sk.accept()

coon.send("下了一天的雨".encode())
coon.send("适合发呆".encode())
coon.close()
sk.close()


# 客户端
import socket
import time
sk = socket.socket()
sk.connect(("127.0.0.1",9000))
time.sleep(0.1)

res1 = sk.recv(1024)
print(res1.decode())
res2 = sk.recv(1024)
print(res2.decode())
# 下了一天的雨适合发呆  两个数据粘在了一起,这就是数据的黏包

sk.close()

TCP黏包解决方式:

# TCP的黏包现象
# 服务端
# 方式一:
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
coon,addr = sk.accept()
# 在发送真实数据之前,先发送真实数据的大小
coon.send("18".encode())
coon.send("下了一天的雨".encode())
coon.send("适合发呆".encode())
coon.close()
sk.close()


# 客户端
import socket
import time
sk = socket.socket()
sk.connect(("127.0.0.1",9000))
time.sleep(0.1)

res = sk.recv(2)
n = int(res.decode())

res1 = sk.recv(n)
print(res1.decode())
res2 = sk.recv(1024)
print(res2.decode())
# 下了一天的雨
# 适合发呆

sk.close()

##############
# TCP的黏包现象
# 服务端
# 方式二:
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
coon,addr = sk.accept()
# 在发送真实数据之前,先发送真实数据的大小
coon.send("00000018".encode())  # 以免客户端每次都要改第一次接收字节数
coon.send("下了一天的雨".encode())
coon.send("适合发呆".encode())
coon.close()
sk.close()


# 客户端
import socket
import time
sk = socket.socket()
sk.connect(("127.0.0.1",9000))
time.sleep(0.1)

res = sk.recv(8)  # 将第一次接受的大小固定
n = int(res.decode())

res1 = sk.recv(n)
print(res1.decode())
res2 = sk.recv(1024)
print(res2.decode())
# 下了一天的雨
# 适合发呆

sk.close()

# 但是方式一和方式二都太麻烦,每次都需要改动

TCP黏包解决方式三(推荐)

"""
pack: 把任意长度的数字转化成居右4个字节的固定长度的字节流

unpack: 把4个字节值恢复成原本的数字,返回是元组
# 整型用i表示, 浮点型用f表示, 字符串用s表示
"""

# TCP的黏包现象
# 服务端
# 方式三: 引入struct
import socket
import struct
sk = socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
coon,addr = sk.accept()

strvar = input("请输入数据:")
msg = strvar.encode()
length = len(msg)
res = struct.pack("i",length)  # 将要发送的第一个数据的长度先用struct进行压缩
coon.send(res)
coon.send(strvar.encode())
coon.send("适合发呆".encode())
coon.close()
sk.close()


# 客户端
import socket
import time
import struct
sk = socket.socket()
sk.connect(("127.0.0.1",9000))
time.sleep(0.1)

res = sk.recv(4)  # struct压缩的数据永远都是4个字节
tup = struct.unpack("i",res)
print(tup[0])  # 18

res1 = sk.recv(tup[0])
print(res1.decode())
res2 = sk.recv(1024)
print(res2.decode())
# 下了一天的雨
# 适合发呆

sk.close()

解决黏包场景: 应用场景在实时通讯时,需要阅读此次发的消息是什么
不需要解决黏包场景: 下载或者上传文件的时候,最后要把包都结合在一起,黏包无所谓.

udp数据传输简单通信
# dup 数据传输
# 服务端
import socket
# 1. 创建一个socket对象 type=socket.SOCK_DGRAM表明创建的是UDP协议对象
sk = socket.socket(type=socket.SOCK_DGRAM)

# 2. 绑定IP和端口号
sk.bind(("127.0.0.1",9000))

# 4. 接收消息,(UDP作为服务端的时候,第一次肯定是接收消息)
msg,cli_addr = sk.recvfrom(1024)
print(msg.decode())  # 今天天气咋样啊?
print(cli_addr) # (‘127.0.0.1‘, 64877)

# 发送消息给客户端
msg = "下了一天的雨"
sk.sendto(msg.encode(),cli_addr)

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


# 客户端
import socket
# 1. 创建socket对象
sk = socket.socket(type=socket.SOCK_DGRAM)

# 2. 发送数据
msg = "今天天气咋样啊?"
sk.sendto(msg.encode(),("127.0.0.1",9000))

msg,ser_addr = sk.recvfrom(1024)
print(msg.decode())  # 下了一天的雨
print(ser_addr) # (‘127.0.0.1‘, 9000)

# 3. 断开连接
sk.close()

udp 数据传输循环通信

# udp循环通信
# 服务端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)

sk.bind(("127.0.0.1",9000))

while True:
    while True:
        msg,cli_addr = sk.recvfrom(1024)
        print(msg.decode())

        msg = input("服务端给客户端发送的消息: ")
        sk.sendto(msg.encode(), cli_addr)
        if msg.upper() == "Q":
            break

sk.close()



# 客户端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)

while True:
    msg = input("客户端给服务端发送的消息: ")
    sk.sendto(msg.encode(),("127.0.0.1",9000))

    msg, ser_addr = sk.recvfrom(1024)
    if msg.upper() == b"Q":
        break
    print(msg.decode())

sk.close()

 

socket用法(TCP, UDP)

标签:客户端   font   适合   数字   back   阅读   建立   挥手   cli   

原文地址:https://www.cnblogs.com/fdsimin/p/13052153.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!