리눅스에서 c로 udp 파일전송 프로그램 만드는데 질문입니다!

shin7의 이미지

c언어를 이용하여 리눅스상에서 파일전송 프로그램을 만드려합니다
tcp로는 간단하게 만들고 테스트 해봤는데
아주 잘되네요.
문제는 udp인데..
udp를 이용하여 클라이언트에서 파일을 보내고 서버에서 파일을 받게끔 구현하였습니다
최대한 간단하게 구현하였는데
문제는

파일이 끝까지 전송이 안된다는 점입니다.
서버에서 대기하고 있다가
클라이언트가 접속해들어오고 파일을 전송하는 과정을 행하는데
전송을 하다가 중간에 그냥 멈춰버리네요. 클라이언트는 계속 무한대기상태이구요.. 한마디로 끝이 안난다는겁니다..
파일용량이 총4mb인 파일을 전송해서 테스트 해봣는데
언제는 2mb, 언제는 3.5mb 머 이런식으로 중간까지밖에 전송이 안됩니다..ㅠ

udp라는게 파일이 전송이 될수도 있고 안될수도 있다고 듣긴 했는데
제 경우는 파일이 전송되는 중간에 그냥 멈춰버리고 프로그램이 끝나지가 않네요.. 이문제..어떻게 해결해야 할까요?

이게 첫번째 문제고
두번째 문제로는!
파일전송이 완료되지 않고 프로그램이 계속 대기중상태로 있어 클라이언트프로그램을 ctrl+c를 눌러 강제종료를 한 후
다시 클라이언트를 실행시키면 기존에 다운받은 파일을 첨부터 받지 않고 전에 받은 파일의 용량부터 받습니다..
간단하게
서버실행 -> 클라이언트실행
파일전송 -> 3.5mb까지 전송됨(그후프로그램종료되지 않고계속대기상태)
ctrl+c를 눌러 강제종료
다시 클라이언트실행 -> 3.5mb + 다시받은파일용량 (즉 이번에 2mb를 받았다면 파일용량이 총 5.5mb가 되버립니다.. 원래파일은 4mb짜리 파일인데)

계속 파일을 지우지 않고 클라이언트를 실행시키면 이렇게 덮어씌워집니다
이상하네요...
tcp로 할때는 다운받을 파일이 존재하면 파일을 지우고 처음부터 다운받게 되는데
udp로 하면 그냥 기존파일에서 플러스한상태로 덮어씌워버립니다..

이 문제 꼭좀 해결해주세요..^^

제 소스가 문제일까요..? ㅎ
고수님들 제 소스 보시면서 문제점좀 꼭 갈켜주시면 감사하겠습니다!!

파일첨부가 안되어 글밑에 소스를 쓰겠습니다.. (tcp소스는 pthread를 이용하여 구현하였는데 udp로 변화시키다보니 몇몇 쓸모없는 변수도 있습니다.)
------------------------------------------
udp서버.c

#include
#include
#include
#include
#include
#include
#include
#include
#include

#define BUFSIZE 100

//void *clnt_connection(void *arg);
//void send_message(char* message,int len);
void error_handling(char *message);

int clnt_number=0;
//int clnt_socks[10];
//pthread_mutex_t mutx;

int main(int argc,char **argv)
{
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in from_addr;
int from_addr_size;
int serv_addr_size;
pthread_t thread;
FILE *fpw;
char buf[BUFSIZE+1];
int len;
int se;
int str_len;
char message[BUFSIZE];
if(argc!=2)
{
printf("Usage : %s \n",argv[0]);
exit(1);
}

//if(pthread_mutex_init(&mutx,NULL))
//error_handling("mutex init error");

serv_sock=socket(PF_INET,SOCK_DGRAM,0);
if(serv_sock == -1)
error_handling("udp socket error");

memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));

if(bind(serv_sock,(struct sockaddr*) &serv_addr,sizeof(serv_addr))==-1)
error_handling("bind() error");

fpw=fopen("TestFile","rb");

while(1)
{
//printf("-----------------\n");
from_addr_size=sizeof(from_addr);

while((recvfrom(sock,buf,MAXLINE,0,(struct sockaddr*)&from_addr,&from_addr_size) > 0)
{
fwrite(buf,sizeof(char),nbyte,fpw);
}

}
close(clnt_sock);
fclose(fp);
return 0;

}

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

--------------------------------------------------------------------------------------

udp클라이언트
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFSIZE 100
#define MAXLINE 61440

