网络-UDP(用户数据报协议)
需要明确的几个问题:
- 使用网络的目的是为了联通多方然后进行通信用的,即把数据从一方传递到另一方
- 所谓的网络编程就是让在不同的电脑上运行的软件之间进行数据传递,即进程之间的通信
- 所谓进程指的是运行的程序以及运行这个程序所需要的资源的整体
- 所谓的进程间通信指的是进程间的数据传输
IP地址:
IP地址是用来在网络中标记一台电脑,在本地局域网中是唯一的
IP地址的分类(了解):
3.1 A类IP地址
一个A类IP地址由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”,
地址范围1.0.0.1-126.255.255.254
二进制表示为:00000001 00000000 00000000 00000001 - 01111110 11111111 11111111 11111110
可用的A类网络有126个,每个网络能容纳1677214个主机
3.2 B类IP地址
一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,
地址范围128.1.0.1-191.255.255.254
二进制表示为:10000000 00000001 00000000 00000001 - 10111111 11111111 11111111 11111110
可用的B类网络有16384个,每个网络能容纳65534主机
3.3 C类IP地址
一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”
范围192.0.1.1-223.255.255.254
二进制表示为: 11000000 00000000 00000001 00000001 - 11011111 11111111 11111110 11111110
C类网络可达2097152个,每个网络能容纳254个主机
3.4 D类地址用于多点广播
D类IP地址第一个字节以“1110”开始,它是一个专门保留的地址。
它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中
多点广播地址用来一次寻址一组计算机 s 地址范围224.0.0.1-239.255.255.254
3.5 E类IP地址
以“1111”开始,为将来使用保留
E类地址保留,仅作实验和开发用
3.6 私有ip
在这么多网络IP中,国际规定有一部分IP地址是用于我们的局域网使用,也就
是属于私网IP,不在公网中使用的,它们的范围是:
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
3.7 注意
IP地址127.0.0.1~127.255.255.255用于回路测试,
如:127.0.0.1可以代表本机IP地址,用http://127.0.0.1就可以测试本机中配置的Web服务器。
一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”
范围192.0.1.1-223.255.255.254
二进制表示为: 11000000 00000000 00000001 00000001 - 11011111 11111111 11111110 11111110
C类网络可达2097152个,每个网络能容纳254个主机
3.4 D类地址用于多点广播
D类IP地址第一个字节以“1110”开始,它是一个专门保留的地址。
它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中
多点广播地址用来一次寻址一组计算机 s 地址范围224.0.0.1-239.255.255.254
3.5 E类IP地址
以“1111”开始,为将来使用保留
E类地址保留,仅作实验和开发用
3.6 私有ip
在这么多网络IP中,国际规定有一部分IP地址是用于我们的局域网使用,也就
是属于私网IP,不在公网中使用的,它们的范围是:
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
3.7 注意
IP地址127.0.0.1~127.255.255.255用于回路测试,
如:127.0.0.1可以代表本机IP地址,用http://127.0.0.1
就可以测试本机中配置的Web服务器。
Linux命令(ping, ifconfig)
ifconfig用来查看或者配置网卡信息,ping可以用来检测网络是否正常;
附:ens是用于两个进程之间通信的网卡,lo表示的是本地网卡,不可以进行通信;可以通过sudo ifconfig ens33 down/up将网卡关闭或者打开。
端口
局域网中每台电脑都有不同的IP,在网络收发过程中可以都过选定某一个特定的IP进行通信。这样就又有一个问题就是某一台主机中有很多的进程,如何选择某一个特定的进程,这就需要选择特定的端口号。
例如输入的是des ip:192.168.1.1 src ip:192.168.1.2 des port:4423 src port:7788 content:你好啊;
这句话的意思就是源IP为192.168.1.1,目的IP是192.168.1.2,源端口号是7788,目标端口号为4423,发送内容为你好啊;
端口分为知名端口和动态端口,知名端口是已经确定好功能的端口号(0-1023),动态端口则是需要自己定义功能的端口号(1024-65535)
小总结:
端口有什么用呢 ? 我们知道,一台拥有IP地址的主机可以提供许多服务,比如HTTP(万维网服务)、FTP(文件传输)、SMTP(电子邮件)等,这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区分不同的服务的。 需要注意的是,端口并不是一一对应的。比如你的电脑作为客户机访问一台WWW服务器时,WWW服务器使用“80”端口与你的电脑通信,但你的电脑则可能使用“3457”这样的端口。
socket简介
socket(简称 套接字
) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:
它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的
创建socket
在Python中使用socket模块中的函数就可以完成:
import socket
socket.socket(AddressFamily, Type)
在创建socket时,socket函数中有两个参数:
- Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
- Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
创建一个tcp socket(tcp套接字):
import socket
#创建tcp socket(tcp套接字)
s = socket.socket(socket.AF.INET, socket.SOCK_STREAM)
#这里是使用套接字的功能(省略)
s.close()
#创建一个udp套接字
s = socket.socket(socket.AF.INET, socket.SOCK_DGRAM)
#这里是使用套接字的功能(省略)
#不用的时候关闭
s.close()
udp网络程序--发送接收数据
udp网络程序--发送数据
创建一个基于udp的网络程序流程很简单,具体步骤如下:
- 创建客户端套接字
- 发送/接收数据
- 关闭套接字
代码如下:
#coding=utf-8
from socket import *
#创建udp套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
#准备接收方的地址
#192.168.1.103表示目标IP地址,8080表示目标端口号
dest_addr = ('192.168.1.103', 8080)
#从键盘中获取数据
send_data = input("请输入你要发送的内容")
#发送数据到指定的电脑中的指定的程序中
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)
#关闭套接字
udp_socket.close()
会出现的一些问题:
- 主机之间进行通信的时候双方的IP地址前三位一样意味着这两个主机位于相同的局域网之间,可以直接进行通信。
- 如果用Ubuntu和Windows进行通信,Ubuntu一般来说是无法直接与Windows进行通信的,也就是IP地址的前三位不一致,这时需要将Ubuntu的网络连接改为桥接模式,然后在终端输入sudo dhclient,系统会自动分配一个IP地址,然后ping一下看看是否可以联通。
- 对Python3进行编码转换:bytes-->str:decode() str-->bytes:encode(),这里边还可以有参数,encoding表示使用的编码方式,errors表示发生错误时处理方案
bytes.decode(encoding="utf-8", errors="strict") str.encode(encoding="utf-8", errors="strict")
udp网络程序--接收数据
from socket import *
# 创建一个套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
# 开始接收数据
# 先要绑定一个端口,表示用此端口专门用来接收数据
localAddr = ("", 7788) # 表示7788是专门进行接收数据的端口
udp_socket.bind(localAddr) # 必须绑定自己主机的IP和port,绑定其他的不行
# 下面开始接收数据
recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字节数
# 下面显示数据
# 收到的recv_data是一个元组,第一项是对方发送的数据,第二项是对方的IP地址和端口号
# 收到的第一项是对方发送的数据,还是bytes类型,如果对方发送的数据依然还是乱码,Windows采用的是gbk编码方式,所以如果是从Windows向Ubuntu发送数据,那么解码的时候也就需要利用gbk解码
print(recv_data[0].decode('gbk'))
print(recv_data[1])
# 关闭套接字
udp_socket.close()
if __name__=="__main__":
main()
UDP绑定端口问题
如果没有绑定端口,我们每次向别的进程种发送消息的时候因为没有固定的端口,所以操作系统会默认分配一个端口进行作为输出端口。
上面这种情况的出现是因为系统每次都分配一个不同的端口,所以每次接收端显示的输出端口都不一样。解决这一问题的方法就是进行绑定。
一般来说,主机种的网络进程非常多,为了不与已经存在的进程重复,在编程过程中我们一般不绑定端口号。但是如果要做成服务器的程序的话,就必须要进行绑定了。
# coding = utf-8
from socket import *
# 创建一个udp套接字
udp_socket = socket(socket.AF_INET, socket.SOCK_DGRAM)
dest_addr = ('192.168.1.33', 8800)
# 进行套接字的绑定
local_addr = ("", 7890)
udp_socket.bind(local_addr)
# 传输数据
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)
# 关闭套接字
udp_socket.close()
总结:
切记不要把已经在占用的端口号进行绑定,如果一个端口号同时绑定两个进程那么就会导致进程崩溃。
网络通信过程之所以需要IP和port是为了将一个复杂的通信任务进行划分,从而达到数据准确无误传输的目的。
案例:UDP聊天器
功能要求:编写一个可以实现收发数据功能。
from socket import *
# 发送信息
def send_mes(udp_socket):
# 输入所需的IP地址
ip = input("输入目标IP")
# 输入所需的端口
port = int(input("输入目标端口"))
# 请输入所要发送的消息
send_data = input("请输入需要发送的消息")
# 发送数据
udp_socket.sendto(send_data.encode('utf-8'), (ip, port))
# 关闭套接字
udp_socket.close()
#接受数据
def recv_mes(udp_socket):
recv_data = udp_socket.recvfrom(1024)
# 打印出所有需要的输出数据
print(recv_data[0].decode('gbk'))
print(recv_data[1])
def main():
# 创建套接字
udp_socket = socket(socket.AF_INET, socket.SOCK_DGRAM)
# 将套接字进行绑定
udp_socket.bind(('', 7890))
print("""1--发送信息
2--接收信息
0--退出""")
# 开始执行操作
while True:
choice = int(input("请输入需要执行的操作"))
if choice==1:
send_mes(udp_socket)
elif choice==2:
recv_mes(udp_socket)
else:
break
if __name__ == '__main__':
main()