반응형

전문 구성 하기 II. line 단위 전문

전문 구성에서 가장 simple한 형태가 new line으로 전문을 구성하는 방법입니다. 많은 표준 protocol에서 new line 단위의 전문 구성이 있습니다. telnet, ftp, http 등은 new line으로 통신 format을 구성하고 있습니다. 특히, ftp, telnet과 같이 대화형 protocol에 대해서는 new line은 좋은 전문 구성이 될 수 있습니다.

 

ftp는 한 라인으로 명령어를 보내면, 응답으로 3자리의 숫자와 처리결과 문구가 return되는 형식으로 전문을 구성되어 있습니다.

telnet은 명령어를 new line 단위로 실행하면 그 결과를 line단위로 받습니다.

http는 header부를 new line으로 여러개 구분하고 header의 끝은 빈 데이터로 new line이 오면 header부가 끝났음을 의미합니다.

그리고 많은 chatting 프로그램들이 new line 단위로 데이터를 송수신하도록 구성하기도 합니다.

 

그러면 socket에서 

new line 단위로 데이터를 주고 받으려면 어떻게 구성하는 것이 좋을까요?

recv(2)함수로 1바이트씩 읽으면서, \n 문자가 들어오는 지 읽는 것은 너무 성가신 일입니다.

 

우리가 file I/O를 할 때에 라인단위로 쓰고 읽기를 위해서 어떤 방식을 많이 쓰나요?

바로 fprintf( )와 fgets( )로 line 단위의 출력과 입력으로 많이 사용합니다.

 

그러면 socket에서는 이 방식을 사용할 수 없을까요? 됩니다.

UNIX/LINUX에서는 socket도 파일로 취급합니다. 따라서, stream file I/O를 할 수 있습니다.

누구나 쉽게 사용하는 Stream File I/O로 통신 프로그램을 짤 수 있습니다.

 

Stream File I/O를 하려면 FILE * 를 생성해야 합니다. 이를 위한 함수가 바로 fdopen(3)함수입니다.

즉, file descriptor나 socket descriptor를 이용하여 FILE *를 생성하는 함수가 바로 fdopen(3)함수입니다.

see alsofdopen(3) - file descriptor를 stream으로

 

Sample). connect 또는 accept를 통해서 얻은 socket descriptor로  FILE *생성

FILE *fp;
int   sock;

......

    if((fp = fopen(sock, "r+")) == NULL) {
        // 오류처리
        return -1;
    }

 

위와 같이 하면 파일이 FILE *가 생성되며, 이를 통해서 stream 함수들인 fprintf(fp, ...), fgets(...fp) 등 stream file I/O의 모든 함수들을 사용할 수 있습니다.

그런데, 이렇게만 하면 new line만으로 데이터 전송이 안됩니다.

 

Sample). new line 단위로 데이터를 보내고 fflush(3)를 해줌.

     fprintf(fp, "%s\n", data);
     fflush(fp);

위와 같이 일일이 new line으로 출력하고 나면 fflush(3)해서 buffer에 있는 데이터는 전송한다면, 상당히 귀찮습니다.

이를 위하여 stream의 buffering 방식을 변경해주어야 합니다.

stream의 buffering 방식을 변경해주는 함수가 setvbuf(3)함수입니다.

setvbuf(3)함수의 상세한 설명은 see also : setvbuf(3) - stream buffering 방식을 지정함 를 참고하시기 바랍니다.

 

라인단위의 buffering으로 변경하면, newline이 출력되거나 입력되면 실제로 socket으로 데이터 전송, 송신을 끝냅니다.

 

Sample). 라인단위의 buffering으로 변경하기

   if((fp = fdopen(sock, "r+")) != NULL) {
        return 1;
    }
    setvbuf(fp, NULL, _IOLBF, 0); // 라인단위 buffering으로 변환하기

이를 이용하여 connect후 client 프로그램에서, accept후 server 프로그램에서 Stream으로 전환하여 사용하면 좋습니다.

참고로 stream I/O로 전환된 이후에는 모두 stream 함수를 사용하는 것이 좋습니다.

 

만약 라인단위 buffering으로 변경하였다고 하더라도 가끔은 라인단위가 아닌 데이터를 대량으로 보는 경우가 있을 수 있습니다. http의 경우 바디에 대해서는 new line 단위가 아닌 대량의 바이너리 데이터도 전송을 합니다.

이 경우에는 fwrite(3)를 통해서 데이터를 보내고 fflush(3)를 한번 호출해주면 됩니다. 즉, new line을  만나지 않았더라도 전송을 꼭 해야하는 경에는 fflush(3)를 통해서 socket으로 강제 전송하게 됩니다.

 

Sample). Stream I/O를 통한 socket 프로그래밍.

// tcpip_client 프로그램 sample
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include "tcpip_lib.h"

#define SERVER_NAME     "localhost"   // 서버에 대한 domain이나 IP address 정의
#define SERVER_PORT      23           // 서버에 대한 port번호 설정

int main(int argc, char *argv)
{
    int sock;
    int recv_len;
    char recv_buf[4096];
    char send_buf[4096];
    FILE *fp = NULL;

    if((sock = TCPIPconnect(SERVER_NAME, SERVER_PORT)) == -1) {
        fprintf(stderr, "tcp_connect error: %s\n", strerror(errno));
        return -1;
    }

    if((fp = fdopen(sock, "r+")) != NULL) {
        return 1;
    }
    setvbuf(fp, NULL, _IOLBF, 0);

    if(fgets(recv_buf, 4096, fp) != NULL) {
        printf("%s\n", recv_buf);
        fprintf(fp, "hw\n");
    }

    fclose(fp);
    
    return 0;
}

 


 

see also :  TCP/IP 통신 프로그램   Socket 통신 관련 Library  

 

 

 

 

반응형
블로그 이미지

자연&사람

행복한 개발자 programmer since 1995.

,