linux c++ 與python之間使用tcp傳輸圖片

客戶端爲 c++,服務端爲python

客戶端可參考:https://blog.csdn.net/qq_31511955/article/details/94580915

json庫地址:https://github.com/nlohmann/json/tree/develop/include/nlohmann

需要一個第三方的json庫,主要流程爲:圖片用imread轉換爲Mat,然後Mat通過imencode函數轉爲uchar*,再轉換爲string類型,然後通過c_str()函數轉換爲const char*,最後Base64編碼存到json裏面

 客戶端代碼:

#include "myTcp.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include "base64.h"
#include "json.hpp"

using namespace std;
using namespace cv;
using json = nlohmann::json;


int main()
{	
	json j;
	const char* c;
	Mat image = imread("./1.jpg", IMREAD_UNCHANGED);
	if (image.empty())
	{
		cout<<"read image failed \n";
		return -1;
	}
	else
		cout<<"read successful!\n";

	std::vector<unsigned char> data_encode;
	int res = imencode(".jpg", image, data_encode);
	std::string str_encode(data_encode.begin(), data_encode.end());
	c = str_encode.c_str();
	j["mat"] = base64_encode(c, str_encode.size());
	
	string s = j["mat"].dump();
	const char * sendData;
	sendData = s.c_str();
	
	TCPclient tcp;
	if(tcp.connect_Server("127.0.0.1", 6789))
		cout<<"connect successful!\n";
	else
		cout<<"connect failed\n";
	

	//先發送圖片大小,在發送圖片數據
	int length = strlen(sendData);
	cout << tcp.Send((char*)&length, sizeof(int)) << endl;
	cout << tcp.Send((char*)sendData, length) << endl;
	

	return 0;
}

服務器利用消息隊列機制:

#識別服務
import socketserver 
import threading
from queue import Queue
import cv2
import json
import base64
import numpy as np


HOST = '127.0.0.1' 
PORT = 6789
ADDR = (HOST, PORT) 

class msgRequestHandler(socketserver.BaseRequestHandler): 
    def handle(self): 
        print ('connected from.....', self.client_address) 
        while True:
            head = self.request.recv(4)
            data = bytearray(head)
            
            #獲取圖片大小
            msgLen = int.from_bytes(data, byteorder='little')
            print('msg len is %d' %(msgLen))
            
            #獲取圖片
            curSize = 0
            allData = b''
            while curSize < msgLen:
                data = self.request.recv(1024)
                allData += data
                curSize += len(data)
                print(curSize)
            print("recv data len is ", len(allData))
            
            img = json.loads(allData)
            msg = {}
            msg['sock'] = self.request
            print(msg['sock'])
            msg['img'] = img
            #print(img)
            msgQueue.put(msg)
            
            break;
   


def processRequest():
    while True:
        msg = msgQueue.get()
        #print (msg)
        
        #解碼保存
        imgDecode = base64.b64decode(msg['img'])

        #保存圖片
        fImg = open('2.jpg', 'wb')
        fImg.write(imgDecode)
        fImg.close()
        print("save img...\n")
        
        socket.close()
        
        print('\n')
        
if __name__ == "__main__": 
    msgQueue = Queue(maxsize=10)
    detect_thread = threading.Thread(target=processRequest)
    detect_thread.start()
    
    tcpServ = socketserver.ThreadingTCPServer(ADDR, msgRequestHandler) 
    print ('waiting for connection...')
    tcpServ.serve_forever()

others:

封裝的tcp類:可參考:https://blog.csdn.net/PPprogrammer/article/details/103018358

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <netdb.h>
#include <iostream>

using namespace std;

class TCPclient
{
public:
        TCPclient()
		{
            sock_clientfp=0;
        }
		
        ~TCPclient()
		{
            if(sock_clientfp > 0 ){
               close(sock_clientfp);   //在析構函數中關閉socket
            }
        }
public:
        bool connect_Server(const char *serverIP,int port);  //連接服務端需要做的事情,也就是一些函數的使用和初始化
        ssize_t Send(const void *buf, size_t len);    //發送數據
        ssize_t Recv(void *buf, size_t len);          //接收數據
                                             //這兩個函數都會等待服務端的回覆
private:
        int sock_clientfp;
};


