网络通信概述
网络:网络是一种辅助双方或者多方能够连在一起的工具
使用网络的目的:就是为了联通多方然后进行通信用的,即把数据从一方传递到另一方
所谓的网络编程就是,让在不同的电脑上的软件能够进行数据传递,即进程之间的通信。
HTTP协议:
通俗讲http是一种约定浏览器和服务器之间传输的协议,服务器和浏览器之间发送的内容都是基于TCP的二进制数据。协议格式如下:
浏览器------>服务器:
GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
服务器------>浏览器:
HTTP/1.1 200 OK # 这个是必须要回送的
Bdpagetype: 2
Bdqid: 0xfed1826e000c0f58
Cache-Control: private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Sat, 11 Apr 2020 12:01:18 GMT
Expires: Sat, 11 Apr 2020 12:01:18 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=488; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=1447_21127_30842_31187_30908_31270_31229_30824_31085_31164_31196; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1586606478022147533818361600564725026648
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
<html>
......
</html>
模拟服务器回送消息给浏览器(responst_header和responst_body之间要有一个空行):
HTTP/1.1/ 200 OK
<p>百度一下,你就知道</p>
用python实现返回浏览器固定页面的http服务器:
1 import socket 2 3 4 def service_client(client_socket): 5 """为这个客户端返回数据""" 6 # 1.接收浏览器发送过来的请求 7 request = client_socket.recv(1024) 8 print(request) 9 10 # 2.返回http格式的数据给浏览器 11 # 2.1 准备发送给浏览器的header 12 response = "HTTP/1.1 200 OK\r\n" 13 response += 'Content-Type: text/html;charset=utf-8\r\n' 14 response += '\r\n' 15 # 2.2 准备发送给浏览器的body 16 response += '<h1><a href="http://www.baidu.com">百度一下,你就知道</a></h1>' 17 client_socket.send(response.encode('utf-8')) 18 19 # 关闭套接字 20 client_socket.close() 21 22 23 def main(): 24 """用来完成整体的控制""" 25 # 1.创建套接字 26 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 27 # tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 28 29 # 2.绑定 30 tcp_server_socket.bind(('', 7788)) 31 32 # 3.设置为被动监听 33 tcp_server_socket.listen(128) 34 35 while True: 36 # 4.等待客户端连接 37 client_socket, client_adress = tcp_server_socket.accept() 38 39 # 5.为客户端服务 40 service_client(client_socket) 41 42 # 关闭监听套接字 43 tcp_server_socket.close() 44 45 46 if __name__ == '__main__': 47 main()
效果:
TCP的三次握手、四次挥手:
三次握手——保证双方准备资源:
- 客户端调用connect方法后堵塞,此时会发送一个数据包给服务器
- 服务器将数据包解密后回传给客户端以便让客户端知道服务器已经准备完毕,同时会发送一个数据包给客户端
- 客户端此时connect解堵塞,并将服务器的数据包解密回传给服务器,告知服务器客户端也准备完毕。之后便都开始收发数据
四次挥手——将资源释放:
- 客户端要关闭先告诉服务器不再给服务器发送数据了,此时客户端会关闭发送数据
- 服务器接收后会关闭接收数据
- 服务器随后告诉应用程序关闭客户端套接字,并告诉客户端不再接收数据
- 客户端收到后关闭接收数据,回传确认接收消息给服务器,服务器关闭发送数据
四次挥手扩展:
当客户端接收到服务器不再接收数据的消息后,会回传消息给服务器,但后面并不能知道服务器是否有收到这个消息。于是第3步的时候服务器告诉客户端不再接收数据后会有一个超时时间(在此时间内如果没有收到客户端回传的消息将会重发),客户端在接收到服务器不再接收数据的消息后会等待两倍的MSL(数据包在网上存活的最长时间)。
如果客户端和服务器的过程反过来,服务器先关闭,那么服务器最后会等待两倍的MSL,在这时间内服务器的资源不会释放,端口也会一直占用。
为了避免因服务器先关闭造成服务器资源占用,需将服务器监听套接字调用setsockopt()方法,如下:
1 # 1.创建套接字 2 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 3 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
代码改进:返回浏览器需要访问的页面:
1 import socket 2 import re 3 4 5 def service_client(client_socket): 6 """为这个客户端返回数据""" 7 # 1.接收浏览器发送过来的请求 8 request = client_socket.recv(1024).decode('utf-8') 9 # 将浏览器请求的头部信息,根据换行拆分成一个列表 10 headers = request.splitlines() 11 # print(headers[0]) # GET /XXX/XXXX HTTP/1.1 12 ret = re.match(r'[^/]+(/[^ ]*)', headers[0]) 13 print(ret.group()) 14 if ret: 15 # 符合正则的值为GET /XXX/XXX和/XXX/XXX 取后面这个group(1) 16 file_name = ret.group(1) 17 # 如果浏览器请求的是127.0.0.1(也就是只有一个/),那么让浏览器显示主页(index.html) 18 if file_name == '/': 19 file_name = '/index.html' 20 21 # 2.返回http格式的数据给浏览器 22 try: 23 f = open('jQuery响应式网站导航菜单'+file_name, 'rb') 24 except: 25 # 如果浏览器访问的页面不存在,则返回404 26 response = "HTTP/1.1 404 \r\n" 27 response += 'Content-Type: text/html;charset=utf-8\r\n' 28 response += '\r\n' 29 response += "您所访问的地址或文件不存在" 30 # 发送网页的headers 31 client_socket.send(response.encode('utf-8')) 32 else: 33 # 2.1 准备发送给浏览器的header 34 response = "HTTP/1.1 200 OK\r\n" 35 response += 'Content-Type: text/html;charset=utf-8\r\n' 36 response += '\r\n' 37 # 发送网页的headers 38 client_socket.send(response.encode('gbk')) 39 40 # 发送网页的body 41 html_content = f.read() 42 f.close() 43 client_socket.send(html_content) 44 45 # 关闭套接字 46 client_socket.close() 47 48 49 def main(): 50 """用来完成整体的控制""" 51 # 1.创建套接字 52 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 53 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 54 55 # 2.绑定 56 tcp_server_socket.bind(('', 7788)) 57 58 # 3.设置为被动监听 59 tcp_server_socket.listen(128) 60 61 while True: 62 # 4.等待客户端连接 63 client_socket, client_adress = tcp_server_socket.accept() 64 65 # 5.为客户端服务 66 service_client(client_socket) 67 68 # 关闭监听套接字 69 tcp_server_socket.close() 70 71 72 if __name__ == '__main__': 73 main()