ARM과 x86간의 소켓을 이용한 파일 전송

htjung07의 이미지

arm 보드와 x86 pc간에 jpg파일을 전송해보고 있습니다.

signed char 형태로 4k 바이트의 공간을 malloc 함수를 이용해 만들고
여기에 파일 끝을 구분하기 위해 1바이트 공간을 헤더로 추가하여
총 4k + 1 바이트를 전송하는 방식을 만들었습니다.

각각 윈도우와 리눅스를 쓰는 x86 pc간에는 제대로 전송이 되었는데...

암 보드에서 윈도우로는 헤더에 넣은 표시가 정상적으로 전송되지 않는 현상을 보였습니다.
처음에는 arm보드와 x86간 char형을 arm은 기본적으로 unsigned 로 인식한다는 사실을 찾아서
arm쪽에 변수를 signed로 바꿔주었는데 조금 나아진듯 했으나 그래도 많은 패킷들이 비정상적인 헤더를 갖더군요
아마도 프로세서간 무엇인가 차이가 있는 것 같은데, 사정상 찾아서 비교해볼 여유가 없어 이렇게 질문을 올립니다.

아래는 제가 짠 서버와 클라이언트 프로그램 소스입니다. 서버에서는 파일을 받고 클라이언트에서는 파일을 보내게 됩니다.

관심가져 주셔서 감사합니다~.

######################서버(윈도우 x86)#################################

#include
#include
#include
#include

#define TRUE 1
#define FALSE 0

#define BUFSIZE 30
#define TRANSSIZE 1024*4

char *port = "9190";

//DEBUG용 함수
void ErrorHandling(char *message);

//###파일 전송 관련 함수###
//파일 전송 프로토콜
signed char FT_eof = 'x'; //파일끝
signed char FT_mid = 'm'; //파일중간

int recvFile(SOCKET soc);

int main()
{

WSADATA wsaData;
SOCKET hListenSock;
SOCKET hAcceptSock;

SOCKADDR_IN listenAddr;
SOCKADDR_IN acceptAddr;
int acceptAddrSize;

if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHandling("WSAStartup() error!");

hListenSock = socket(PF_INET, SOCK_STREAM, 0);
if(hListenSock == INVALID_SOCKET)
ErrorHandling("WSAStartup() error!");

memset(&listenAddr, 0, sizeof(listenAddr));
listenAddr.sin_family = AF_INET;
listenAddr.sin_addr.s_addr = htonl(INADDR_ANY);
listenAddr.sin_port = htons(atoi(port));

if( bind(hListenSock, (SOCKADDR*) &listenAddr, sizeof(listenAddr)) == SOCKET_ERROR )
ErrorHandling("bind() error");

if( listen(hListenSock, 5) == SOCKET_ERROR )
ErrorHandling("listen() error" );

acceptAddrSize = sizeof(acceptAddr);
hAcceptSock = accept(hListenSock, (SOCKADDR*)&acceptAddr, &acceptAddrSize);
if( hAcceptSock == INVALID_SOCKET)
ErrorHandling("accept() error");

recvFile(hAcceptSock); //사진을 전송 받는다.

closesocket(hAcceptSock);
WSACleanup();

return 0;
}

void ErrorHandling(char *message)
{
fputs(message, stderr);
fputc('\n',stderr);
exit(1);
}

int recvFile(SOCKET soc)
{
char *recvBuf;
struct timeval timeout;
int test =0;
FILE *fp;
fd_set o_recv_set, t_recv_set;

recvBuf = (char*)malloc(sizeof(char)*TRANSSIZE+4);

fp = fopen("sample.jpg", "wb" );
if( fp == NULL ) ErrorHandling("file open error");

FD_ZERO(&o_recv_set);
FD_SET(soc, &o_recv_set);

while( test < 50 )
{
t_recv_set = o_recv_set;
timeout.tv_sec = 0;
timeout.tv_usec = 1000;

if( select(0, &t_recv_set, 0, 0, &timeout) == SOCKET_ERROR )
ErrorHandling("select error");

if( FD_ISSET(soc, &t_recv_set) )
{
recv(soc,recvBuf, TRANSSIZE+1, 0); //4k + 1 바이트를 소켓으로부터 받음
printf("\n%d",recvBuf[0]);
if( recvBuf[0] == FT_eof ) { //헤더의 1바이트에 정보가 파일 끝인이 검사함

fwrite(&recvBuf[1], sizeof(char), TRANSSIZE, fp);
printf("\nrecieving complete");
break;
}
else if( recvBuf[0] == FT_mid) {
fwrite(&recvBuf[1], sizeof(char), TRANSSIZE, fp);
}
}

}


free(recvBuf);
fclose(fp);

return TRUE;
}

