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还在接受数据,这时会出现几百毫秒的卡顿,这个问题也没搞明白。