- udp
-
工作原理描述:类似于写信,知道收件方地址之后,就将信寄出去,不能确定收信人是否收到信。
因此,udp不安全 -
udp分为服务器和客户端,客户端访问服务器,有本机随机绑定端口,服务器需要绑定特定端口
-
客户端:
import socket
# 1. 创建信箱 -- udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 接受/发送 数据
# 发送数据,
# 第一个参数需要发送的信息,但必须是byte类型数据,所以字符串要编码encode;
# 第二个参数是元组类型,是ip+port,ip是字符串,端口是数字
udp_socket.sendto(msg.encode('utf-8'), ('192.168.236.129', 8080))
# 接收数据
#客户端从缓存中获取数据,recvfrom的参数就是最大能接收的字节数,可以自己调整
# recvfrom获取的是 (msg, (ip, port))
recv_msg = udp_socket.recvfrom(1024)
# 4. 关闭套接字
udp_socket.close()
服务器:
import socket
# 1. 创建信箱 -- udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 绑定端口,bind的参数是元组,ip+port,ip是字符串,端口是数字
udp_socket.bind(("192.168.236.129", 7890))
# 3. 接受/发送 数据
# 接收数据
#客户端从缓存中获取数据,recvfrom的参数就是最大能接收的字节数,可以自己调整
# recvfrom获取的是 (msg, (ip, port))
recv_msg = udp_socket.recvfrom(1024)
# 发送数据,
# 第一个参数需要发送的信息,但必须是byte类型数据,所以字符串要编码encode;
# 第二个参数是元组类型,是ip+port,ip是字符串,端口是数字
udp_socket.sendto(msg.encode('utf-8'), ('192.168.236.130', 8080))
# 4. 关闭套接字
udp_socket.close()
- tcp
-
工作原理描述:类似于打电话,知道收件方地址之后,先连接,接通后,才发送信息。
因此,udp不安全 -
tcp分为服务器和客户端,客户端访问服务器,有本机随机绑定端口,服务器需要绑定特定端口
-
客户端:
import socket
# 1. 创建电话机 -- tcp套接字
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 拨号 -- 连接服务器
tcp_client_socket.connect((server_ip, server_port))
# 3. 接受/发送 数据
# 发送数据,
# 参数需要发送的信息,但必须是byte类型数据,所以字符串要编码encode;
# 因为已经和服务器连接起来了,所以发送信息的时候不需要带服务器地址;
# 因此使用send函数,sendto函数是带地址的。
tcp_client_socket.send(msg.encode('utf-8'))
# 接收数据
#客户端从缓存中获取数据,recv的参数就是最大能接收的字节数,可以自己调整,获取的是byte类型数据
# recv函数获取的只有信息,recvfrom函数获取(msg, (ip, port))
recv_msg = tcp_client_socket.recv(1024)
# 4. 关闭套接字
tcp_client_socket.close()
服务器:
import socket
# 1. 创建电话机 -- tcp套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定端口,bind的参数是元组,ip+port,ip是字符串,端口是数字
tcp_server_socket.bind(("192.168.236.129", 7890))
# 3. 设置为接听状态,listen参数128表示socket的排队数量为128个
tcp_server_socket.listen(128)
# 4. 有链接过来,产生一个专门的套接字,为这个客户端服务,使用accept函数
# accept函数产生阻塞,只有链接过来时,才能解阻塞
server_client_socket, server_client_addr = tcp_server_socket.accept()
# 5. 接受/发送 数据
# 接收数据
#客户端从缓存中获取数据,recv的参数就是最大能接收的字节数,可以自己调整
# recv函数获取的只有信息,recvfrom函数获取(msg, (ip, port))
recv_msg = server_client_socket.recv(1024)
# 发送数据,
# send函数参数是需要发送的信息,但必须是byte类型数据,所以字符串要编码encode;
# 使用send函数不带地址,sendto函数是带地址的。
server_client_socket.send(msg.encode('utf-8'))
# 4. 先关闭服务客户端的套接字,最后关闭服务器套接字
server_client_socket.close()
tcp_server_socket.close() # 一般实际应用中服务器套接字是不会关闭的
服务器循环为多个客户端服务,且对单个客户端多次服务:
- 为多个客户端服务,即不停的在获取链接,即将accept函数写入死循环
- 为单个客户服务多次,即判断接受的信息是否为空,为空则表明客户端关闭,不为空则继续服务
import socket
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.bind(("192.168.236.129", 7890))
tcp_server_socket.listen(128)
# 1. 循环为多个客户端服务
while True:
server_client_socket, server_client_addr = tcp_server_socket.accept()
# 2. 循环为单个客户端服务
while True:
recv_msg = server_client_socket.recv(1024)
print('客户端发送过来的请求是:%s'%recv_msg.decode("utf-8"))
# 当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0;
# 因此服务器可以通过返回数据的长度来区别客户端是否已经下线
if recv_msg:
server_client_socket.send(msg.encode('utf-8'))
else:
break
server_client_socket.close()
-
tcp与udp的联系与区别
1. 联系:都是通信的一种方式。 2. 区别: 1. udp通信方式就好比是写信;tcp通信方式就好比是打电话; 2. tcp的传输更安全可靠: 1. 报文发送应答机制:连接请求发送过去,会有同意请求或拒绝请求的信息返回; 2. 超时重传:请求发送过去一段时间后,没有收到返回信息,会重新再发送; 3. 错误校验:TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验; 4. 流量控制和阻塞管理 -- 流量控制用来避免主机发送得过快而使接收方来不及完全收下。
补充知识:
-
使用终端启动python文件时,可以通过终端输入的信息,给python文件传递参数;
sys.args可以获取终端中除去python的参数,其余参数以元组形式呈现,(xxx.py, 7890)
终端:
python xxx.py port
python文件:
import sys
if len(sys_args) != 2:
print("请按照如下方式运行:python3 xxx.py 7890")
return
else:
# 运行方式为python3 xxx.py 7890
port = int(sys.argv[1])
- 解决端口复用
# setsockopt还可以设置缓存区大小;
# 参考:https://blog.csdn.net/chenggong2dm/article/details/9293427
# 参考:https://blog.csdn.net/rlenew/article/details/107592753
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)