1、socket介绍
socket(简称 套接字 ) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:
它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的
例如我们每天浏览网页、QQ 聊天、收发 email 等等
import socket
socket.socket(Family, Type)
参数说明:
Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
2、实现TCP服务器和客户端
服务端
import socket
# 1、创建一个tcp的套接字
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 2、绑定ip和端口
server_sock.bind(('127.0.0.1', 9988))
# 3、开启监听,设置最大的连接数
server_sock.listen(10)
while True:
# 4、等待客户端的连接
cli, addr = server_sock.accept()
# 5、接收客户端的请求数据
res = cli.recv(1024)
print("服务端接收到的数据为:", res.decode())
# 6、返回数据给客户端
cli.send('你好,客户端,我收到了你的数据'.encode())
# 7、断开连接
cli.close()
客户端
import socket
# 1、创建一个tcp的套接字
cli_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 2、连接到服务器参数(ip,port)
cli_sock.connect(('127.0.0.1', 9988))
# 3、向服务端发送请求:
info = '你好,我是tcp客户端'.encode()
cli_sock.send(info)
# 4、接收服务端的返回数据
res = cli_sock.recv(1024)
print("服务端返回的数据为:", res.decode())
# 5、关闭客户端(断开连接)
cli_sock.close()
socket:创建一个套接字 socket(AF_INET,SOCK_STREAM)
bind:绑定ip和port
listen:使套接字变为可以被动链接
accept:等待客户端的链接
recv/send:接收发送数据
3、实现HTTP服务器和客户端
http基于tcp实现,底层通信层用的是tcp协议
使用http请求TCP服务器,服务器收到的请求数据如下:
postman
浏览器
!](https://img-blog.csdnimg.cn/8b69aca650e64d57834d05125c4063a0.png)
客户端发送的数据只要是按照http格式的数据,那么客户端发送的请求就是http请求
服务端
import socket
# 1、创建一个tcp的套接字
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 2、绑定ip和端口
server_sock.bind(('127.0.0.1', 9988))
# 3、开启监听,设置最大的连接数
server_sock.listen(10)
while True:
# 4、等待客户端的连接
cli, addr = server_sock.accept()
# 5、接收客户端的请求数据(解析http请求的请求报文,并进行解析)
res = cli.recv(1024)
print("服务端接收到的数据为:", res.decode())
# 6、返回数据给客户端(返回http响应报文)
header = 'HTTP/1.1 200 OK\r\n'
header += 'Content-Type: text/html;charset=utf-8\r\n'
header += '\r\n'
body = '响应体:你好,客户端,我收到了你的数据'
header += body
cli.send(header.encode())
# 7、断开连接
cli.close()
客户端
import socket
# 1、创建一个tcp的套接字
cli_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 2、连接到服务器参数(ip,port)
cli_sock.connect(('127.0.0.1', 9988))
# 3、向服务端发送请求:
headers = 'GET / HTTP/1.1\r\n'
headers += 'Host:127.0.0.1:9988\r\n'
headers += '\r\n'
cli_sock.send(headers.encode())
# 4、接收服务端的返回数据
res = cli_sock.recv(10240)
print("服务端返回的数据为:", res.decode())
# 5、关闭客户端(断开连接)
cli_sock.close()
"""
http:请求数据包:请求首行+ 请求头 + 请求体
GET / HTTP/1.1
Host: 127.0.0.1:9988
Connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: csrftoken=Jvmv4Gz4R9N4JeSqvh9Yjg6HNJjGyrxwRdNWbsdjpec3N2SFweVMkT76DMKmZJWJ; sessionid=ml7u785x5yjkmb3hmac7mazch63b57ln
http 响应数据包:响应首行+响应头+响应体
HTTP/1.1 200 OK
Server: nginx/1.13.7
Date: Wed, 06 Apr 2022 12:40:17 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Thu, 23 Aug 2018 06:57:43 GMT
Connection: keep-alive
ETag: "5b7e5ae7-264"
Accept-Ranges: bytes
响应体xxxxx
"""
返回html的服务端
# 1、创建一个tcp的套接字
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 2、绑定ip和端口
server_sock.bind(('127.0.0.1', 9988))
# 3、开启监听,设置最大的连接数
server_sock.listen(10)
while True:
# 4、等待客户端的连接
cli, addr = server_sock.accept()
# 5、接收客户端的请求数据(解析http请求的请求报文,并进行解析)
res = cli.recv(1024)
print("服务端接收到的数据为:", res.decode())
# 6、返回数据给客户端(返回http响应报文)
##======== 返回html页面==============
# header = 'HTTP/1.1 200 OK\r\n'
# header += 'Content-Type: text/html;charset=utf-8\r\n'
# header += '\r\n'
# # 读取要返回的html页面
# with open('report.html', 'rb') as f:
# html_page = f.read()
# response = header.encode() + html_page
##======== 返回json数据==============
header = 'HTTP/1.1 200 OK\r\n'
header += 'Content-Type: application/json;charset=utf-8\r\n'
header += '\r\n'
data = {
"code":100,
"msg":"请求成功",
"data":None
}
# 将响应头和响应体进行拼接
response = header + json.dumps(data)
# 转换为二进制返回
cli.send(response.encode())
# 7、断开连接
cli.close()
4、多线程实现并发服务器
import json
import socket
from concurrent.futures.thread import ThreadPoolExecutor
def handel_request(cli):
"""处理用户请求的方法"""
# 1、接收客户端的请求数据(解析http请求的请求报文,并进行解析)
res = cli.recv(1024)
print("服务端接收到的数据为:", res.decode())
# 2、返回数据给客户端(返回http响应报文)
##======== 返回json数据==============
header = 'HTTP/1.1 200 OK\r\n'
header += 'Content-Type: application/json;charset=utf-8\r\n'
header += '\r\n'
data = {
"code": 100,
"msg": "请求成功",
"data": None
}
# 将响应头和响应体进行拼接
response = header + json.dumps(data)
# 转换为二进制返回
cli.send(response.encode())
# 3、断开连接
cli.close()
# 1、创建一个tcp的套接字
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 2、绑定ip和端口
server_sock.bind(('127.0.0.1', 9988))
# 3、开启监听,设置最大的连接数
server_sock.listen(100)
# 4、创建一个线程池,每个用户开启一个线程进行处理
with ThreadPoolExecutor(max_workers=100) as tps:
while True:
# 4、等待客户端的连接
cli, addr = server_sock.accept()
tps.submit(handel_request, cli)
5、封装http服务器
import json
import socket
from concurrent.futures.thread import ThreadPoolExecutor
class Server:
def __init__(self, ip='127.0.0.1', port=9988):
# 1、创建一个tcp的套接字
self.server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 2、绑定ip和端口
self.server_sock.bind((ip, port))
# 3、开启监听,设置最大的连接数
self.server_sock.listen(100)
def run(self):
"""服务启动的方法"""
with ThreadPoolExecutor(max_workers=100) as tps:
while True:
# 4、等待客户端的连接
cli, addr = self.server_sock.accept()
tps.submit(self.handle_request, cli)
def recv_data(self, cli):
"""循环接收请求报文,直到数据全部接受完"""
data = b''
while True:
res = cli.recv(1024)
if len(res) < 1024:
data += res
break
else:
data += res
return data.decode()
def parser_request(self, request_info):
"""
解析请求报文
:param request_info: 请求报文
:return:
{
path:请求路径,
method: 请求方法,
headers:请求头(字典)
params:查询字符串,
data:表单参数
json:json参数
}
"""
request_info_list = request_info.split('\r\n')
# 获取请求方法
method = request_info_list[0].split(' ')[0]
# 获取请求路径
path = request_info_list[0].split(' ')[1]
# 获取请求头
header_list = request_info.split('\r\n\r\n')[0].split('\r\n')[1:]
headers = {i.split(':')[0]: i.split(':')[1].strip() for i in header_list}
# 判断是否有查询字符串参数
if "?" in path:
path, params_str = path.split('?')
params_ = {i.split('=')[0]: i.split('=')[1] for i in params_str.split('&')}
else:
params_ = {}
json_params = {}
form_params = {}
# 判断是否有请求体
if headers.get('Content-Type'):
# 判断是否有json参数
if headers.get('Content-Type') == 'application/json':
request_body = request_info.split('\r\n\r\n')[1]
json_params = json.loads(request_body)
elif headers.get('Content-Type') == 'application/x-www-form-urlencoded':
request_body = request_info.split('\r\n\r\n')[1]
form_params = {i.split('=')[0]: i.split('=')[1] for i in request_body.split('&')}
# 判断是否有表单参数
return {
'path': path,
"method": method,
"headers": headers,
"params": params_,
"json": json_params,
'data': form_params
}
def handle_request(self, cli):
"""处理客户端请求的方法"""
# 1、接收客户端的请求数据
request_info = self.recv_data(cli)
# 2、解析http请求报文
request = self.parser_request(request_info)
# 3、处理相关的业务(后端开发,重点是业务处理)
# 判断请求路径,调用对应的处理函数去处理改请求
if request['path'] == '/login':
body = '登录页面'
elif request['path'] == '/register':
body = '注册页面'
elif request['path'] == '/user':
body = '用户页面'
else:
body = '你访问的路径不存在'
# 4、返回http响应报文
header = 'HTTP/1.1 200 OK\r\n'
header += 'Content-Type: text/html;charset=utf-8\r\n'
header += '\r\n'
# 将响应头和响应体进行拼接
response = header + body
# 转换为二进制返回
cli.send(response.encode())
cli.close()
if __name__ == '__main__':
app = Server()
app.run()
"""
json格式:参数传递:
POST / HTTP/1.1
Host: 127.0.0.1:9988
User-Agent: python-requests/2.24.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 102
Content-Type: application/json
{"title": "\u7528\u4f8b1", "data": "\u7528\u4f8b\u53c2\u6570", "expected": "\u9884\u671f\u7ed3\u679c"}
# 表单参数 (key1=value1&key2=value3)
POST / HTTP/1.1
Host: 127.0.0.1:9988
User-Agent: python-requests/2.24.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 113
Content-Type: application/x-www-form-urlencoded
title=%E7%94%A8%E4%BE%8B1&data=%E7%94%A8%E4%BE%8B%E5%8F%82%E6%95%B0&expected=%E9%A2%84%E6%9C%9F%E7%BB%93%E6%9E%9C
# 查询字符串参数 (在url中?key1=value1&key2=value2)
POST /?title=%E7%94%A8%E4%BE%8B1&data=%E7%94%A8%E4%BE%8B%E5%8F%82%E6%95%B0&expected=%E9%A2%84%E6%9C%9F%E7%BB%93%E6%9E%9C HTTP/1.1
Host: 127.0.0.1:9988
User-Agent: python-requests/2.24.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 0
"""