Socket传输图片
最近做一个项目,需要c++端和python端进行通信,c++将图片传到python端,然后python把处理完数据传回到c++端,然后就尝试了用socket通信,c++作为客户端,python作为服务器。
文件流发送
刚开始是用本地图片测试的,c++读取图片,发送到python端,然后python将图片数据写进硬盘,然后进行读取,然后测试
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void initialization();
int main() {
int cnt = 0;
//定义长度变量
int send_len = 0;
int recv_len = 0;
//定义发送缓冲区和接受缓冲区
char send_buf[2000];
char recv_buf[2000];
//定义服务端套接字,接受请求套接字
SOCKET s_server;
//服务端地址客户端地址
SOCKADDR_IN server_addr;
initialization();
//填充服务端信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(1234);
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
cout << "服务器连接失败!" << endl;
WSACleanup();
}
else {
cout << "服务器连接成功!" << endl;
}
//发送图片
FILE *fq = fopen("2.jpg", "rb");
while (!feof(fq))
{
cout << cnt++ << endl;
send_len = fread(send_buf, 1, sizeof(send_buf), fq);
send(s_server, send_buf, 2000, 0);
}
send(s_server, "exit", 2000, 0);
//发送,接收数据
while (1) {
cout << "请输入发送信息:";
cin >> send_buf;
send_len = send(s_server, send_buf, 100, 0);
if (send_len < 0) {
cout << "发送失败!" << endl;
break;
}
recv_len = recv(s_server, recv_buf, 100, 0);
if (recv_len < 0) {
cout << "接受失败!" << endl;
break;
}
else {
cout << "服务端信息:" << recv_buf << endl;
}
}
//关闭套接字
closesocket(s_server);
//释放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 << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}
def deal_image(sock):
# print("Accept connection from {0}".format(addr)) # 查看发送端的ip和端口
cnt = 0
new_filename = "cat1.jpg"
fp = open("scr.jpg", 'wb')
pxiel = 0
#接受焦距信息
data = sock.recv(200)
string = data.decode('utf-8','ignore').split('\x00')[0]
if string[0] == 'p':
pxiel = int(string[1:3])
print(pxiel)
#接受图片数据
data = sock.recv(2000)
start_time = time.time()
while True:
# sock.send(cnt)
# print(len(data))
# print(data[0:4])
string = data.decode('utf-8','ignore').split('\x00')[0]
# print(type(data),type(string))
if string=='end':
print("over")
break
fp.write(data) # 写入图片数据
data = sock.recv(2000)
# s= dete_img.test_img(img_path).cpu().numpy()[0]
# sock.send(str(key_value[s]).encode('utf-8')) #.decode('utf-8')).encode('utf-8')
#print(s)
fp.close()
end_time = time.time()
#发送识别到的信息
str_list = ["1","2"]
str = "1,jingling,0.34,100,200,300,400,23,t16,0.34,100,200,300,400,23"
sock.send(str.encode())
str = "0.34,100,200,300,400,23"
sock.send(str.encode())
print(end_time-start_time)
sock.close()
编码发送
后来意识到把图片数据写到硬盘和之前的文件夹通信一样,不是很安全,所以就想对视频中图片进行编码,然后服务器解码即可。
vector<string> sendsocket::getSocketInfo(cv::Mat img, int p)
{
int x1 = (img.cols - img.rows) / 2;
cv::Rect rect = cv::Rect(x1, 0, img.rows, img.rows);
img = img(rect);
std::vector<uchar> data_encode;
std::vector<int> quality;
quality.push_back(CV_IMWRITE_JPEG_QUALITY);
quality.push_back(90);//进行90%的压缩
imencode(".jpg", img, data_encode, quality);//将图像编码
int nSize = data_encode.size();
string s1 = "p," + to_string(p) + "," + to_string(nSize) + ",";
const char* c = s1.c_str();
for (long int i = 0; i < nSize; i++)
{
encodeImg[i] = data_encode[i];
}
send(sock, c, 200, 0);
//发送图片
long int send_num = 0;
while (send_num < nSize)
{
send(sock, encodeImg + send_num, 2000, 0);
send_num += 2000;
}
send(sock, "end", 100, 0);
//接受数据
recv(sock, recv_buf, 400, 0);
vector<string> img_info = split(recv_buf, ",");
memset(recv_buf, '\0', sizeof(recv_buf));
memset(&encodeImg, '\0', sizeof(encodeImg)); //初始化结构体
return img_info;
}
多线程编码发送
之前通信顺序是串行的,感觉这样速度很慢,所以想用多线程提高一下速度,如果图片数据比较小还好,速度只被网络处理时间限制,如果图片很大的话,socket传输时间还是挺长的。
在python中,将网络和socket通信各自给了一个线程,理想情况下是,网络处理完图片,就可以处理下一张图片,不需要等待。
def dealSocketMessage():
global image,pxiel,sock,recv_flag,send_flag,lock
# while True:
# cv2.waitKey(100)
# print("test socket")
cnt = 0
while True:
# print("recv_flag",recv_flag)
if recv_flag:
start_time_s = time.time()
start_time = time.time()
data = sock.recv(200)
print("time1",time.time()-start_time)
string = data.decode('utf-8', 'ignore').split(',')
if string[0] == 'p':
pxiel = int(string[1])
img_bytes = bytes()
start_time = time.time()
while True:
start_time1= time.time()
data = sock.recv(2000)
print("recve img time",time.time()-start_time1)
string = data.decode('utf-8', 'ignore').split('\x00')[0]
if string == "end":
# print("接受成功")
lock.acquire()
img = np.asarray(bytearray(img_bytes), dtype="uint8")
img = cv2.imdecode(img, cv2.IMREAD_COLOR)
image = copy.deepcopy(img)
lock.release()
break
img_bytes = img_bytes + data
print("time2", time.time() - start_time)
str = ",".join(str_list) + ","
# 发送识别到的数据
sock.send(str.encode())
lock.acquire()
recv_flag = False
lock.release()
cnt+=1
if cnt==101:
cv2.waitKey()
print("recv time ",(time.time()-start_time_s))
else:
cv2.waitKey(1)
问题
1、设置一个标志位,True代表socket接受图片数据,当为false表示接受完成。我是一直判断的,但是发现这样处理速度在1fps,当时很郁闷,后来改成每隔nms检测一次标志位就好了,但是具体原因还没有找到。
2、如果图片数据比较小,可以达到顶速,也就是网络的速度,但是图片为1080p时,如果网络已经处理完图片,但是socket还在接受数据,这时会出现几百毫秒的卡顿,这个问题也没搞明白。