项目要用到通信协议,强行学习了一波HTTP协议
报文格式
请求格式
HTTP请求由状态行、请求头、请求正文三部分组成:
- 状态行:包括请求方式Method、资源路径URL、协议版本Version;
- 请求头:包括一些访问的域名、用户代理、Cookie等信息;
- 请求正文:就是HTTP请求的数据。
请求方式Method一般有GET、POST、PUT、DELETE,含义分别是获取、修改、上传、删除,其中GET方式仅仅为获取服务器资源,方式较为简单,因此在请求方式为GET的HTTP请求数据中,请求正文部分可以省略,直接将想要获取的资源添加到URL中。
上面这张图是Fiddle捕获的一个实际请求报文,它清晰的展示了HTTP 消息的结构。详情如下:
- 请求行:即第一排用空格分割成的三个小块,分别对应请求方法、请求URL、HTTP协议版本三个部分。
- 请求头:从第二行开始到倒数第二行都是我们的请求头(headers)。
- 消息主体:截图的最后一行是请求体,也就是我们要发送的数据的主体,消息主体(entity-body)。
- 也就是说一个正常的post请求主要由请求行,请求头,消息主体组成。接下来我们来了解一下什么是Content-Type。
Content-Type的格式种类
四种最常用的编码方式,基本上形成了相应的规范,即基本固定的Content-Type取值application/x-www-form-urlencoded(默认格式)、application/json、text/xml、multipart/form-data,与默认传递的urlencoded、json格式、xml格式、文件格式一 一对应。
应答格式
HTTP响应由三部分组成:状态行、响应头、响应正文;
- 状态行:包括协议版本Version、状态码Status Code、回应短语;
- 响应头:包括搭建服务器的软件,发送响应的时间,回应数据的格式等信息;
-
响应正文:就是响应的具体数据
使用轮子
Python Requests 快速上手
https://2.python-requests.org//zh_CN/latest/user/quickstart.html
Python实现简单HTTP服务器
https://www.cnblogs.com/xinyangsdut/p/9099623.html
Python Http Post请求四种请求体的Python实现
https://www.cnblogs.com/Detector/p/9404391.html
使用socket创建简单的客户端和服务端
客户端
client.py
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 5 17:00:36 2019
@author: Administrator
"""
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务端
s.connect(('127.0.0.1', 9999))
# 请求 | 发送数据到服务端
s.sendall(b'hello')
# 响应 | 接受服务端返回到数据
data = s.recv(1024)
print(data) # hello
# 关闭 socket
s.close()
服务端
# -*- coding: utf-8 -*-
"""
Spyder Editor
This is a temporary script file.
"""
import socket
# socket.AF_INET (IPV4)
# socket.SOCK_STREAM (TCP)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 监听 IP:port
s.bind(('127.0.0.1', 9999))
# 最大允许连接数量
s.listen(3)
# 死循环,重复的处理着每个客户端的请求
while True:
# 阻塞 每当有客户端的请求过来开始执行
# 连接处理 (已完成三次握手)并获取资源对象 | conn 请求对象 | addr 客户端地址 ip: port
conn, addr = s.accept()
# 请求处理 | 读取客户端发送过来的数据 | recv(1024) 指定每次读取 1024 字节,当数据较长时可以通过 while 循环读取
data = conn.recv(1024).decode('utf-8')
# 响应处理 | 把客服端发送过来的数据又转发回去
conn.sendall(data.encode('utf-8'))
# 关闭客户端连接
conn.close()
HTTP服务端
# -*- coding: utf-8 -*-
"""
Created on Mon Jul 8 09:02:54 2019
@author: Administrator
"""
# coding:utf-8
import socket
import json
from multiprocessing import Process
def handle_client(client_socket):
"""
处理客户端请求
"""
request_data = client_socket.recv(1024)
print("request data:", request_data)
# 构造响应数据
response_start_line = "HTTP/1.1 200 OK\r\n"
response_headers = "Server: My server\r\n"
response_body = "<h1>Python HTTP Test</h1>"
datas={"param1": "Detector", "param2": "cnblogs"}
response = response_start_line + response_headers + "\r\n" + datas
# 向客户端返回响应数据
client_socket.send(bytes(response, "utf-8"))
# 关闭客户端连接
client_socket.close()
if __name__ == "__main__":
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("", 8000))
server_socket.listen(128)
while True:
client_socket, client_address = server_socket.accept()
print("[%s, %s]用户连接上了" % client_address)
handle_client_process = Process(target=handle_client, args=(client_socket,))
handle_client_process.start()
client_socket.close()
C端TCP/IP 服务器
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void initialization();
int server() {
//定义长度变量
int send_len = 0;
int recv_len = 0;
int len = 0;
//定义发送缓冲区和接受缓冲区
char send_buf[100];
char recv_buf[100];
memset(send_buf, 0, sizeof(send_buf));
memset(recv_buf, 0, sizeof(recv_buf));
//定义服务端套接字,接受请求套接字
SOCKET s_server;
SOCKET s_accept;
//服务端地址客户端地址
SOCKADDR_IN server_addr;
SOCKADDR_IN accept_addr;
initialization();
//填充服务端信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(8001);
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
if (bind(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
cout << "bing faild!" << endl;
WSACleanup();
}
else {
cout << "bing success!" << endl;
}
//设置套接字为监听状态
if (listen(s_server, SOMAXCONN) < 0) {
cout << "listen faild!" << endl;
WSACleanup();
}
else {
cout << "listen success!" << endl;
}
cout << "listening...." << endl;
//接受连接请求
len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);
if (s_accept == SOCKET_ERROR) {
cout << "listen faild!" << endl;
WSACleanup();
return 0;
}
cout << "connect..." << endl;
//接收数据
while (1) {
recv_len = recv(s_accept, recv_buf, 1, 0);
if (recv_len <= 0) {
cout << "rec failed!" << endl;
break;
}
else {
cout << "client:" << recv_buf << endl;
}
/*cout << "please cin:";
cin >> send_buf;*/
//send_len = send(s_accept, send_buf, 100, 0);
//if (send_len < 0) {
// cout << "send failed!" << endl;
// break;
//}
}
//关闭套接字
closesocket(s_server);
closesocket(s_accept);
//释放DLL资源
WSACleanup();
return 0;
}
void initialization() {
//初始化套接字库
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "init faied!" << endl;
}
else {
cout << "init success!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
cout << "version error!" << endl;
WSACleanup();
}
else {
cout << "version right!" << endl;
}
//填充服务端地址信息
}