###########################클라이언트(arm 커널 2.6, 크로스컴파일러 3.4.3)########################
//************서버&클라이언트 프로그램************
#include
#include
#include
#include
#include
#include
#include
#include

#define BUFSIZE 30
#define TRANSSIZE 4*1024 //전송 데이터 크기

#define TRUE 1
#define FALSE 0

char *target_addr = "118.221.57.145";
char *target_port = "9190";
char *host_port = "9200";

//디버그용 함수
void error_handling(char *message);

//###파일 전송 부###
//파일 전송 프로토콜
char FT_eof = 'x';
char FT_mid = 'm';

int sendFile(int soc, char* file_name);

int main()
{
//서버와 클라이언트 프로그램의 두 기능 모두 사용하게 되므로 필요한 모든 변수를 만듦

int serv_listen_sock, serv_accept_sock; //서버에서 필요한 소켓
int clnt_connect_sock; //클라이언트에서 필요한 소켓

struct sockaddr_in serv_listen_addr; //서버 대기용 소켓 주소 패밀리
struct sockaddr_in serv_accept_addr; //서버 실제 통신용 소켓 주소 패밀리
struct sockaddr_in clnt_connect_addr; //클라리언트 실제 통신용 소켓 주소 패밀리

int serv_accept_addr_size; //bind에 들어가는 대기용 소켓을 제외한 나머지의 주소 패밀리 크기
int clnt_connect_addr_size;

/*###########################################################################################
클라이언트 연결 구현 부
###########################################################################################*/

clnt_connect_sock = socket(PF_INET, SOCK_STREAM, 0);
if( clnt_connect_sock == -1 ) error_handling("socket() error");

memset( &clnt_connect_addr, 0, sizeof(clnt_connect_addr) );
clnt_connect_addr.sin_family = AF_INET;
clnt_connect_addr.sin_addr.s_addr = inet_addr(target_addr);
clnt_connect_addr.sin_port = htons(atoi(target_port));

if( connect(clnt_connect_sock,(struct sockaddr*)&clnt_connect_addr, sizeof(clnt_connect_addr)) == -1 )
error_handling("connect() error!");

sendFile(clnt_connect_sock, "sample.jpg");
// write(clnt_connect_sock, buf, BUFSIZE);

close(clnt_connect_sock);

return 0;
}

void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}

int sendFile(int soc, char *file_name)
{
signed char *sendBuf;
int fd_max,fd_ct;
int result;
struct timeval timeout;

FILE *fp;
fd_set o_send_set, t_send_set;

sendBuf = (signed char*)malloc(sizeof(signed char)*TRANSSIZE+4);

fp = fopen( file_name, "rb" );
if( fp == NULL ) error_handling("file open error");

FD_ZERO(&o_send_set);

FD_SET(soc, &o_send_set);
fd_max = soc;

while(1)
{
t_send_set = o_send_set;
timeout.tv_sec = 0;
timeout.tv_usec = 1000;

if( select(fd_max+1, 0, &t_send_set, 0, &timeout) == -1 )
error_handling("select error");

if( FD_ISSET(soc, &t_send_set) ) {

fread(&sendBuf[1], sizeof(signed char),TRANSSIZE, fp);// 헤더 다음 칸부터 파일로부터 4k읽음

if( feof(fp) ) {
sendBuf[0] = FT_eof;
write(soc, sendBuf, TRANSSIZE+1);
printf("\nsending complete");
break;
}
sendBuf[0] = FT_mid;
write(soc, sendBuf, TRANSSIZE+1); // 4k + 1 을 전송
}
}

free(sendBuf);
fclose(fp);
return 0;
}

ironiris의 이미지

pc 측에 ftp 서버를 운영하고
arm 측에서 wput 같은 프로그램을 쓰는게 속편하지 않을까요??

htjung07의 이미지

메모리에 있는 상태 그대로 처리를해서 결과만 다시 리턴하는 기능을 생각하고 있습니다.

ftp를 사용해보려는 생각도 하긴했었는데

아무래도 저장하고 읽어오는 시간이 좀 걸릴 것 같아서요.

조언 감사합니다^^

skysign의 이미지

