Vintage appMaker의 Tech Blog

python에서 간단한 RPC 구현 (Ubuntu <--> Windows) 본문

Source code or Tip/python

python에서 간단한 RPC 구현 (Ubuntu <--> Windows)

VintageappMaker 2020. 12. 9. 09:59
 

VintageAppMaker/PythonRPC

파이썬 RPC 구현. Contribute to VintageAppMaker/PythonRPC development by creating an account on GitHub.

github.com

 

해더

#pragma pack(push, 1)
      // Header 정보
      typedef struct tagPacketHeader{
          CHAR   identify[3];  // 인식자 PSW
          int    length;       // 데이터 크기 
          BYTE   encode;       // 클라이언트 문자열 인코딩 종류(아직 미정)  
          BYTE   req;          // 0:shell, 1:python
     } PacketData;
  #pragma pack(pop)

 

ubuntu server

# -*- coding: utf-8 -*-
#  작성자: 박 성완(adsloader@naver.com)
#  목적  : 리눅스용 편리한 서버 사용  
#  작성일: 2012.03.17  

import socket
import threading
import SocketServer
import os
import struct
import binascii
import array
import base64

# packet 처리용 클래스 
class Packet:
    
    # 생성자: 변수 초기화용 
    def __init__(self):

        self.m_pData     = []    # Data 값           
        self.m_HDRSize   = 9     # packet Header Size 
        
        #packet 관련 정의
        self.struct   = ' 3B l B B'
        self.identify = []
        
    # data 읽기함수     
    def ReadData(self, pRead):
        tmp = []
        try:
            s   = struct.Struct("<" + self.struct)
            tmp = s.unpack(pRead)
            print "unpack hdr:" ,  tmp
 
            # 필드 자르기 
            self.identify = tmp[0:3]
            self.length   = tmp[3]
            self.encode   = tmp[4]
            self.req      = tmp[5]
        
        except:
            print "Structure Format Error!!"
            return 
        
        
    # data 쓰기함수     
    def WriteData(self, clientID, req, pWrite):
        try:
            
            rst =[]
            
            tmp = []
            tmp += [ord('P'), ord('S'), ord('W')]
            tmp.append(clientID)
            tmp.append(req)
            tmp.append(0)
            
            tmp += pWrite
            sFormat =  "<" + self.struct + " %dB" %  len(pWrite)
            print sFormat
            s = struct.Struct(sFormat)
            rst = s.pack(*tmp)
                        
            return rst
            
        except:
            print "Structure Format Error!!"
            return 


# 클라이언트에서 호출할 함수
def TestFunc(str):
    return "Test Func called with (%s)" % str

class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    # command를 실행하기  
    def doShellCommand(self, sCmd):
        pingaling = os.popen(u"%s"% sCmd)
        sline = ""
             
        while 1:
            line = pingaling.readline()
            sline += line
            if not line: break

        return sline 

    # python command를 실행하기  
    def doPythonCommand(self, sCmd):
        result = ""
        exec(sCmd)
        print result
        return result  

    def MakeSendCommand(self,p , sMsg ):
        return p.WriteData(len(sMsg) + 1, 3, self.MakeString(sMsg))

    def MakeString(self, sMsg):
        # data영역 만들기 
        Data = []
        #sData = bytes(sMsg) 
        sData = sMsg 
        for a in sData:
            Data.append ( ord(a) )
        Data.append(0)
        return Data

    def handle(self):
         
        data    = []
        rcvdata = []
       
        # function table 설정  
        CMD = {0:self.doShellCommand,1: self.doPythonCommand}        

        p = Packet()
        nReadSize = p.m_HDRSize
        MaxSize   = nReadSize 
        bHeader   = 0
        
        while True:
            rcvdata  = self.request.recv(nReadSize)
          
            # 소켓종료
            if rcvdata == "":
                break
            
            #  버퍼링  
            if len(data) == 0 :
                data = rcvdata
            else:
                data += rcvdata
            
            # 패킷크기만큼 대기 한다. 
            if len(data) < MaxSize:
                continue
            
            # Header분석 
            if bHeader  == 0: 
                p.ReadData(data)
                nReadSize = p.length                 
                MaxSize   = p.m_HDRSize + nReadSize
                bHeader   = 1
                continue 

            sData = unicode(data[9:-1],"cp949")
            print sData

            # 펑션테이블을 dictionary로 제어하다. 
            sline = CMD.get(p.req, self.doShellCommand )(sData)
            wData = self.MakeSendCommand(p, sline )
            self.request.send(wData) 
            
            data = []
        
    def finish(self):
        try:
            print "closed"
        
        except:
            print "unknown error"
        
class ThreadedTCPServer(SocketServer.TCPServer):
   pass 

if __name__ == "__main__":
    HOST, PORT = "", 9080

    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    ip, port = server.server_address

    # Start a thread with the server -- that thread will then start one
    # more thread for each request
    server_thread = threading.Thread(target=server.serve_forever)

    # Exit the server thread when the main thread terminates
    
    # False일 경우는 프로그램 종료안하고 데몬이 실행됨.
    server_thread.setDaemon(False)
    server_thread.start()

Windows Client



