文章目录
笔记中代码均可运行在Jupyter NoteBook下(实际上Jupyter-lab使用体验也很棒)。
建议不要光看,要多动手敲代码。眼过千遭,不如手读一遍。
相关笔记的jupiter运行代码已经上传,请在资源中自行下载。
网络编程
使用网络能够把多方链接在一起,然后可以进行数据传递
所谓的网络编程就是,让在不同的电脑上的软件能够进行数据传递,即进程之间的通信
Python Internet 模块
协议 | 功能用处 | 端口号 | Python模块 |
---|---|---|---|
HTTP | 网页访问 | 80 | httplib, urllib, xmlrpclib |
NNTP | 阅读和张贴新闻文章,俗称为"帖子" | 119 | nntplib |
FTP | 文件传输 | 20 | fitlib, urllib |
SMTP | 发送邮件 | 25 | smtplib |
POP3 | 接收邮件 | 110 | poplib |
IMAP4 | 获取邮件 | 143 | imaplib |
Telnet | 命令行 | 23 | telnetlib |
Gopher | 信息查找 | 70 | gopherlib,urllib |
TCP/IP协议(族)
应用层:应用层,表示层,会话层
传输层:传输层
网络层:网络层
链路层:数据链路层,物理层
对上图的说明:
网际层也称为:网络层;网络接口层也称为:链路层
端口
端口:可以认为是设备与外界通讯交流的出口。
端口号是唯一的
端口范围:0~65535
端口分类:
**公认端口(WellKnownPorts):**从0到1023,它们紧密绑定(binding)于一些服务。
**注册端口(RegisteredPorts):**从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。
**动态和/或私有端口(Dynamicand/orPrivatePorts):**从49152到65535。理论上,不应为服务分配这些端口。实际上,机器通常从1024起分配动态端口。但也有例外:SUN的RPC端口从32768开始。
查看端口:
1、netstat -an
指令查看
2、第三方扫描软件
IP地址
ip地址:用来在网络中标记一台电脑的一串数字,比如192.168.1.1;在本地局域网上是惟一的。
ip地址分类:
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个主机
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主机
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个主机
D类地址用于多点广播
D类IP地址第一个字节以“1110”开始,它是一个专门保留的地址。
它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中
多点广播地址用来一次寻址一组计算机
地址范围224.0.0.1-239.255.255.254
- E类IP地址*
以“1111”开始,为将来使用保留
E类地址保留,仅作实验和开发用
- 私有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
注意
IP地址127.0.0.1~127.255.255.255用于回路测试,
如:127.0.0.1可以代表本机IP地址,用[http://127.0.0.1]
就可以测试本机中配置的Web服务器。
子网掩码
子网掩码(subnet mask)又叫网络掩码、地址掩码、子网络遮罩,
它是一种用来指明一个IP地址的哪些位标识的是主机所在的子网,
以及哪些位标识的是主机的位掩码。
子网掩码不能单独存在,它必须结合IP地址一起使用。
子网掩码只有一个作用:就是将某个IP地址划分成网络地址和主机地址两部分。
与IP地址相同,子网掩码的长度也是32位,
左边是网络位,用二进制数字“1”表示;
右边是主机位,用二进制数字“0”表示。
假设IP地址为“192.168.1.1”子网掩码为“255.255.255.0”。
其中,“1”有24个,代表与此相对应的IP地址左边24位是网络号;
“0”有8个,代表与此相对应的IP地址右边8位是主机号。
这样,子网掩码就确定了一个IP地址的32位二进制数字中哪些是网络号、哪些是主机号。
这对于采用TCP/IP协议的网络来说非常重要,只有通过子网掩码,才能表明一台主机所在的子网与其他子网的关系,使网络正常工作。
子网掩码是“255.255.255.0”的网络:
最后面一个数字可以在0~255范围内任意变化,因此可以提供256个IP地址。
但是实际可用的IP地址数量是256-2,即254个,因为主机号不能全是“0”或全是“1”。
主机号全为0,表示网络号
主机号全为1,表示网络广播
注意:
如果将子网掩码设置过大,也就是说子网范围扩大,根据子网寻径规则,很可能发往和本地主机不在同一子网内的目标主机的数据,会因为错误的判断而认为目标主机是在同一子网内,导致数据包将在本子网内循环,直到超时并抛弃,使数据不能正确到达目标主机,导致网络传输错误;如果将子网掩码设置得过小,那么就会将本来属于同一子网内的机器之间的通信当做是跨子网传输,数据包都交给缺省网关处理,这样势必增加缺省网关的负担,造成网络效率下降。因此,子网掩码应该根据网络的规模进行设置。如果一个网络的规模不超过254台电脑,采用“255.255.255.0”作为子网掩码就可以了,现在大多数局域网都不会超过这个数字,因此“255.255.255.0”是最常用的IP地址子网掩码;假如在一所大学具有1500多台电脑,这种规模的局域网可以使用“255.255.0.0”。
socket
本地(一台机器上)的进程间通信(IPC)有很多种方式,例如
队列
同步(互斥锁、条件变量等)
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket(简称:套接字)。
Python提供了两个级别访问的网络服务:
低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
Python创建socket:
import socket
socket.socket(family[, type[, protocol]])
参数解释:
family:套接字家族可以使AF_UNIX或者AF_INET
type:套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM
protocol:一般不填默认为0.
Socket 对象(内建)方法
服务器端套接字
s.bind()
绑定地址(host, port)到socket, 在AF_INET下,以元组(host, port的形式表示地址。)
s.listen()
开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
s.accept()
被动接受TCP客户端连接,(阻塞式)等待连接的到来
返回为(socket object, address info)
其中socket object
用于处理连接期间的通信,
包括服务器接收自客户端的信息,和服务器向客户端发送的信息。
区别于服务器套接字(server_socket)和客户端套接字(client_socket),
可以记为通信套接字(communication_socket)
而address info
是客户端的ip地址及端口(ip:port)
客户端套接字
s.connect()
主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
s.connect_ex()
connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv()
接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
s.send()
发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
s.sendall()
完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
s.recvfrom(size)
接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
s.sendto(msg, addr)
发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
s.close()
关闭套接字
s.getpeername()
返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
s.getsockname()
返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level, optname, value)
设置给定套接字选项的值。
s.getsocketopt(level, optname, buflen)
返回套接字选项的值。
s.settimeout(timeout)
设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout()
返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
s.gileno()
返回套接字的文件描述符。
s.setbolcking(flag)
如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常
s.makefile()
创建一个与该套接字相关连的文件
# 创建socket 例子
import socket
# 创建一个tcp socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('tcp socket created!')
# 创建一个udp socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print('udp socket created!')
tcp socket created!
udp socket created!
# socket 简单服务器
# 导入socket, sys 模块
import socket
import sys
from time import time
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 指定绑定地址
host = ""
# 是指端口
port = 6666
# 绑定端口号
server_socket.bind((host, port))
# 设置最大连接数,超过后阻塞
server_socket.listen(5)
# 使用start_time、stop_time、run_time来控制循环运行
start_time = time() # 开始监听时间
run_time = 0 # 运行时间
while run_time < 10: # 运行时长:10s
# 建立客户端连接
client_socket, addr = server_socket.accept()
print("client add is {}".format(addr))
msg = "Hello this is server_socket send message!".encode("utf-8")
client_socket.send(msg)
client_socket.close()
stop_time = time() # 停止时间
run_time = stop_time - start_time # 总运行时间
print("Server exit.")
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
<ipython-input-1-ea5ca28be367> in <module>
21 while run_time < 10: # 运行时长:10s
22 # 建立客户端连接
---> 23 client_socket, addr = server_socket.accept()
24
25 print("client add is {}".format(addr))
~/WorkStations/anaconda3/lib/python3.7/socket.py in accept(self)
210 For IP sockets, the address info is a pair (hostaddr, port).
211 """
--> 212 fd, addr = self._accept()
213 sock = socket(self.family, self.type, self.proto, fileno=fd)
214 # Issue #7995: if no default timeout is set and the listening
KeyboardInterrupt:
# socket 客户端例子
# 导入socket, sys 模块
import socket
import sys
# 创建socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 指定服务器地址
host = "127.0.0.1"
# 是指端口
port = 6666
# 连接服务,指定主机和端口
s.connect((host, port))
# 接收小于2048字节的数据
msg = s.recv(2048)
s.close()
print(msg.decode('utf-8'))
socket 服务器 客户端 通信 结果图
#### UDP
UDP — 用户数据报协议,是一个无连接的简单的面向数据报的运输层协议。
udp是TCP/IP协议族中的一种协议能够完成不同机器上的程序间的数据通信
udp只是把应用层传给ip层的数据报发出去,不能保证到达;udp在传输数据报前不用在客户和服务器之间建立连接,没有超时重发等机制,传输速度快。
UDP是一种面向无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
UDP特点:
面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。
UDP传输数据有大小限制:每个被传输的数据报必须限定在64KB内。
udp不可靠,发送方所发送的数据报并不一定以相同的次序到达接收方。
UDP一般用于多点通信和实时的数据业务:
语音广播
视频
QQ
TFTP(简单文件传送)
SNMP(简单网络管理协议)
RIP(路由信息协议,如报告股票市场,航空信息)
DNS(域名解释)
UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。
udp端口号动态变化解释:
每次重新运行网络程序,对于未绑定端口号的程序,系统默认随机分配一个端口号来唯一标识这个程序。如果需要向此程序发送信息,只需要向这个端口标识的程序发送即可。
UDP网络通信过程
udp服务器、客户端
udp的服务器和客户端的区分:往往是通过请求服务和提供服务来进行区分
请求服务的一方称为:客户端
提供服务的一方称为:服务器
udp 发送数据:
创建一个udp客户端程序步骤:
1、创建客户端套接字
2、发送/接收数据
3、关闭套接字
UDP创建流程图
# 创建udp 客户端 例子udp
# -*- coding: utf-8 -*-
import socket
# 创建客户端socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 服务器地址
server_addr = ("192.168.1.1", 2333)
# 客户端向服务器发送的信息
# 记得编码【encode()】
client_data = "hello".encode("utf-8")
# 向服务器发送client_data数据
client_socket.sendto(client_data, server_addr)
# 接收服务器发送的信息(数据,地址)
msg, addr = client_socket.recvfrom(1024)
# 打印接收自服务器的信息msg, 服务器地址:addr
print("server message is:\n\t{}\nserver address is:{}\n\t".format(
msg.decode("utf-8"), addr))
# 关闭连接
client_socket.close()
# udp 服务器
# -*- coding: utf-8 -*-
import socket
# 创建服务器socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 设置绑定地址
listen_addr = ("", 2333)
# 绑定地址
server_socket.bind(listen_addr)
# 接收客户端信息msg,客户端地址addr
msg, addr = server_socket.recvfrom(1024)
# 打印客户发送的数据
print("receive message from client:\n\t{}".format(msg.decode("utf-8")))
# 服务器向客户端发送的数据
server_data = "this is server_data".encode("utf-8")
# 向客户端addr,发送数据server_data
server_socket.sendto(server_data, addr)
# 关闭连接
server_socket.close()
udp绑定
一个udp网络程序,可以不绑定,此时操作系统会随机进行分配一个端口,
如果重新运行次程序端口可能会发生变化
一个udp网络程序,也可以绑定信息(ip地址,端口号),如果绑定成功,
那么操作系统用这个端口号来进行区别收到的网络数据是否是此进程的
# udp 接收方 端口绑定 例子
# -*-coding: utf-8 -*-
import socket
# 1、创建socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2、绑定本地的相关信息,若一个网络程序不绑定,则系统会随机分配端口号
bind_addr = ('',6666)# ip地址和端口号,ip一般不用写,表示本机的任何一个ip
udp_socket.bind(bind_addr)
# 3、等待接收数据
recv_data = udp_socket.recvfrom(1024)# 1204为本次接收的最大字节数
# 4、显示接收数据
# 若接收到的是bite格式,则用decode('decode-type')解码,str类型不必decode
print(recv_data.decode('utf-8'))
# 5、关闭socket
udp_socket.close()
# 因为例子是接收方的端口绑定,所以单独运行是没有显示的,故不运行
# udp echo 服务器 示例
# echo服务器:将收到的信息原封不动的返回
import socket
# 1、创建socket
udp_socket = socket