void error_handling(char *message);
char name[NAMESIZE]="[Default]";
char buf[MAXLINE+1];
char message[BUFSIZE]="asdf";
clock_t t1,t2;
FILE *fpr;

int main(int argc,char **argv)
{
int sock;
int nbyte;
struct sockaddr_in serv_addr;
struct sockaddr_in from_addr;
int addr_size;

if(argc!=3)
{
printf("Usage : %s \n",argv[0]);
exit(1);
}

sprintf(name,"[%s]",argv[2]);

sock=socket(PF_INET,SOCK_DGRAM,0);


memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));

if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
error_handling("connect() error");

fpr = fopen("TestFile","wb");

while(1)
{
addr_size=sizeof(from_addr);

while(!feof(fp))
{
len=fread(buf,sizeof(char),100,fpr);
write(clnt_sock,buf,len); //Data Send
//sendto(serv_sock,buf,len,0,(struct sockaddr*)&clnt_addr,sizeof(clnt_addr));
}

}

fclose(fpw);
close(sock);
return 0;

}

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

라키시스의 이미지

Quote:

파일용량이 총4mb인 파일을 전송해서 테스트 해봣는데
언제는 2mb, 언제는 3.5mb 머 이런식으로 중간까지밖에 전송이 안됩니다..ㅠ

unreliable 한 UDP 를 사용해서 전체 파일의 전송을 보장하시려면
나름대로 reliablity 를 보장해 주기 위한 새로운 프로토콜(?)을 UDP 위에 얹어서
그것을 이용해서 전송하셔야 합니다.

예를 들면, 각 datagram 에 sequence number 를 부여해서
받는 쪽에서 수신하지 못한 datagram 의 sequence number 를 보내는 쪽에 알려주어서 재전송을 유도한다든지
하는 방식으로요.
(TCP 가 이 작업을 해 줍니다)

shin7의 이미지

답변 감사드립니다
근데 잘 이해가..ㅠㅠ 지송함다 초보라..ㅎ
datagram 에 sequence number 을 부여하는 방법을 알 수 있을까요..? 참고자료라도 혹시 있을까요..?
그리고 datagram은 파일을 얘기하는건가용..?

앗! 참 그리구 저의프로그램에서 발생하는 문제점은 udp에 어쩔 수 없이 발생하는 문제인건지요..?
저의 소스상에 문제점은 없는건가요?

지송함다 한번만 더 자세하게 설명 부탁드릴게요^^;;

안녕하세요

익명 사용자의 이미지

유명한 스티븐스님의 Unix Network Programming 책을 보면, UDP에 신뢰성을 부여하는 방법을 구현한 부분이 있습니다. 방법이야, 보내고 타임아웃 걸고, 받았다는 확인응답메시지등을 조합한 방법의 구현이지요.
라이브러리 스타일로 구현되어 있으니, 해당 라이브러리를 구해다가 사용하시기 바랍니다.
그 라이브러리 자체에 대한 공부도 하시면 보다 좋겠군요.

키워드: UDP , 신뢰성, Reliability, reliable 정도가 되겠군요.

shin7의 이미지

신뢰성있는 udp구현에 대해 공부해야겠군요
마지막으로 아직 정확치가 않아서 그러는데

제가 만든프로그램에서의 문제점(파일이 다 안보내지고 종료가안된다는점, 받으려는파일이존재해도기존파일에서덮어씌운상태로다운받아지는점)
이문제점은
제 소스의 문제라기 보단

udp프로그램에서 나타나는 특징인지요..?
이걸 꼭 알고싶습니다
답변 부탁드릴게용^~^

안녕하세요

익명 사용자의 이미지

1) fopen()에서 "rb", "wb"가 클라이언트 서버에서 반대로 사용된것으로 보입니다.
2) 클라이언트는 파일을 전부 읽고 송신하고 종료했으나,(중간에서 데이터 손실: UDP의 특성), 수신측의 파일크기를 보면 잃어버린 만큼이 기록되지 않으므로, 다 안보내고 종료했다고 생각하는 듯 보입니다. 또한 UDP의 경우 버퍼가 1개이며, 연속적으로(무한루프스타일) 송신을 빠른 속도로 하는 경우, 데이터는 송신측의 운영체제 수준에서 대부분 지워버립니다.(네트워크 라인이 아니라..) 따라서, 원시적인 형태의 프로그램에서는(원형UDP상에 구현), 질문자님의 아래 코드부분을

