socket编程
【一】什么是socket
-
传输层以下的操作都很复杂且固定,大神对传输层及以下层的操作进行的一个封装,至于传输层怎么封装,网络层怎么封装,数据链路层怎么封装,物理层怎么发出去,大神都将他们封装到socket模块里面了
-
socket翻译过来是套接字,套接字就是我们用来通过网络发数据和收数据的
【1】DHCP
- DHCP就是一个自动分配、管理和回收网络地址的系统,它让设备能夠轻松地连接到网络,而不需要手动设置IP地址。
- 想象你刚搬到一个新公寓楼里,而且你需要一个邮箱。在这个情况下,公寓的管理人(在网络中相当于DHCP服务器)会给你一个邮箱(这就像网络中的IP地址)。这个邮箱是你在这栋楼里独有的,别人不能使用同一个邮箱。如果你搬走了,管理人可以把这个邮箱分配给新来的居民。
- 同样地,在网络中,当一台设备(比如你的电脑或智能手机)连接到网络时,DHCP服务器会给它分配一个IP地址。这个IP地址在网络中唯一地标识你的设备,使得设备能够发送和接收信息。当设备不再使用这个网络时,比如你关掉了电脑或离开了Wi-Fi范围,这个IP地址会被回收,并可以分配给其他设备使用。
【2】ARP协议
- 简单来说,ARP是一个帮助设备在局域网内找到对方物理地址(MAC地址)的协议,就像你在办公室寻找同事一样。
- 想象你在一个大型办公室工作,你需要给位于同一层楼的同事递送一份文件。你知道你的同事的名字(比如“Bob”),但你不知道他的办公桌在哪里。所以,你大声地问:“谁是Bob?”在听到你的呼唤后,Bob回应说:“我在这里!”并指向他的办公桌。现在你知道了Bob的具体位置,就可以直接走过去交给他文件。
- 当你的设备(比如电脑)需要与同一局域网(LAN)中的另一台设备通信时,它知道目标设备的IP地址(这就像知道同事的名字),但它不知道目标设备的物理地址(在网络中称为MAC地址)。
- 你的设备通过网络发送一个ARP请求,大致相当于问:“IP地址为XXX的设备的MAC地址是什么?”这就像在办公室中大声问“谁是Bob?”一样。
- 拥有该IP地址的设备收到ARP请求后,回应其MAC地址,就像Bob回应“我在这里”一样。
- 一旦你的设备收到了这个回应,它就会知道如何在局域网中直接寻址到目标设备,并开始通信。
【3】DNS协议
-
简单来说,DNS就像一个互联网上的信息中心,帮助将你熟悉的网站名字(域名)转换成电脑能够理解和定位的数字地址(IP地址)。
-
想象你想去一家餐厅吃饭,但你只知道餐厅的名字,比如“美味餐厅”,而不知道它的具体地址。为了找到餐厅的位置,你打电话给一位信息中心的工作人员(相当于DNS服务器),告诉他们你想去的餐厅名字。工作人员查找他们的信息记录(域名对应的IP地址),告诉你餐厅的确切地址。现在你知道了怎么去那家餐厅。
-
当你想访问一个网站时,你通常会输入网站的域名,比如
www.example.com
。这就像你知道餐厅的名字一样。 -
你的电脑(或其他设备)并不知道这个域名对应的IP地址(网站的实际“网络地址”),所以它会向DNS服务器发送一个请求,询问这个域名对应的IP地址是什么。
-
DNS服务器查找它的记录,找到域名对应的IP地址,然后把这个信息返回给你的电脑。
-
一旦你的电脑得到了这个IP地址,它就能够连接到目标网站的服务器,并加载网站内容。
【4】URL
- 通过这个URL,你的浏览器知道如何找到并展示你请求的网页或资源,就像你通过购物中心地图找到想去的店铺一样。
- 想象你有一张详细的购物中心地图。这张地图上标有每个商店的名称、楼层、和具体位置。当你想去特定的商店时,你会参考这张地图,找到商店的确切位置。
- URL就像是网络世界中的这张购物中心地图。它是一种定位互联网上资源(比如网站、图片、视频等)的方法。一个URL可以告诉你资源在哪里(服务器的地址),以及如何去那里(使用的协议)。
- 协议 (
http
):告诉你使用什么方式去访问资源,就像地图上的“你在这里”标记。 - 主机名 (
www.example.com
):告诉你资源位于哪个服务器上,就像是购物中心地图上的商店名称。 - 端口 (
80
)(可选):提供访问资源需要连接的特定“门”或“入口”,就像是商店的门号。 - 路径 (
/path/to/myfile.html
):指向服务器上的具体资源,就像是指向商店内特定商品的路径。 - 查询字符串 (
?key1=value1&key2=value2
)(可选):提供额外参数,就像是你对商店员工说的“我想看这个品牌的那种款式”。 - 片段 (
#SomewhereInTheDocument
)(可选):在资源内部指向更具体的位置,就像是在商店内指向特定的一排货架。
【二】socket TCP协议
【1.0】基础版
- 服务端
# 1.导入模块
import socket
# 2.创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3.绑定地址
sk.bind(('127.0.0.1', 8080)) # ip 加 端口号 用元组包起来
# 4.创建监听
sk.listen(5)
# 5.获取连接信息
obj, addr = sk.accept()
print(obj) # <socket.socket fd=316, family=2, type=1, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51460)>
print(addr) # ('127.0.0.1', 51460)
# 6.接收消息
data = obj.recv(1024) # 1024是接收消息的大小,无需定义太大,因为TCP协议是流式传输,会把大目标拆分成一个个的小目标
data = data.decode('utf-8') # 将接收到的消息解码
print(f'客户端:>>>>{data}') # 输出客户端发送的消息
# 7.发送消息
data = input('请输入发送的消息:>>>>').strip().encode('utf8') # 要将消息转码
obj.send(data)
obj.close() # 关闭与对方的连接
sk.close() # 关闭服务端
"""
<socket.socket fd=316, family=2, type=1, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51460)>
('127.0.0.1', 51460)
客户端:>>>>111
请输入发送的消息:>>>>2222
"""
- 客户端
# 1.导入模块
import socket
# 2.创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3.建立连接 (三次握手)
sk.connect(('127.0.0.1', 8080))
# 4.发送消息
to_data = input('请输入发送的消息:>>>>').strip().encode('utf8') # 要将消息转码
sk.send(to_data)
# 5.接收消息
data = sk.recv(1024) # 1024是接收消息的大小,无需定义太大,因为TCP协议是流式传输,会把大目标拆分成一个个的小目标
data = data.decode('utf-8') # 将接收到的消息解码
print(f'客户端:>>>>{data}') # 输出客户端发送的消息
# 6.关闭连接(四次挥手)
sk.close()
"""
请输入发送的消息:>>>>111
客户端:>>>>2222
"""
【2.0】通讯循环
- 服务端
# 1.导入模块
import socket
# 2.创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3.绑定地址
sk.bind(('127.0.0.1', 8080)) # ip 加 端口号 用元组包起来
# 4.创建监听
sk.listen(5)
# 5.获取连接信息
obj, addr = sk.accept()
print(obj) # <socket.socket fd=316, family=2, type=1, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51460)>
print(addr) # ('127.0.0.1', 51460)
while True:
# 6.接收消息
data = obj.recv(1024) # 1024是接收消息的大小,无需定义太大,因为TCP协议是流式传输,会把大目标拆分成一个个的小目标
data = data.decode('utf-8') # 将接收到的消息解码
print(f'客户端:>>>>{data}') # 输出客户端发送的消息
# 7.发送消息
data = input('请输入发送的消息:>>>>').strip().encode('utf8') # 要将消息转码
obj.send(data)
obj.close() # 关闭与对方的连接
sk.close() # 关闭服务端
- 客户端
# 1.导入模块
import socket
# 2.创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3.建立连接 (三次握手)
sk.connect(('127.0.0.1', 8080))
while True:
# 4.发送消息
to_data = input('请输入发送的消息:>>>>').strip().encode('utf8') # 要将消息转码
sk.send(to_data)
# 5.接收消息
data = sk.recv(1024) # 1024是接收消息的大小,无需定义太大,因为TCP协议是流式传输,会把大目标拆分成一个个的小目标
data = data.decode('utf-8') # 将接收到的消息解码
print(f'客户端:>>>>{data}') # 输出客户端发送的消息
# 6.关闭连接(四次挥手)
sk.close()
【3.0】结束通讯
- 服务端
# 1.导入模块
import socket
# 2.创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3.绑定地址
sk.bind(('127.0.0.1', 8081)) # ip 加 端口号 用元组包起来
# 4.创建监听
sk.listen(5)
# 5.获取连接信息
obj, addr = sk.accept()
print(obj) # <socket.socket fd=316, family=2, type=1, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51460)>
print(addr) # ('127.0.0.1', 51460)
while True:
# 6.接收消息
data = obj.recv(1024) # 1024是接收消息的大小,无需定义太大,因为TCP协议是流式传输,会把大目标拆分成一个个的小目标
data = data.decode('utf-8') # 将接收到的消息解码
print(f'客户端:>>>>{data}') # 输出客户端发送的消息
if data == 'q':
break
# 7.发送消息
to_data = input('请输入发送的消息:>>>>').strip().encode('utf8') # 要将消息转码
obj.send(to_data)
obj.close() # 关闭与对方的连接
sk.close() # 关闭服务端
"""
<socket.socket fd=316, family=2, type=1, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51460)>
('127.0.0.1', 51460)
客户端:>>>>111
请输入发送的消息:>>>>2222
"""
- 客户端
# 1.导入模块
import socket
# 2.创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3.建立连接 (三次握手)
sk.connect(('127.0.0.1', 8081))
while True:
# 4.发送消息
to_data = input('请输入发送的消息:>>>>').strip().encode('utf8') # 要将消息转码
sk.send(to_data)
if to_data.decode('utf8') == 'q':
break
# 5.接收消息
data = sk.recv(1024) # 1024是接收消息的大小,无需定义太大,因为TCP协议是流式传输,会把大目标拆分成一个个的小目标
data = data.decode('utf-8') # 将接收到的消息解码
print(f'客户端:>>>>{data}') # 输出客户端发送的消息
# 6.关闭连接(四次挥手)
sk.close()
"""
请输入发送的消息:>>>>111
客户端:>>>>2222
"""
- 当客户端输入q之后就会断开与服务端的连接,退出循环
【4.0】空消息问题
- 服务端不变
# 1.导入模块
import socket
# 2.创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3.绑定地址
sk.bind(('127.0.0.1', 8081)) # ip 加 端口号 用元组包起来
# 4.创建监听
sk.listen(5)
# 5.获取连接信息
obj, addr = sk.accept()
print(obj) # <socket.socket fd=316, family=2, type=1, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51460)>
print(addr) # ('127.0.0.1', 51460)
while True:
# 6.接收消息
data = obj.recv(1024) # 1024是接收消息的大小,无需定义太大,因为TCP协议是流式传输,会把大目标拆分成一个个的小目标
data = data.decode('utf-8') # 将接收到的消息解码
print(f'客户端:>>>>{data}') # 输出客户端发送的消息
if data == 'q':
break
# 7.发送消息
to_data = input('请输入发送的消息:>>>>').strip().encode('utf8') # 要将消息转码
obj.send(to_data)
obj.close() # 关闭与对方的连接
sk.close() # 关闭服务端
"""
<socket.socket fd=316, family=2, type=1, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51460)>
('127.0.0.1', 51460)
客户端:>>>>111
请输入发送的消息:>>>>2222
"""
- 客户端
# 1.导入模块
import socket
# 2.创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3.建立连接 (三次握手)
sk.connect(('127.0.0.1', 8081))
while True:
# 4.发送消息
to_data = input('请输入发送的消息:>>>>').strip().encode('utf8') # 要将消息转码
if not to_data:
print('不能输入空消息')
continue
sk.send(to_data)
if to_data.decode('utf8') == 'q':
break
# 5.接收消息
data = sk.recv(1024) # 1024是接收消息的大小,无需定义太大,因为TCP协议是流式传输,会把大目标拆分成一个个的小目标
data = data.decode('utf-8') # 将接收到的消息解码
print(f'客户端:>>>>{data}') # 输出客户端发送的消息
# 6.关闭连接(四次挥手)
sk.close()
"""
请输入发送的消息:>>>>111
客户端:>>>>2222
"""
- 客户端加入空消息的判断
【5.0】连接循环
- 服务端
# 1.导入模块
import socket
# 2.创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3.绑定地址
sk.bind(('127.0.0.1', 8081)) # ip 加 端口号 用元组包起来
# 4.创建监听
sk.listen(5)
while True:
# 5.获取连接信息
obj, addr = sk.accept()
print(obj) # <socket.socket fd=316, family=2, type=1, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51460)>
print(addr) # ('127.0.0.1', 51460)
while True:
# 6.接收消息
data = obj.recv(1024) # 1024是接收消息的大小,无需定义太大,因为TCP协议是流式传输,会把大目标拆分成一个个的小目标
data = data.decode('utf-8') # 将接收到的消息解码
print(f'客户端:>>>>{data}') # 输出客户端发送的消息
if data == 'q':
break
# 7.发送消息
to_data = input('请输入发送的消息:>>>>').strip().encode('utf8') # 要将消息转码
obj.send(to_data)
obj.close() # 关闭与对方的连接
sk.close() # 关闭服务端
"""
<socket.socket fd=316, family=2, type=1, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51460)>
('127.0.0.1', 51460)
客户端:>>>>111
请输入发送的消息:>>>>2222
"""
- 客户端不变
【三】socket UDP协议
- 服务端
import socket
# 创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 数据报协议 UDP协议
# 绑定端口
sk.bind(('127.0.0.1', 8080))
# 接收数据
data = sk.recvfrom(1024)
print(data)
- 客户端
import socket
# 创建对象
sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
msg = input('请输入发送的消息:>>>>').strip().encode('utf8')
sk.sendto(msg, ('127.0.0.1', 8080))
sk.close()