#include "stdafx.h"
#include "Packet.h"

char* UTF8ToANSI(const char *pszCode)
{
	BSTR    bstrWide;
	char*   pszAnsi;
	int     nLength;

	nLength = MultiByteToWideChar(CP_UTF8, 0, pszCode, lstrlen(pszCode) + 1, NULL, NULL);
	bstrWide = SysAllocStringLen(NULL, nLength);

	MultiByteToWideChar(CP_UTF8, 0, pszCode, lstrlen(pszCode) + 1, bstrWide, nLength);

	nLength = WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, NULL, 0, NULL, NULL);
	pszAnsi = new char[nLength];

	WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, pszAnsi, nLength, NULL, NULL);
	SysFreeString(bstrWide);

	return pszAnsi;
}

// 이벤트 전송 - 소켓접속 및 전송 일괄처리
int SendEvent(int nEvnet, int nSize, BYTE* pData)
{
	//	int nInt = sizeof(CPacket::PacketData);

	SOCKET sock;
	SOCKADDR_IN dest_sin;
	WORD wPort = 9080;
	int rc;

	// Create socket
	sock = socket( AF_INET, SOCK_STREAM, 0);
	if (sock == INVALID_SOCKET) {
		return INVALID_SOCKET;
	}

	// Set up IP address to access
	memset (&dest_sin, 0, sizeof (dest_sin));
	dest_sin.sin_family = AF_INET;
	//dest_sin.sin_addr.S_un.S_addr = inet_addr ("10.0.2.15");
	dest_sin.sin_addr.S_un.S_addr = inet_addr ("192.168.0.17");
	dest_sin.sin_port = htons(wPort);

	// Connect to the device
	rc = connect( sock, (PSOCKADDR) &dest_sin, sizeof( dest_sin));
	if (rc == SOCKET_ERROR) {
		closesocket( sock );
		return INVALID_SOCKET;
	}

	// 값 저장, 패킷전송, 패킷수신
	CPacket pkt(sock);
	pkt.SetHeader(nEvnet, 0);

	// 데이터가 있다면 복사한다.
	if( nSize > 0){
		pkt.SetData(pData, nSize);
	}
	
	pkt.SendPacket();
	pkt.Analyze();

	// 읽은 정보 출력하기 
	char* szMsg = (char *)pkt.pPackData;
	printf ("recived(%d):%s\r\n", strlen(szMsg), UTF8ToANSI(szMsg));
    
	CPacket::PacketData* pInfo =  pkt.GetPacketInfo();
	closesocket (sock);
	
	return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsaData;
	if ( ( WSAStartup(0x101,&wsaData) ) != 0) {
		WSACleanup();
		return -1;
	}

	char* szMsg  = "result = TestFunc('메롱')";
	SendEvent(CPacket::PKT_PYTHON, strlen(szMsg)+1, (BYTE*)szMsg);
	
	char* szMsg2 = "ps  | grep 'python' && ls -al";
	SendEvent(CPacket::PKT_SHELL, strlen(szMsg2)+1, (BYTE*)szMsg2);
	
	return 0;
}

packet.h

/***************************************************************************
	제목: packet Class
	작성자: 박성완(adsloader@naver.com)
	작성일: 2009.3.3
	목적  : 패킷 전송 및 분석 클래스 
	참고  : Fixed Size
***************************************************************************/

#ifndef __PSW_PACKET__
#define __PSW_PACKET__

#include "stdafx.h"
#include <winsock.h>

class CPacket
{
public:

	// 커맨드 정의 
	// 커맨드 정의 
	enum reqCMD{
		PKT_SHELL  = 0,
		PKT_PYTHON    
	};
	

public:

	#pragma pack(push, 1)
	// Header 정보
 	typedef struct tagPacketHeader{
		CHAR   identify[3];  // 인식자 PSW
		int    length;       // 데이터 크기 
		BYTE   encode;       // 클라이언트 문자열 인코딩 종류(아직 미정)  
		BYTE   req;          // 0:shell, 1:python

		   		
	} PacketData;
	#pragma pack(pop)

public:
	CPacket(SOCKET s);
	virtual ~CPacket();
    
	int Analyze();
	int SetHeader( BYTE req, BYTE ack);
	int SendPacket(int nSize, byte* pData);
    int SetData(byte* pData, int nSize);

	int SendPacket();
    
	BYTE*        GetPacketData();
	PacketData*  GetPacketInfo();

	// Data영역
	BYTE*   pPackData;
	
private:
	SOCKET m_socket;

	PacketData m_pkt;
    
	// nSize읽을때까지 계속 버퍼링 및 블럭한다.
	int RecvBySize(char* pData, int nSize);
};

#endif

 

실행하기 

  1. 우분투에서 ububtu_server.py를 실행한다.
  2. 윈도우에서 클라이언트 소스를 vs2008에서 컴파일하여 실행한다.
  3. 서버에서 클라이언트의 접속을 처리한다.

  1. 클라이언트에서 result = TestFunc('메롱') 을 파이썬 명령어로 요청한다.
  2. 클라이언트에서 ps | grep 'python' && ls -al 을 리눅스 쉘 명령어로 요청한다.

 

Comments