2013-04-29 3 views
0

저는 C에서 소켓 프로그래밍을 처음 사용하기 때문에 아래의 코드는 초보자 용으로 많은 실수를 범할 수 있습니다. 서버가 UDP 소켓을 사용하여 클라이언트로 파일을 전송할 클라이언트 - 서버 응용 프로그램을 만들려고합니다. 클라이언트와 서버 모두 Linux 호스트에서 실행됩니다. 과제이기 때문에 그렇게해야합니다. 다른 클라이언트 - 서버 통신은 TCP 소켓을 사용할 수 있지만 파일 전송은 UDP를 통해 이루어져야합니다. 이 프로그램은 작은 파일에 대해서는 올바르게 작동하지만, 약간 큰 파일 (예 : 600KB 텍스트 파일)을 보내려고하면 서버가 패킷을 모두 보내더라도 클라이언트는 패킷 수신을 중단합니다. 다음은 서버 코드의 파일 전송 부분입니다.C에서 UDP 소켓을 사용하여 파일 전송하기

FILE* myFile; 
    long fileSize, readBytes, sentBytes, sizeCheck; 
    uint32_t encodedFileSize; 

    myFile = fopen(fileName, "rb"); 
    if(myFile == NULL) 
    { 
     perror("Error when opening file."); 
     exit(1); 
    } 

    fseek(myFile, 0, SEEK_END); 
    fileSize = ftell(myFile); 
    encodedFileSize = htonl(fileSize); 
    rewind(myFile); 
    sizeCheck = 0; 

    write(myTCPSocket, &encodedFileSize, sizeof(encodedFileSize)); 

    if(fileSize > 255) 
    { 
     while(sizeCheck < fileSize) 
     { 
     readBytes = fread(bufferRW, 1, 256, myFile); 
     sentBytes = sendto(sockfdUDP, bufferRW, readBytes, 0, (struct sockaddr*)&cli_addr, udpAddressSize); 
     sizeCheck += sentBytes; 
     } 
    } 
    else 
    { 
     readBytes = fread(bufferRW, 1, 256, myFile); 
     sentBytes = sendto(sockfdUDP, bufferRW, readBytes, 0, (struct sockaddr*)&cli_addr, udpAddressSize); 
    } 

    if(fileSize == sizeCheck) 
    { 
     printf("Success.\n"); 
    } 
    else 
    { 
     printf("Fail.\n"); 
    } 
    fclose(myFile); 
    fflush(stdout); 
    close(sockfdUDP); 

위에서 알 수 있듯이 TCP 소켓을 사용하여 클라이언트에게 파일 크기를 보냅니다. 여기서 클라이언트 코드는 :

FILE *myFile; 
    long receivedBytes, writtenBytes, sizeCheck; 
    long fileSize, realFileSize; 
    char ack2[5] = "Ok"; 
    sockfdUDP = socket(AF_INET, SOCK_DGRAM, 0); 

    read(socketTCP, &fileSize, sizeof(long)); 

    realFileSize = ntohl(fileSize); 

    myFile = fopen(fileName, "wb"); 

    if(myFile == NULL) 
    { 
    perror("Error when creating file."); 
    exit(1); 
    } 

    sizeCheck = 0; 

    if((realFileSize) > 255) 
    { 
     while(sizeCheck < (realFileSize)) 
     { 
     receivedBytes = recvfrom(sockfdUDP, bufferRW, 256, 0, (struct sockaddr*)&serv_addr, &serv_addr_size); 
     writtenBytes = fwrite(bufferRW, 1, receivedBytes, myFile); 
     fflush(myFile); 
     sizeCheck += writtenBytes; 
     } 
    } 
    else 
    { 
     receivedBytes = recvfrom(sockfdUDP, bufferRW, 256, 0, (struct sockaddr*)&serv_addr, &serv_addr_size); 
     fwrite(bufferRW, 1, receivedBytes, myFile); 
     fflush(myFile); 
    } 

    if(realFileSize == sizeCheck) 
    { 
    printf("Success."); 
    } 
    else 
    { 
     printf("Fail."); 
    } 
    fclose(myFile); 
    close(sockfdUDP); 

은 "bufferRW"버퍼

원래 선언 CHAR bufferRW [256], 인수로서 함수에 전달있다. 선언되지 않은 다른 변수에 대해서도 마찬가지입니다. 전에 말했듯이, 서버는 아무런 문제없이 (분명히) 전체 파일을 보낼 것입니다. 그러나 클라이언트는 약 423936 바이트 (실행마다 달라질 수 있음)를 작성한 후에 패킷 수신을 중지합니다. 그냥 아무것도 읽지 않고 recvfrom 줄에 머무를거야.