len=fread(buf,sizeof(char),100,fpr);
write(clnt_sock,buf,len); //Data Send
//sendto(serv_sock,buf,len,0,(struct sockaddr*)&clnt_addr,sizeof(clnt_addr));

아래와 같은 형태로 바꾸면 전송 속도는 다소 늦어지나, 수신율은 훨씬(? 어쩌면 100%)로 좋아지리라고 예측됩니다.
len=fread(buf,sizeof(char),100,fpr);
write(clnt_sock,buf,len); //Data Send
// ****잠시쉬기 *****
//sendto(serv_sock,buf,len,0,(struct sockaddr*)&clnt_addr,sizeof(clnt_addr));

위의 잠시쉬기 부분에,
sleep(), usleep(), nanosleep(), select()를 통한 sleep()등을 구현하여 쉬어가게 만들면 된다는 얘기지요.
시스템의 usleep()이 안정적이라면 단순하게,
usleep(10000); // 10ms
이정도만 해도 좋아 질듯 보입니다. 별 소용이 없다면, 수십에서 수백 밀리초로 올려보셔도 좋겠습니다.
<주의>그러나, 프로그램의 동기(synchronization)을 sleep()등으로 구현하면 하드웨어별 운영체제별, 시스템의 런타임 비해비어별로, 결과가 전혀 예측이 안되므로, 실험실 외에서는 절대 사용하면 안되는 코드입니다. 절대로 말입니다!!! 아울러, sleep()등이 남발된 소스는 별로 훌륭한 소스가 아닙니다. 학습용(가정이라던지, 현재 관심사가 다른 것이므로 ...등 서두가 있는 경우에 한하여 ...)으로 잠시~ 라면 모를까요.

3) 기존 파일에 덮어쓴다는 것은, 프로그래머의 책임입니다. 파일이 존재하는 경우 새 파일을 열고 저장하도록 한다던지, 기존 파일을 지우고 새로 만들어 쓴다던지, 이어서 쓴다던지 등을 프로그래머가 책임을 지고 구현해야 합니다. 이러한 구현이 질문자님의 코드상에 없습니다. 아울러 기존파일의 존재유무 및 파일 크기등을 알아보는 시스템 호출이 별도로 존재하니 검색해 보시기 바랍니다.
naddolki의 이미지

..

설연희 입니다 ^ ㅡ^ㅋ

blee의 이미지

소스를 대충 훓어 보니, 받는 쪽에서 받은 사이즈 만큼 파일에 쓰질 않는 것 같네요.
그리고, 파일이던 소켓이던 읽거나 쓸때, 지정된 사이즈를 다 읽었는가? 썼는가? 리턴값으로 체크를 해야 합니다.. 보통 while 문으로 실제로 사이즈와 함수가 리턴한 사이즈를 비교해서 같을 때까지 돕니다. 일반적으로 모든 read/write,recv,send 다 그렇습니다. man 를 보시면, 지정된 사이즈 만큼 꼭 다 수행되지 않는다라고 나와 있습니다. 이는 간단한 프로그램일 때는 증상이 안 나타날지 모르지만, 서버 프로그램 같이 많은 일을 하면, 찾지 못하는 버그를 만들어 버립니다. 그리고, tcp 와는 다르게 udp 는 보내는쪽에서 쭉쭉.. 보낼수 있을 만큼 다 보내 버리면, 받는 쪽에선 그것을 다 받는다는 보장이 없습니다. 이는 100% 실패입니다. tcp는 신뢰성이 있다는 것을 아실겁니다. 보내는쪽에서 send 했을때, 받는쪽에서 수신버프가 다 차면, 보내는쪽에서 send 가 블락이 됩니다. 동기가 이루어 집니다. udp는 그렇지 않습니다. 받을 준비가 되어 있던 없던, 보내는 쪽에선 무조건 성공입니다. 이를 가장 간단하게 해결하는 것은 보내는 쪽에서 보낸 후에 일정 시간 sleep 를 주는 겁니다. 보통 udp 는 전송량을 체크하는 루틴이 들어 가곤 합니다. 초당 얼마를 보낼까를 정해서 말이죠. 예를 들면 초당 1Mbytes 를 보낸다고 하면, 프로그래머가 그렇게 구현을 해 줘야 합니다. udp에서 패킷 손실은 다 받는 쪽에서 수신버프가 다 차서 버리는 것이라고 보시면 맞을 것입니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.