본문 바로가기

IT/Python

[TCP/IP, Ubuntu, Python, C/C++] C서버와 Python 클라이언트 Data casting Issue

최근 TCP/IP Network Programming을 해야할 일이 있어서, 일을 진행하다가 개인적으로 신기했던게 있어서


복기할 겸 글을 포스팅합니다.


1. TCP / IP


일반적으로 TCP/IP는 Byte를 전송하는 Streamer의 역활을 합니다.








따라서 모든 데이터는 Byte Stream으로 전송이 되고, 서버 및 클라이언트 상호간의  상위 데이터 프로토콜 (무엇을 보낼거냐)가 없다면


데이터를 해독할 수 없습니다. 


이는 데이터를 받아서, 어떠한 데이터 캐스팅 없이, 데이터를 출력해보면 알 수 있습니다.


예를들어 C Server에서 Python Client로 어떠한 데이터를 전송하고, 


이를 출력해본다면 특정한 Byte Stream으로 받는다는 것을 확인할 수 있습니다.


1-1. C Server


#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define PORT 8888

char            data[7] = "Hello!";
int             serversock, clientsock;
void            quit(char* msg, int retval);

int main(int argc, char** argv)
{
    struct sockaddr_in      server, client;
    int                     accp_sock;
    int                     addrlen = sizeof(client);
    int                     bytes;
    int                     dataSize = sizeof(data);

    printf("Data Size is : %d\n", dataSize);

    /* open socket */
    if ((serversock = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        quit("socket() failed", 1);
    }

    /* setup server's IP and port */
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = INADDR_ANY;

    /* bind the socket */
    if (bind(serversock, (const void*)&server, sizeof(server)) == -1) {
        quit("bind() failed", 1);
    }

    printf("start listen....\n");
    // wait for connection
    if(listen(serversock, 10) == -1){
        quit("listen() failed.", 1);
    }
    printf("client wait....\n");

    accp_sock = accept(serversock, (struct sockaddr *)&client, &addrlen);
    if(accp_sock < 0){
        quit("accept() failed", 1);
    }

    bytes = send(accp_sock, &data, dataSize, 0);

    if(bytes != dataSize){
        fprintf(stderr, "Connection closed. bytes->[%d], dataSize->[%d]\n",bytes, dataSize);
        close(accp_sock);

        if ((accp_sock = accept(serversock, NULL, NULL)) == -1) {
            quit("accept() failed", 1);
        }
    }

    quit(NULL, 0);
}

void quit(char* msg, int retval)
{
    if (retval == 0) {
        fprintf(stdout, (msg == NULL ? "" : msg));
        fprintf(stdout, "\n");
    } else {
        fprintf(stderr, (msg == NULL ? "" : msg));
        fprintf(stderr, "\n");
    }

    if (clientsock) close(clientsock);
    if (serversock) close(serversock);

    exit(retval);
}


1-2. Python Client


from socket import *

class SocketInfo():
    HOST=""
    PORT=8888
    BUFSIZE=7
    ADDR=(HOST, PORT)

class socketInfo(SocketInfo):
    HOST = "127.0.0.1"

csock = socket(AF_INET, SOCK_STREAM)
csock.connect(socketInfo.ADDR)
print("conenct is success")

commend = csock.recv(socketInfo.BUFSIZE, MSG_WAITALL)
data = commend.decode("UTF-8")

print("type : {}, data len : {}, data : {}, Contents : {}".format(type(commend),len(commend), commend, data))
print()

csock.close()
exit()



1-3. 실행화면


왼쪽부터 C Server, Python client TCP/IP Programming의 결과입니다.

서버에서 데이터를 보내면, 클라이언트에서는 Bytes타입의 데이터를 받는 것을 확인할 수 있습니다.




2. TCP/IP Connection between Python client with C Server


위에서는 C Server에서 Python Client 문자열 데이터를 보냈을 때, 해당 데이터는 Bytes 데이터로 전송되고, 


잘 받았다는 것을 확인할 수 있었습니다.


그렇다면, Python Client에서 C Server로 데이터를 전송하면 어떻게 될까요?


이번에는 Python Client에서 C Server로 데이터를 전송해보도록 하겠습니다.


2-1. C Server


#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define PORT 8888

char            data[7] = "Hello!";
int             serversock, clientsock;
void            quit(char* msg, int retval);

int main(int argc, char** argv)
{
    struct sockaddr_in      server, client;
    int                     accp_sock;
    int                     addrlen = sizeof(client);
    int                     bytes;
    int                     dataSize = sizeof(data);
    int                     from_client;

    printf("Data Size is : %d\n", dataSize);

    /* open socket */
    if ((serversock = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        quit("socket() failed", 1);
    }

    /* setup server's IP and port */
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = INADDR_ANY;

    /* bind the socket */
    if (bind(serversock, (const void*)&server, sizeof(server)) == -1) {
        quit("bind() failed", 1);
    }

    printf("start listen....\n");
    // wait for connection
    if(listen(serversock, 10) == -1){
        quit("listen() failed.", 1);
    }
    printf("client wait....\n");

    accp_sock = accept(serversock, (struct sockaddr *)&client, &addrlen);
    if(accp_sock < 0){
        quit("accept() failed", 1);
    }

    bytes = send(accp_sock, &data, dataSize, 0);

    if(bytes != dataSize){
        fprintf(stderr, "Connection closed. bytes->[%d], dataSize->[%d]\n",bytes, dataSize);
        close(accp_sock);

        if ((accp_sock = accept(serversock, NULL, NULL)) == -1) {
            quit("accept() failed", 1);
        }
    }

    // Data Receive
    // bytes re-init
    printf("Get Data from client\n");
    bytes = 0;
    for(i=0; i< sizeof(from_client); i+= bytes){
        if ((bytes = recv(sock, &from_client + i, sizeof(from_client) - i, 0)) == -1) {
            is_check = 0;
            quit("recv failed", 1);
        }
    }
    printf("Contents : %d\n, from_client");

    quit(NULL, 0);
}

void quit(char* msg, int retval)
{
    if (retval == 0) {
        fprintf(stdout, (msg == NULL ? "" : msg));
        fprintf(stdout, "\n");
    } else {
        fprintf(stderr, (msg == NULL ? "" : msg));
        fprintf(stderr, "\n");
    }

    if (clientsock) close(clientsock);
    if (serversock) close(serversock);

    exit(retval);
}


2-2. Python Client


from socket import *
import numpy as np

class SocketInfo():
    HOST=""
    PORT=8888
    BUFSIZE=7
    ADDR=(HOST, PORT)

class socketInfo(SocketInfo):
    HOST = "127.0.0.1"

csock = socket(AF_INET, SOCK_STREAM)
csock.connect(socketInfo.ADDR)
print("conenct is success")

commend = csock.recv(socketInfo.BUFSIZE, MSG_WAITALL)
data = commend.decode("UTF-8")

print("type : {}, data len : {}, data : {}, Contents : {}".format(type(commend),len(commend), commend, data))
print()

to_server = int(12345)
to_server_bytes = bytes(to_server)
print("Send Data : {}, bytes : {}".format(to_server, to_server_bytes))
sent = csock.send(to_server_bytes)

csock.close()
exit()


2-3. 실행결과


이런 괴상한 결과가 나오게 됩니다.

C에서의 Int형 사이즈는 4이고 Python에서 Int형 데이터를 Bytes로 컨버팅한 결과는 12345가 됩니다.




3. 해결방법


일반적으로 아래 그림과 같이 Python의 integer의 Size는 C언어의 int Size (4)와는 다릅니다.

따라서 이를 해결하려면, 서로 통신하고있는 디바이스의 자료형 데이터가 어떻게 구성되어있는지 확인하고

그에 맞춰서 데이터를 전송해야만, Server나 Client 쪽에서 데이터를 올바르게 얻어올 수 있습니다.





3-1. C Server


#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define PORT 8888
char            data[7] = "Hello!";
int             serversock, clientsock;
void            quit(char* msg, int retval);

int main(int argc, char** argv)
{
    struct sockaddr_in      server, client;
    int                     accp_sock;
    int                     addrlen = sizeof(client);
    int                     bytes;
    int                     dataSize = sizeof(data);
    int                     from_client;
    int                     i;

    printf("Data Size is : %d\n", dataSize);

    /* open socket */
    if ((serversock = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        quit("socket() failed", 1);
    }

    /* setup server's IP and port */
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = INADDR_ANY;

    /* bind the socket */
    if (bind(serversock, (const void*)&server, sizeof(server)) == -1) {
        quit("bind() failed", 1);
    }

    printf("start listen....\n");
    // wait for connection
    if(listen(serversock, 10) == -1){
        quit("listen() failed.", 1);
    }
    printf("client wait....\n");

    accp_sock = accept(serversock, (struct sockaddr *)&client, &addrlen);
    if(accp_sock < 0){
        quit("accept() failed", 1);
    }

    bytes = send(accp_sock, &data, dataSize, 0);

    if(bytes != dataSize){
        fprintf(stderr, "Connection closed. bytes->[%d], dataSize->[%d]\n",bytes, dataSize);
        close(accp_sock);

        if ((accp_sock = accept(serversock, NULL, NULL)) == -1) {
            quit("accept() failed", 1);
        }
    }

    // Data Receive
    // bytes re-init
    printf("Get Data from client\n");
    bytes = 0;
    for(i=0; i< sizeof(from_client); i+= bytes){
        if ((bytes = recv(accp_sock, &from_client + i, sizeof(from_client) - i, 0)) == -1) {
            quit("recv failed", 1);
        }
    }
    printf(" from_client size : %d, Contents : %d\n", sizeof(from_client), from_client);

    quit(NULL, 0);
}

void quit(char* msg, int retval)
{
    if (retval == 0) {
        fprintf(stdout, (msg == NULL ? "" : msg));
        fprintf(stdout, "\n");
    } else {
        fprintf(stderr, (msg == NULL ? "" : msg));
        fprintf(stderr, "\n");
    }

    if (clientsock) close(clientsock);
    if (serversock) close(serversock);

    exit(retval);
}


3-2. Python Client


from socket import *
import numpy as np

class SocketInfo():
    HOST=""
    PORT=8888
    BUFSIZE=7
    ADDR=(HOST, PORT)

class socketInfo(SocketInfo):
    HOST = "127.0.0.1"

csock = socket(AF_INET, SOCK_STREAM)
csock.connect(socketInfo.ADDR)
print("conenct is success")

commend = csock.recv(socketInfo.BUFSIZE, MSG_WAITALL)
data = commend.decode("UTF-8")

print("type : {}, data len : {}, data : {}, Contents : {}".format(type(commend),len(commend), commend, data))
print()

to_server = int(12345)
right_method = to_server.to_bytes(4, byteorder='little')
print("Send Data : {}, bytes len : {}, bytes : {}".format(to_server, len(right_method), right_method))
sent = csock.send(right_method)

csock.close()
exit()


3-3. 실행결과