背景
我目前需要解决一个需求,将一个c工程中的特定数据转发到VUE前端框架上做界面展示,且该框架已经有后端为flask框架。所以得考虑如何将c工程中的数据发送到python中。容易知道,进程间通信的方式有管道、信号量、消息队列、共享内存、套接字等。为了简易实现上述功能和尽量不影响他们两边原先进程的功能,使用套接字发送封装的数据做http请求给flask后端,这样来实现数据转发。
HTTP(超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP请求的资源由统一资源标识符(Uniform Resource Identifiers,URI)来标识。具体区别这篇博客讲的很详细。总而言之http连接=以http协议为通信协议的tcp连接,http协议可以由tcp协议封装报文而来,现在要解决的就是c的套接字如何封装成符合http协议的get/post请求。
参考案例
最开始找了网上很多案例,tcp套接字细节此处不赘述。http请求就是其tcp传输附上对应http请求的报文,但是实际测试不对,没有相应返回。猜想到可能测试环境不同封装格式也要改变,所以使用wireshark抓包软件抓了个具体的数据包来分析。
使用的get、post请求的html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>GET and POST</title>
</head>
<body>
<form action = "http://localhost:5000" method = "get">
<table>
<tr>
<td>Name</td>
<td><input type ="text" name ="username"></td>
</tr>
<tr>
<td>Password</td>
<td><input type ="password" name ="password"></td>
</tr>
<tr>
<td><input type = "get submit"></td>
</tr>
</table>
</form>
<form action = "http://localhost:5000" method = "post">
<table>
<tr>
<td>Name</td>
<td><input type ="text" name ="username"></td>
</tr>
<tr>
<td>Password</td>
<td><input type ="password" name ="password"></td>
</tr>
<tr>
<td><input type = "post submit"></td>
</tr>
</table>
</form>
</body>
</html>
后端flask接收代码
from flask import Flask, request
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
username = request.args.get('username')
password = request.args.get('password')
if username == "xugaoxiang" and password == "xugaoxiang":
return f"<html><body>Welcome {username}</body></html>"
else:
return f"<html><body>Welcome!</body></html>"
@app.route('/', methods=['POST'])
def index():
username = request.form['username']
password = request.form['password']
if username == "xugaoxiang" and password == "xugaoxiang":
return f"<html><body>Welcome {username}</body></html>"
else:
return f"<html><body>Welcome!</body></html>"
if __name__ == '__main__':
app.run(debug=True)
测试案例取自此教程,贴出教程源码链接:https://github.com/xugaoxiang/FlaskTutorial
其抓包结果如下所示:
只需要关注http数据包中的tcp报文内容即可。
具体实现
注:为了解决大小端和数据位数不统一的问题,我是将所有数据转为字符串来发送。如果想要发送json等数据格式同样用抓包看下具体如何封装即可,此处简易的实现先不考虑那些功能。
有了上面的数据样本,进行下面c代码的封装转发。使用环境不同,部分函数可能有所变化。这里只展示基础模板。
真正通用的是下面这段补齐信令的函数
void addget(const char* str1)
{
strcat(str1, "Host: 127.0.0.1\r\n");
strcat(str1, "Content-Type: text/html\r\n\r\n");
}
void addpost(char* str1, const char* data)
{
char postlength[128];
sprintf(postlength, "%d\r\n", strlen(data + 1));
strcat(str1, "Host: 127.0.0.1\r\n");
strcat(str1, "Content-Type: application/x-www-form-urlencoded\r\n");
strcat(str1, "Content-Length: ");
strcat(str1, postlength);
strcat(str1, "\r\n\r\n");
strcat(str1, data + 1);
}
请求代码模板
只讨论http请求方面内容,展示基础的tcp套接字绑定及封装http请求流程
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <windows.h>
#include <pthread.h>
#include <assert.h>
#define PORT 5000
#define BUFSIZE 1024
#define DATASIZE 2000
enum send_flask_type {
start=1,
accomplish,
get_test,
post_test
};
int HandleFlask(const char* flaskip, const char* data, int send_type)
{
int i, sockfd_flask;
fd_set t_set1;
struct timeval timeset;
struct sockaddr_in flaskaddr;
char str1[2 * DATASIZE], buf[BUFSIZE];
if ((sockfd_flask = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
printf("创建网络连接失败,本线程即将终止!\n");
return -1;
}
flaskaddr.sin_family = AF_INET;
flaskaddr.sin_addr.s_addr = inet_addr(flaskip);
flaskaddr.sin_port = htons(PORT);
memset(&flaskaddr.sin_zero, 0, 8);
if (connect(sockfd_flask, (struct sockaddr *)&flaskaddr, sizeof(flaskaddr)) < 0){
printf("连接到flask服务器失败!\n");
return -1;
}
switch(send_type) {
case get_test:{
strcpy(str1, "GET /getsometing?");
strcat(str1, data + 1);
strcat(str1, " HTTP/1.1\r\n");
addget(str1);
break;
}
case post_test:{
strcpy(str1, "POST /postsometing HTTP/1.1\r\n");
addpost(str1, data);
break;
}
default:{
printf("接收到无效格式,舍弃\n");
return 0;
}
}
i = send(sockfd_flask, str1, strlen(str1), 0);
if (i < 0) {
printf("发送数据给flask失败!错误代码是%d\n", WSAGetLastError());
closesocket(sockfd_flask);
return -1;
}
else {
}
FD_ZERO(&t_set1);
FD_SET(sockfd_flask, &t_set1);
timeset.tv_sec= 0;
timeset.tv_usec= 100000;
i= select(sockfd_flask +1, &t_set1, NULL, NULL, ×et);
if (i == 0) {
}
else if (i < 0) {
printf("在读取flask数据报文时SELECT检测到异常,该异常导致线程终止!\n");
closesocket(sockfd_flask);
return -1;
}
else {
memset(buf, 0, sizeof(buf) );
i = recv(sockfd_flask, buf, sizeof(buf), 0);
if (i <= 0) {
closesocket(sockfd_flask);
if (i == 0) {
return 0;
}
else {
printf("接收flask数据报出现错误!错误代码是%d\n", WSAGetLastError());
return -1;
}
}
else {
}
}
closesocket(sockfd_flask);
return 0;
}
flask接收示例
***flask后端测试用****
@app.route('/getsometing', methods=['GET'])
def gettest():
type = request.args.get('type')
src = request.args.get('src')
dst = request.args.get('dst')
print(f"get data: type={type},src={src},dst={dst}")
return f"<html><body>get return: type={type},src={src},dst={dst}</body></html>"
@app.route('/postsometing', methods=['POST'])
def posttest():
type = request.form['type']
src = request.form['src']
dst = request.form['dst']
print(f"post data: type={type},src={src},dst={dst}")
return f"<html><body>post return: type={type},src={src},dst={dst}</body></html>"
#如果数据量比较大可以这样写
@app.route('/getsometing', methods=['GET'])
def gettest():
get_data=request.args.to_dict()
type = get_data['type']
cur_time=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
if type == 'Start' :
pass
elif type == 'Accomplish' :
pass
elif type == 'change' :
pass
else :
print ("接收到无效数据,将其丢弃")
with open("log.txt", "a+") as f:
f.write('\n# ' + cur_time + ' ---------- get error:\n' + json.dumps(get_data))
return "<html><body> Flask access invalid data </body></html>"
return "<html><body> data access </body></html>"
@app.route('/postsometing', methods=['POST'])
def posttest():
post_data = request.form.to_dict()
print (post_data)
type = post_data['type']
cur_time=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
if type == "change":
pass
else :
print ("接收到无效数据,将其丢弃")
with open("log.txt", "a+") as f:
f.write('\n# ' + cur_time + ' ---------- post error:\n' + json.dumps(post_data))
return "<html><body> Flask access invalid data </body></html>"
return "<html><body> data access </body></html>"
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)