이제 동일한 호스트에서 두 프로세스를 테스트 중이므로 잘못된 연결로 인해 문제가 발생하지 않았 음을 확신합니다. 그리고 "256 바이트 패킷 크기는 무엇입니까?"라는 질문을하기 전에 1500이라는 버퍼 크기를 사용하면 realFileSize = ntohl(fileSize); 클라이언트 라인에 세그멘테이션 오류가 발생합니다.

너 내가 뭘 놓치고 있는지 말해 줄래?

편집 : 이제 다른 파일 크기로 시도하고 있습니다. 문제없이 256 바이트보다 큰 파일을 처리하는 것 같습니다 (클라이언트와 서버 모두에서 while 루프가 올바르게 시작되고 종료됩니다). 그러나 클라이언트는 파일이 300kb보다 클 때 문제가 발생하기 시작합니다.

편집 2 : 방금 프로그램을 디버깅했습니다. 분명히, 서버는 클라이언트가 while 루프를 입력하기 전에 전체 파일을 보냅니다.

편집 3 : 문제의 원인을 알고 있다고 생각합니다. 클라이언트가 읽기를 시작하기 전에 서버가 많은 패킷을 보내는 것처럼 클라이언트는 크기에 관계없이 최대 278 개의 패킷을 읽습니다. 클라이언트가 읽기를 시작하기 전에 279를 전송하려고하면 279 번째 패킷을 읽지 않습니다. 따라서 서버가 패킷을 충분히 빨리 보내면 클라이언트가 아직 읽지 않은 패킷 수가 278을 초과하고 클라이언트가 모든 패킷을 읽지 못하게됩니다. 이 문제를 해결하는 방법에 대한 아이디어가 있습니까?

+2

당신이 UDP를 사용한다면, 당신은 현재하지 않고있는 패킷 방울과 재정렬을 처리해야한다는 것을 알기를 바랍니다. – icktoofay

답변

1
  1. 길이가 긴 포인터를 선언했지만 코드에서 아무 것도 가리키지 않습니다. 사실, 그것은 임의의 주소를 가리키고 있습니다. long fileSize으로 신고하고 대신 read(socketTCP, &fileSize, sizeof(long))으로 전화하십시오.
  2. read, write 등의 반환 값이 실패하지 않았는지 확인해야합니다. 예를 들어, sendto은 오류시 -1을 반환합니다. 이것을 무시하고 sizeCheck을이 값으로 증가시킵니다.
  3. UDP는 파일 전송을위한 신뢰할 수있는 프로토콜은 아니지만이를 사용하지 않으면 UDP가 패킷 순서 변경, 데이터 체크섬 등과 같이 무료로 제공하는 일부 컨트롤을 구현하는 것이 좋습니다. 작업 자체.
  4. 코드를 -Wall -Wextra으로 컴파일하십시오.컴파일러는 잠재적으로 잘못 될 수있는 것에 대한 힌트를 제공합니다. 당신이 아직도 *fileSize을 사용하고있는 것을 볼 수 있습니다. 이것은 분명히 잘못되었습니다.
  5. *fileSize 문제를 해결 한 후에도 루프 조건에 여전히 잘못된 값 (fileSize = ntohl(fileSize))이 사용됩니다. 이 값을 다른 변수에 저장하거나 실제 파일 크기를 사용하도록 루프 조건을 변경해야합니다. 당신의 EDIT 3에 관한

, 당신은 어떻게 든 클라이언트 & 서버를 동기화, 그래서 그들은 동시에 전송을 시작해야합니다. 그러나 수신기보다 훨씬 빠른 보낸 사람은 여전히 ​​패킷 손실을 일으 킵니다. 이 문제를 해결하려면 패킷 확인을 구현하고, 보낸 사람이 시간 초과 후 각 보낸 패킷에 대한 ACK를받지 못하면 패킷을 다시 전송해야합니다. 이것은 TCP가 이미 당신을 위해하는 것입니다. 간단하지만 (완전히 신뢰할 수는 없지만) 송신 프로세스를 조금 느리게 할 수 있습니다. sendto을 호출 할 때마다 nanosleep을 사용하는 것이 좋습니다.

+0

1. 완료. 그래도 작동이 안되는. 2. 완료, fread와 fwrite 모두 잘 작동하고있는 것 같습니다. Sendto는 오류를 발생시키지 않습니다. 3. 흥미롭게도, 작은 파일 (256 바이트보다 큰 파일)로 프로그램을 테스트했을 때, 그 일을하지 않고도 정상적으로 작동했습니다. 나는 클라이언트가 모든 패킷을 얻지 못하고 있다는 것을 보면서 여기서 문제가 있다고 생각하지 않는다. 하지만 얻은 것은 완벽하게 주문됩니다. – Phillipe

+0

그것에 집계하지 마십시오 ... – japreiss

+0

LAN에서는 적절한 순서로 UDP 데이터 그램을 잃지 않을 것입니다. 클라우드를 거쳐야하는 경우 질서와 신뢰할 수없는 전달에 대해 걱정할 필요가 있습니다. – Fred

관련 문제