bool TCPclient::connect_Server(const char *serverIP,int port)
{
	sock_clientfp = socket(AF_INET,SOCK_STREAM,0); //創建客戶端的socket
	int flags  = fcntl(sock_clientfp, F_GETFL, 0);
	fcntl(sock_clientfp, F_SETFL, flags&~O_NONBLOCK); 
	struct hostent *h; //客戶端ip地址的結構體
	if((h = gethostbyname(serverIP)) == 0)
	{
		close(sock_clientfp);
		sock_clientfp = 0;
		return false;
	}

	//把服務器的地址和端口轉換爲數據結構體
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family = AF_INET;  //協議族,在socket編程中只能是AF_INET
	servaddr.sin_port = htons(port); //綁定通訊端口
	memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);

	//向服務端發起連接請求
	if(connect(sock_clientfp,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0)
	{
		close(sock_clientfp);
		sock_clientfp = 0;  //這裏手動置零比較嚴謹
		return false;
	}
	return true;
}


ssize_t TCPclient::Send(const void *buf, size_t len)
{
	return send(sock_clientfp, buf,len,0);
}


ssize_t TCPclient::Recv(void *buf, size_t len)
{
	memset(buf,0,len);
	return recv(sock_clientfp, buf,len,MSG_WAITALL);
}

base64.h

#ifndef __BASE64_H__
#define __BASE64_H__
 
#include <iostream>
#include <string>
 
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
 
static inline bool is_base64(const char c)
{
	return (isalnum(c) || (c == '+') || (c == '/'));
}
 
std::string base64_encode(const char * bytes_to_encode, unsigned int in_len)
{
	std::string ret;
	int i = 0;
	int j = 0;
	unsigned char char_array_3[3];
	unsigned char char_array_4[4];
 
	while (in_len--)
	{
		char_array_3[i++] = *(bytes_to_encode++);
		if (i == 3)
		{
			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;
			for (i = 0; (i <4); i++)
			{
				ret += base64_chars[char_array_4[i]];
			}
			i = 0;
		}
	}
	if (i)
	{
		for (j = i; j < 3; j++)
		{
			char_array_3[j] = '\0';
		}
 
		char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
		char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
		char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
		char_array_4[3] = char_array_3[2] & 0x3f;
 
		for (j = 0; (j < i + 1); j++)
		{
			ret += base64_chars[char_array_4[j]];
		}
 
		while ((i++ < 3))
		{
			ret += '=';
		}
 
	}
	return ret;
}
 
std::string base64_decode(std::string const & encoded_string)
{
	int in_len = (int)encoded_string.size();
	int i = 0;
	int j = 0;
	int in_ = 0;
	unsigned char char_array_4[4], char_array_3[3];
	std::string ret;
 
	while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
		char_array_4[i++] = encoded_string[in_]; in_++;
		if (i == 4) {
			for (i = 0; i <4; i++)
				char_array_4[i] = base64_chars.find(char_array_4[i]);
 
			char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
			char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
			char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
 
			for (i = 0; (i < 3); i++)
				ret += char_array_3[i];
			i = 0;
		}
	}
	if (i) {
		for (j = i; j <4; j++)
			char_array_4[j] = 0;
 
		for (j = 0; j <4; j++)
			char_array_4[j] = base64_chars.find(char_array_4[j]);
 
		char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
		char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
		char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
 
		for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
	}
 
	return ret;
}
#endif
 

 

另外客戶端在linux下用g++編譯時,json庫要用 c++11標準編譯,別忘了指定opencv庫和頭文件目錄、json頭文件目錄

g++ -std=c++11  client.cpp `pkg-config --cflags --libs opencv` -I/mnt/data/myTest/  -o client

在測試時,我是在docker裏測的,在同一個bash裏測試比較麻煩,需要將服務器切到後臺操作。這裏建議複製一份bash,然後在新bash裏進入到同一個docker裏

sudo docker exec -it ceb4147fafda /bin/bash 

一個問題:這裏將接收到的圖片保存到本地時圖片大小變大了,但是肉眼看不出來變化,中間出現的問題待解決...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章