ftp 보다 간단한 tftp 도 사용하실 수 있을 것 같습니다.

newmania의 이미지

recv 실행 뒤에 4k + 1 을 모두 받았다는 것이 보장되나요?

drinkme의 이미지

원글님의 code가 그다지 안정적이지 못해 보이네요.

send(), recv() 의 return값을 확인해서
실제 보낸 양, 받은 양에 대해서 처리를 해 주어야 합니다.

htjung07의 이미지

받은 값을 확인해 봤는데
소켓에서 읽는 타이밍이 빨라서 그런지
4바이트 단위로 불규칙하게 들어오는 것을 확인했습니다....-_-;

기본적으로 고려해야 할 사항인듯 한데,
소켓 프로그래밍에 거의 초짜다보니..ㅜㅜ
그래도 사소하지만 좋은 경험이 된것 같네요ㅎㅎ

따로 버퍼를 만들어서 데이터가 충분히 채워진 후
읽어오게 해야겠네요..

감사합니다^^

htjung07의 이미지

ㅜㅜ..

감사합니다^^

chadr의 이미지

아무래도 엔디언 문제같군요.

소켓으로 전송하실때 엔디언을 고려해서 전송을 해주시거나..
아니면 arm 프로세서에 엔디언 방식을 x86과 같이 리틀엔디언으로 세팅해주시면 될것 같습니다.
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

bushi의 이미지

구체적으로 어느 부분에서 endian 문제가 발생한다는건가요 ?
signed char 에서요 ?

OTL

chadr의 이미지

파일에서 1바이트씩 읽어오는지 모르고 답변을 달았네요. :)
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

htjung07의 이미지

태스트로 소수 데이터를 보내보니

순서가 정확하게 들어오더군요 -_-;;

ARM은 빅 엔디언이라고 들었었는데...
소켓이 알아서 처리를 해준 것인지@_@;;

프로그램 완성 여부를 떠나서 알고싶은 사항이네요...

조언 감사합니다^^

정태영의 이미지

ARM 은 빅 엔디안이 아니라 선택이 가능한 것으로 알고 있으며, 대게 네트워크로 데이타를 보낼 때는 빅엔디안으로 변환을 해서 보내게 됩니다. (ntol , lton 등의 함수를 이용...)

--
오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

htjung07의 이미지

리틀 엔디안으로 설정 되있는 거군요...

bushi의 이미지

ARM 은 be/le 겸용입니다.

소켓(커널드라이버 혹은 C lib)이 자동으로 처리해 주지 않습니다.
ntoh 계열과 hton 계열의 함수들이 뭣 때문에 존재하겠습니까.

arm gcc 가 char 를 unsigned char 로 다루는 것은...
소스코드 수준에서 신경쓰기보단 -fsigned-char 컴파일러 옵션을 사용하기를 권해드립니다.
정신건강에도 좋고, 무엇보다도 손가락건강에 좋습니다.

방금확인해봤는데... 이 문제가 gcc4.x 에서도 여전하더군요.
솔직히... 3.x 에 들어서면서부턴 신경끄고 살았는데... 어이구 뒤통수야.

[bushi@rose net]$ 
[bushi@rose net]$ cat char.c
#include <stdio.h>
int main()
{
        char a = -1;
        printf("%d\n", a);
}
[bushi@rose net]$ 
[bushi@rose net]$ /opt/arm-2008q3/bin/arm-none-linux-gnueabi-gcc -v 2>&1|grep "gcc version"
gcc version 4.3.2 (Sourcery G++ Lite 2008q3-41) 
[bushi@rose net]$ 
[bushi@rose net]$ 
[bushi@rose net]$ /opt/arm-2008q3/bin/arm-none-linux-gnueabi-gcc -s -o char char.c
[bushi@rose net]$ /opt/arm-2008q3/bin/arm-none-linux-gnueabi-gcc -s -o char2 char.c -fsigned-char
[bushi@rose net]$ 
[bushi@rose net]$ sudo ./char
255
[bushi@rose net]$ sudo ./char2
-1
[bushi@rose net]$ 

fedora8 에서 qemu-arm-eabi 로 테스트했습니다.

[bushi@rose net]$ cat /proc/sys/fs/binfmt_misc/qemu-arm-eabi 
enabled
interpreter /usr/bin/qemu-arm-eabi
flags: 
offset 0
magic 7f454c4601010100000000000000000002002800
mask fffffffffffffffffffffffffffffffff0ffffff
[bushi@rose net]$

OTL

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.