Python网络编程
一、基础知识
1、名词缩写
TCP(Transmission Control Protocol)传输控制协议
IP(Internet Protocol)因特网协议
HTTP(Hyper Text Transfer Protocol)超文本传输协议
OSI/RM (Open System Interconnection Reference Model)开放式系统互联参考模型
UDP(User Datagram Protocol)用户数据报协议
ICMP(Internet Control Message Protocol)因特网控制报文协议
SMTP(Simple Mail Transfer Protocol)简单邮件传输协议
FTP(File Transfer Protocol)文件传输协议
ARP(Address Resolution Protocol)地址解析协议
2、IP地址
TCP/IP协议的分层模型有四个层次组成,分别为网络接口层、网络层、传输层和应用层。
IP地址实际上是一个32位整数(称为IPV4).
IPV6地址实际上是一个128位整数,它是IPV4的升级版。
我们通过IP地址和子网掩码进行按位“与”运算,可以确定某个设备的网络地址和主机号。
用网线直接连接的计算机或是通过集线器或者普通交换机连接的计算机之间要能够相互通信,计算机必须要处于同一网络,也就是说他们的网络地址必须相同,而主机地址必须不同,否则无法通信。
在网络上标识一台计算机的方式是利用IP地址,但是一组IP数字很不容易记忆,且没有什么联想的意义,因此我们会为网络上的服务器取一个有意义且容易记忆的名字,这个名字就叫做域名(Domain Name)。但是网络还是依靠IP地址去识别机器,所以当使用者输入域名后,浏览器必须先去一台有域名和IP地址对应资料的主机去查询这台电脑的IP地址,而这台被查询的主机称为域名服务器(DNS Server)。
3、网络测试ping命令
ping工具的主要作用是验证与远程计算机的连接状态,该命令只有在安装了TCP/IP协议后才可以使用。ping工具通过向远程计算机发送特定的数据包,然后等待回应并接收返回的数据包,对每个接受的数据包均根据传输的消息进行验证。
4、端口
一台拥有IP得治的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等,并且这些服务完全可以通过一个IP地址来实现。那么,主机是怎样区分不同的网络服务的呢?显示不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区分不同的服务。
端口的分类:
知名端口(Well-Known Port) | 0~1023 |
---|---|
FTP(文件传输协议) | 21 |
SMTP(简单邮件传输协议) | 25 |
HTTP服务 | 80 |
RPC(远程过程调用) | 135 |
动态端口(Dynamic Port) | 1024~65535 |
说明:动态端口不固定分配给某个服务,也就是说许多服务都可以使用这些端口,只有运行的程序向系统提出访问网络的申请。
5、socket
套接字(socket)是计算机之间进行网络通信的一套程序接口,也是计算机进程间通信的一种方式,他可以实现不同主机进程间的通信。socket模块包括两个部分:服务端和客户端,服务端负责监听端口号,等待客户端发送消息;客户端咋需要发送信息时,连接服务端,将信息发送出去。socket是网络编程的一个抽象的概念,通常我们用一个socket表示打开了一个网络连接,而打开网络连接需要知道目标计算机的IP地址和端口号,再指定协议。
二、http协议简介
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP协议永远都是客户端发起请求,服务器回送响应。
1、HTTP协议通信过程:
1.URL自动解析
HTTP URL (URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息)的格式如下:
http://host[":"port][abs_path]
-
http表示使用HTTP协议来定位网络资源;
-
host参数表示合法的主机域名或IP地址;
-
port蚕食指定一个端口号,默认为端口80;
-
abs_path参数指定被请求资源在服务器上的文件路径
如果在URL中没有给出abs_path参数,那么其在URL命令中,必须以“/”的形式给出,通常这个工作由浏览器自动帮我们完成。
2.获取IP地址,建立TCP连接
3.浏览器向服务器发出HTTP请求
一旦建立了TCP连接,Web浏览器就会想Web服务器发送如下请求命令:
GET / HTTP/1.1
然后,Web浏览器将会以头信息的形式向Web服务器发送一些其他信息,之后浏览器将发送一个空白行来通知服务器头信息的发送结束。
4.Web服务器响应,并向浏览器发送数据
浏览器向服务器发出请求后,服务器会向浏览器发送如下应答消息:
HTTP/1.1 200 OK
应答的第一行是协议的版本号和应答码。
Web服务器向浏览器发送头信息完成后,将会发送一个空白行来表示头信息的发送完毕。
5.浏览器解析数据
浏览器对Web服务器发送来的数据进行解析并显示出来,这样我们就看到了我们在URL中所请求的信息。
6.关闭TCP连接
一般情况下,一旦Web服务器向浏览器发送了被请求的数据,其就会关闭TCP连接。
2、HTTP协议请求与响应的具体内容
HTTP无论是请求报文(Request Message)还是响应报文(Response Message)都可以分为四个部分:
- 起始行
- 0个或多个头域
- 空行(作为头域部分的结束)
- 可选消息体
说明:HTTP协议是基于行的协议,每一行以"\r\n"作为分隔符;
头域则附带一些特殊信息,每一个头域占一行,其格式为 “名: 值”;
空行则是一个"\r\n"(在程序中判断空行则是遇到’\r\n\r\n‘作为分隔符);
可选消息体则包含实际数据,例如服务器返回的某个静态HTML文件的内容。
HTTP协议的请求报文的起始行
格式如下:
方法[空格]请求URI[空格]版本号[回车换行]
例如:
GET /index.html /HTTP/1.1
说明:其中,GET就是请求方法,/index.html就是被请求资源在服务器上的路径,HTTP/1.1就是HTTP协议版本号。
请求方法有很多种,各个方法的解释如下:
请求方法 | 说明 |
---|---|
GET | 请求获取Request-URI指定的资源 |
HEAD | 请求获取Request-URI制定资源的响应消息报头 |
POST | 用于向服务器提交数据,正常情况下带有“消息体” |
PUT | 请求服务器存储一个资源,并用Request-URI作为其标识 |
DELETE | 请求服务器删除Request-URI所标识的资源 |
TRACE | 请求服务器回送收到的请求信息,主要用于测试或诊断 |
CONNECT | 保留将来使用 |
OPTIONS | 请求查询服务器的性能,或者查询与资源相关的选项和需求 |
版本号:
现在广泛应用的有HTTP/1.0和HTTP/1.1两个版本,1.1和1.0相比最大的特点就是增加对长连接的支持。
HTTP/1.0只支持端连接,每次连接只处理有关请求,即使对同一站点的每一个页面的访问,浏览器和服务器之间都有建立一次单独的链接。
HTTP/1.1支持长连接,在一个TCP连接上可以传送多个HTTP请求和应答,减少建立和关闭连接的消耗和延迟。例如一个包含多张图片资源的网页文件的多个请求和响应可以在同一个连接中传输,并且还允许浏览器客户端不用等待上一次请求的结果返回就可以发送下一个请求,也就是支持pipeline管线化。
HTTP协议的响应报文的起始行
格式如下:
版本号[空格]状态码[空格]原因[回车]
例如:
HTTP/1.1 200 OK
说明:HTTP/1.1表明协议版本;200是一个status code,也就是响应状态码;OK是状态码的解释字符串。
响应状态码有三位数组成,第一个数字定义了响应类别。
状态码 | 含义 |
---|---|
200 | 正确返回结果 |
302 | 页面跳转 |
304 | 页面未改动 |
404 | 请求的页面未找到 |
405 | 方法不允许 |
501 | 未被使用 |
503 | 服务不可用 |
三、网络编程基础
TCP和UDP是网络体系结构传输层最重要的两个协议。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。因此,TCP协议适合对准确性要求较高的场合,比如文件传输、电子邮件等。UDP是面向无连接的协议,使用UDP协议传输数据,不需要事先建立连接,只需要知道对方的IP地址和端口号即可,但是UDP协议不保证数据能准确送达。虽然UDP数据不可靠,但它的传输速度快,因此对于可靠性要求不高的场合,可以使用UDP协议,如网络语音、视频点播等。
1.TCP客户端编程
TCP是一种面向连接的传输层协议,TCP Socket是基于一种C/S的编程模型,服务端监听客户端的连接请求,一旦建立连接即可进行传输数据。
客户端编程流程:
-
创建socket
-
连接服务器
-
发送数据
-
接收数据
-
关闭socket
# 导入socket库
import socket
# 1、创建socket对象
# socket.AF_INET指定使用IPV4,若要使用IPV6则指定为AF_INET6
# SOCK_STREAM指定使用面向流的TCP协议
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
"""
2、作为服务器,提供什么样的服务,其端口号就一定要固定,访问网页的端口号80,SMTP的服务端口是25,
端口号小于1024的是Internet标准服务端口,大于1024的可以随意使用。
注意:在这里使用的是一个tuple,包含地址与端口号
"""
s.connect(('www.baidu.com', 80))
"""
3、发送数据
广泛应用的有HTTP/1.0和HTTP/1.1两个版本,1.1和1.0相比最大的特点就是增加对长连接的支持。
HTTP/1.1支持长连接,在一个TCP连接上可以传送多个HTTP请求和应答,减少建立和关闭连接的消耗和延迟
"""
s.send(b'GET / HTTP/1.1\r\nHost:www.baidu.com\r\nConnection:close\r\n\r\n')
# 4、接收数据
buf = []
while True:
d = s.recv(1024)
if d:
buf.append(d)
else:
break
# 把列表中的元素连接成字符串
data = b''.join(buf)
# 5、关闭socket
s.close()
# split() 在第一次遇到'\r\n\r\n'时对字符串进行切片,参数1,代表分成两部分。
header, html = data.split((b'\r\n\r\n'), 1)
# 对二进制数据进行解码
print(header.decode('utf-8'))
# 把接收的数据写入文件
with open('baidu.html', 'wb') as b:
b.write(html)
2、TCP服务器端编程
- 打开socket
- 绑定监听地址以及端口
- 监听连接
- 建立连接
- 接收/发送数据
import socket
import threading
# 监听哪些网络 192.168.31.230是监听本机 0.0.0.0是监听整个网络
address = '192.168.31.230'
# 监听自己的哪个端口
port = 8081
# 接收从客户端发来的数据的缓存区大小
buff_size = 1024
s = socket.socket()
s.bind((address, port))
# 最大连接数
s.listen(2)
def tcp_link(sock, addr):
while True:
"""
recv(缓存大小) - 获取客户端给服务器发送的数据,返回值是二进制
缓存大小 - 决定一次可以接收的数据的最大字节数
这儿也会阻塞线程,直到客户端发送了消息才会接着往后执行
"""
recv_data = client_sock.recv(buff_size).decode('utf-8')
if recv_data == 'exit' or not recv_data:
break
print("from client:" + recv_data)
send_data = 'from sever:' + recv_data
"""
send(数据) - 将指定的数据发送给客户端
数据 - 要求是二进制
字符串(str)转二进制(bytes):
a.bytes(字符串, 'utf-8')
b.字符串.encode('utf-8')
二进制转字符串
a.str(二进制数据, 'utf-8')
b.二进制.decode('utf-8')
"""
client_sock.send(send_data.encode())
client_sock.close()
while True:
client_sock, client_address = s.accept()
# client_address是元祖
print('connect from:', client_address)
# 传输数据都利用client_sock,和s无关,t为新创建的线程
t = threading.Thread(target=tcp_link, args=(client_sock, client_address))
t.start()
s.close()
3、UDP编程
和TCP类类似,UDP编程也需要将通信双方分为客户端和服务器。服务器端绑定需要端口,单不需要用listen()进行监听,而是直接接收来自任何客户端的数据。客户端不需要调用connect()与服务器进行连接,直接将数据发送给服务器。
UDP客户端
import socket
BUFSIZE = 1024
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
msg = input(">> ").strip()
ip_port = ('192.168.31.230', 9999)
client.sendto(msg.encode('utf-8'), ip_port)
data, server_addr = client.recvfrom(BUFSIZE)
print('客户端recvfrom ', data.decode('utf-8'), server_addr)
client.close()
UDP服务端
import socket
BUFSIZE = 1024
ip_port = ('192.168.31.230', 9999)
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # udp协议
server.bind(ip_port)
while True:
data, client_addr = server.recvfrom(BUFSIZE)
print('server收到的数据', data.decode('utf-8'))
# 处理接收到的数据,将小写变成大写
server.sendto(data.upper(), client_addr)