Socket
Socket(套接字),是进程间通信的方式。网络间进程间通信。
Socket是应用层的概念
在Socket中,当建立连接的方式,通过传输层有两种建立连接协议的方式:TCP协议、UDP协议
基操
首先是导入socket模块
import socket
然后通过socket类初始化socket对象
socket.socket(AddressFamily,type)
其中:
- AddressFamily:选择AF_INET即可
- type:套接字类型,可以为:
- SOCK_STREAM:流式套接字。主要指TCP协议
- SOCK_DGRAM:数据报套接字。主要指UDP协议
import socket
# 创建一个流式套接字,用于TCP传输
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('socket创建成功', s)
s.close()
import socket
# 创建一个数据报套接字,用于UDP传输
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print('socket创建成功', s)
s.close()
TCP:传输速度慢,但稳定。(打电话)
UDP:传输速度快,但不稳定(写信)
TCP
下面以TCP的服务端和客户端写一遍
服务端
while循环(服务器一般不能随便重启,保持循环),把accept()接收连接、recv()接收信息、send()发送信息的代码放到循环中
# TCP服务端
import socket
# 创建一个流式套接字 用于TCP传输
server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 绑定端口
server_socket.bind(('',10086))
# 监听 参数表示可以接收客户端的个数
server_socket.listen(5)
while 1:
# 接收客户端的连接
# 服务端会在这里阻塞等待,直到客户端连接上
# 返回值1:连接的客户端的socket;返回值2:连接的客户端的信息 是一个元祖 包括地址和端口号
client_socket,client_info = server_socket.accept()
# 接收客户端发送过来的数据
# recv()接收数据 注意与recvfrom()比较
recv_data = client_socket.recv(1024)
print(recv_data)
print(recv_data.decode('gbk'))
# 给客户端返回一条信息
msg = 'get'
client_socket.send(msg.encode('gbk'))
# 关闭socket
server_socket.close()
客户端
# TCP服务端
import socket
# 创建一个流式套接字 用于TCP传输
client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 通过connect()函数连接服务端
# 参数是一个元组 里面包含服务端的地址和端口号
client_socket.connect(('192.168.30.35',10088))
# 给服务器发送一条消息
msg = '在吗'
client_socket.send(msg.encode('gbk'))
# 接收服务器返回等待消息
recv_data = client_socket.recv(1024)
print(recv_data)
print(recv_data.decode('gbk'))
# 关闭socket
client_socket.close()
UDP
- UDP是没有建立连接,所以每次发送信息的时候,都指定目的地,使用的函数是 sendto 、相应的接受函数是 recvfrom 。
- 比较TCP,是一开始就建立好连接,所以每次发送信息的时候,都无需再次指定目的地,使用的函数是 send 、相应接收函数是 recv 。
下面拿最UDP来写程序,我用网络调试助手设置其IP地址和端口号来检验代码:
UDP发送
import socket
# 创建一个数据报套接字 用于UDP传输
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# sendto 通过udp发送信息
# 参数1 要发送的内容
# 参数2 发送的目的地 该参数是一个元组 里面有两个元素
# 1 目的地的IP地址
# 2 目的地的端口号
# s.sendto('hey!!'.encode(), ('192.168.30.35', 9527))
# 发送字符为中文,给encode()加入参数
msg = 'hey!!你好呀'
s.sendto(msg.encode(encoding='gbk'), ('192.168.30.35', 9527))
s.close()
UDP接收
- 在发送数据的时候,无需绑定端口号
- 在接收数据的时候,才需要绑定端口号
- Udp程序通过recvfrom接收信息
import socket
# 创建一个数据报套接字 用于UDP传输
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定地址和端口号
# 参数是一个元素 第一个元素是地址,第二个元素是端口号
# 不写则让系统自取便可
s.bind(('', 52077))
# recvfrom
# 返回值1 content 接收到的信息
# 返回值2 info 发送方的地址+端口号
while 1:
content, info = s.recvfrom(1024)
print(content)
print(info)
#content才是我们要的信息
print(content.decode(encoding='gbk'))
s.close()
输出结果:
b'hey hey \xba\xd9 \xba\xd9'
('192.168.30.35', 9527)
hey hey 嘿 嘿
发送和接收的原理:
在python中通过网络接收到的数据都是字节流,需要通过decode()解码后才能变成我们能够阅读的文字
UDP广播
只有UDP才能发送广播,TCP不能发送
如果要发送广播消息,需要通过setsockopt允许当前套接字发送广播数据
import socket
# 创建一个数据报套接字 用于UDP传输
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 允许当前套接字发送广播
s.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)
# 目标地址,发送到局域网各子机
dest = ('<broadcast>',9527)
msg = 'Hey!大家好,三姑六婆大家好'
s.sendto(msg.encode(encoding='gbk'),dest)
s.close()