Notice
Recent Posts
Recent Comments
Link
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
Archives
Today
Total
관리 메뉴

eliwook 님의 블로그

C언어로 개발하는 socket 서버1 [echo client, server] 본문

Jugle/Today I Learned

C언어로 개발하는 socket 서버1 [echo client, server]

eliwook 2024. 8. 19. 23:33

socket을 이용해 echo client server을 구현해보자!

이 내용은 CS:APP 에 나와있는 코드이며 이를 분석하는 방식으로 진행하겠다.

아래는 클라이언트 echo 코드이다.

#include "csapp.h"

int main(int argc, char **argv){
    int clientfd;
    char *host, *port, buf[MAXLINE];
    rio_t rio;

    if (argc !=3){
        fprintf(stderr,"usage: %s <host> <port>\\n", argv[0]);
        exit(0);
    }
    host = argv[1];
    port = argv[2];

    clientfd = Open_clientfd(host,port);
    Rio_readinitb(&rio, clientfd);

    while(Fgets(buf,MAXLINE,stdin) != NULL){
        Rio_writen(clientfd,buf,strlen(buf));
        Rio_readlineb(&rio, buf, MAXLINE);
    }
    Close(clientfd);
    exit(0);
}

우리가 서버에 연결하기 위해서는 소켓이 필요하다. 이 소켓에는 우리가 연결해야할 서버의 주소, 포트, 그리고 서로 대화하기 위한 버퍼가 필요하다. 이렇게 선언한 buffer을 커널 I/O에서 사용할 수 있도록 rio_t 구조체가 필요하다. 그래서 main함수의 초기 부분에 이런 변수들을 초기화 한다.

Unix 계열의 OS에서는 socket도 하나의 파일이라고 생각(추상화) 한다.

그래서 이런 디스크립터를 가리킬 clientfd라는 소켓 파일 디스크립터를 선언한다.

우리가 연결 할 host,port의 값을 넣어주고, Open_clientfd 함수를 사용해서 우리가 연결할 host와 port로 연결을 시도한다.

Rio_readinitb 함수는 rio 구조체를 초기화 하는 함수이다.

파일 디스크립터와 내부 버퍼를 연결해서 성능을 개선시킬 수 있다.

효율성, 인터페이스 일관성, 에러처리 개선, 라인단위 읽기 등의 기능을 제

공한다.

이제 통신이 끝날때까지 정보를 주고 받음으로 while문을 사용한다.

Fgets를 이용해서 표준 입력에서 한줄을 읽고 buffer에 쓴다. 이때 값을 입력하지 않으면 통신을 끝낸다.

Rio_writen을 사용해서 입력받은 데이터를 서버로 전송한다.(client 파일 디스크립터에 쓴다. 데이터를 쓰면 운영체제가 알아서 전송해줌)

Rio_readlined를 통해서 이후 서버에서 데이터를 전송 받으면 Fputs를 사용해서 표준 출력으로 출력한다.

만약 통신이 끝나면 파일 디스크립터를 닫고 코드를 종료한다.

이제 서버 코드를 분석해보겠다.

#include "csapp.h"

void echo(int connfd);

int main(int argc, char **argv){
    int listenfd, connfd;
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;
    char client_hostname[MAXLINE], client_port[MAXLINE];

    if(argc !=2){
        fprintf(stderr, "usage: %s <port>\\n", argv[0]);
        exit(0);
    }

    listenfd = Open_listenfd(argv[1]); //argv[1] == port
    while(1) {
        clientlen = sizeof(struct sockaddr_storage);
        connfd = Accept(listenfd, (SA*)&clientaddr, &clientlen);
        Getnameinfo((SA *) &clientaddr, clientlen, client_hostname, MAXLINE,    // sockaddr = sa
                    client_port, MAXLINE,0);        // 연결된 클라이언트의 정보에 접근
        printf("Connected to (%s %s)\\n", client_hostname, client_port);
        echo(connfd);
        Close(connfd);
    }
    exit(0);
}

서버에서는 클라이언트가 접속을 요청을 하는지 확인을 해야하고

클라이언트가 접속을 했으면 접속한 클라이언트의 소켓(파일 디스크립터)를 만들어 주어야 하고, 클라이언트의 정보를 저장해놔야한다.

아래는 위의 과정을 저장해 놓을 변수이다.

listenfd, connfd: 리스닝 소켓과 연결 소켓의 파일 디스크립터

clientlen: 클라이언트 주소 구조체의 길이

clientaddr: 클라이언트 주소 정보를 저장할 구조체

client_hostname, client_port: 클라이언트의 호스트 이름과 포트를 저장할 문자열

Open_listenfd 함수를 호출하여 지정된 포트에서 리스닝 소켓을 생성한다.

무한루프를 돌면서 클라이언트와의 연결을 계속 받아드린다.

Accept 함수를 사용하여 클라이언트의 연결을 수락한다.

Getnameinfo 함수를 사용하여 연결된 클라이언트의 호스트 이름과 포트 정보를 가져온다.

Getnameinfo 로 가지고 온 클라이언트 정보를 출력하고 echo함수를 통해 클라이언트와 통신을 한다.

통신이 끝나면 연결을 끊는다.

이제 이를 컴파일 해줄 Makefile만들어서 컴파일 하면 끝이다.

CC = gcc
CFLAGS = -O2 -Wall -I .

# This flag includes the Pthreads library on a Linux box.
# Others systems will probably require something different.
LIB = -lpthread

all: echoclient echoserver

echoserver: echoserver.c csapp.o
	$(CC) $(CFLAGS) -o echoserver echoserver.c csapp.o $(LIB)
	
echoclient: echoclient.c csapp.o
	$(CC) $(CFLAGS) -o echoclient echoclient.c csapp.o $(LIB)

csapp.o: csapp.c
	$(CC) $(CFLAGS) -c csapp.c

clean:
	rm -f *.o echoserver echoclient *~

간단하게 echoserver을 예시로 들면 echoserver.c와 csapp.o를 컴파일 하고 링크하여 echoserver을 만들겠다 라는 명령이고 아래도 동일하게 돌아간다.

client 실행결과

telnet으로 서버에 접속한 결과

서버에 남은 log

이 내용은 CS:APP 에 나와있는 코드이며 이를 분석하는 방식으로 진행하겠다.

아래는 클라이언트 echo 코드이다.

#include "csapp.h"

int main(int argc, char **argv){
    int clientfd;
    char *host, *port, buf[MAXLINE];
    rio_t rio;

    if (argc !=3){
        fprintf(stderr,"usage: %s <host> <port>\\n", argv[0]);
        exit(0);
    }
    host = argv[1];
    port = argv[2];

    clientfd = Open_clientfd(host,port);
    Rio_readinitb(&rio, clientfd);

    while(Fgets(buf,MAXLINE,stdin) != NULL){
        Rio_writen(clientfd,buf,strlen(buf));
        Rio_readlineb(&rio, buf, MAXLINE);
    }
    Close(clientfd);
    exit(0);
}

우리가 서버에 연결하기 위해서는 소켓이 필요하다. 이 소켓에는 우리가 연결해야할 서버의 주소, 포트, 그리고 서로 대화하기 위한 버퍼가 필요하다. 이렇게 선언한 buffer을 커널 I/O에서 사용할 수 있도록 rio_t 구조체가 필요하다. 그래서 main함수의 초기 부분에 이런 변수들을 초기화 한다.

Unix 계열의 OS에서는 socket도 하나의 파일이라고 생각(추상화) 한다.

그래서 이런 디스크립터를 가리킬 clientfd라는 소켓 파일 디스크립터를 선언한다.

우리가 연결 할 host,port의 값을 넣어주고, Open_clientfd 함수를 사용해서 우리가 연결할 host와 port로 연결을 시도한다.

Rio_readinitb 함수는 rio 구조체를 초기화 하는 함수이다.

파일 디스크립터와 내부 버퍼를 연결해서 성능을 개선시킬 수 있다.

효율성, 인터페이스 일관성, 에러처리 개선, 라인단위 읽기 등의 기능을 제

공한다.

이제 통신이 끝날때까지 정보를 주고 받음으로 while문을 사용한다.

Fgets를 이용해서 표준 입력에서 한줄을 읽고 buffer에 쓴다. 이때 값을 입력하지 않으면 통신을 끝낸다.

Rio_writen을 사용해서 입력받은 데이터를 서버로 전송한다.(client 파일 디스크립터에 쓴다. 데이터를 쓰면 운영체제가 알아서 전송해줌)

Rio_readlined를 통해서 이후 서버에서 데이터를 전송 받으면 Fputs를 사용해서 표준 출력으로 출력한다.

만약 통신이 끝나면 파일 디스크립터를 닫고 코드를 종료한다.

이제 서버 코드를 분석해보겠다.

#include "csapp.h"

void echo(int connfd);

int main(int argc, char **argv){
    int listenfd, connfd;
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;
    char client_hostname[MAXLINE], client_port[MAXLINE];

    if(argc !=2){
        fprintf(stderr, "usage: %s <port>\\n", argv[0]);
        exit(0);
    }

    listenfd = Open_listenfd(argv[1]); //argv[1] == port
    while(1) {
        clientlen = sizeof(struct sockaddr_storage);
        connfd = Accept(listenfd, (SA*)&clientaddr, &clientlen);
        Getnameinfo((SA *) &clientaddr, clientlen, client_hostname, MAXLINE,    // sockaddr = sa
                    client_port, MAXLINE,0);        // 연결된 클라이언트의 정보에 접근
        printf("Connected to (%s %s)\\n", client_hostname, client_port);
        echo(connfd);
        Close(connfd);
    }
    exit(0);
}

서버에서는 클라이언트가 접속을 요청을 하는지 확인을 해야하고

클라이언트가 접속을 했으면 접속한 클라이언트의 소켓(파일 디스크립터)를 만들어 주어야 하고, 클라이언트의 정보를 저장해놔야한다.

아래는 위의 과정을 저장해 놓을 변수이다.

listenfd, connfd: 리스닝 소켓과 연결 소켓의 파일 디스크립터

clientlen: 클라이언트 주소 구조체의 길이

clientaddr: 클라이언트 주소 정보를 저장할 구조체

client_hostname, client_port: 클라이언트의 호스트 이름과 포트를 저장할 문자열

Open_listenfd 함수를 호출하여 지정된 포트에서 리스닝 소켓을 생성한다.

무한루프를 돌면서 클라이언트와의 연결을 계속 받아드린다.

Accept 함수를 사용하여 클라이언트의 연결을 수락한다.

Getnameinfo 함수를 사용하여 연결된 클라이언트의 호스트 이름과 포트 정보를 가져온다.

Getnameinfo 로 가지고 온 클라이언트 정보를 출력하고 echo함수를 통해 클라이언트와 통신을 한다.

통신이 끝나면 연결을 끊는다.

이제 이를 컴파일 해줄 Makefile만들어서 컴파일 하면 끝이다.

CC = gcc
CFLAGS = -O2 -Wall -I .

# This flag includes the Pthreads library on a Linux box.
# Others systems will probably require something different.
LIB = -lpthread

all: echoclient echoserver

echoserver: echoserver.c csapp.o
	$(CC) $(CFLAGS) -o echoserver echoserver.c csapp.o $(LIB)
	
echoclient: echoclient.c csapp.o
	$(CC) $(CFLAGS) -o echoclient echoclient.c csapp.o $(LIB)

csapp.o: csapp.c
	$(CC) $(CFLAGS) -c csapp.c

clean:
	rm -f *.o echoserver echoclient *~

간단하게 echoserver을 예시로 들면 echoserver.c와 csapp.o를 컴파일 하고 링크하여 echoserver을 만들겠다 라는 명령이고 아래도 동일하게 돌아간다.

client 실행결과

telnet으로 서버에 접속한 결과

서버에 남은 log

이 내용은 CS:APP 에 나와있는 코드이며 이를 분석하는 방식으로 진행하겠다.

아래는 클라이언트 echo 코드이다.

#include "csapp.h"

int main(int argc, char **argv){
    int clientfd;
    char *host, *port, buf[MAXLINE];
    rio_t rio;

    if (argc !=3){
        fprintf(stderr,"usage: %s <host> <port>\\n", argv[0]);
        exit(0);
    }
    host = argv[1];
    port = argv[2];

    clientfd = Open_clientfd(host,port);
    Rio_readinitb(&rio, clientfd);

    while(Fgets(buf,MAXLINE,stdin) != NULL){
        Rio_writen(clientfd,buf,strlen(buf));
        Rio_readlineb(&rio, buf, MAXLINE);
    }
    Close(clientfd);
    exit(0);
}

우리가 서버에 연결하기 위해서는 소켓이 필요하다. 이 소켓에는 우리가 연결해야할 서버의 주소, 포트, 그리고 서로 대화하기 위한 버퍼가 필요하다. 이렇게 선언한 buffer을 커널 I/O에서 사용할 수 있도록 rio_t 구조체가 필요하다. 그래서 main함수의 초기 부분에 이런 변수들을 초기화 한다.

Unix 계열의 OS에서는 socket도 하나의 파일이라고 생각(추상화) 한다.

그래서 이런 디스크립터를 가리킬 clientfd라는 소켓 파일 디스크립터를 선언한다.

우리가 연결 할 host,port의 값을 넣어주고, Open_clientfd 함수를 사용해서 우리가 연결할 host와 port로 연결을 시도한다.

Rio_readinitb 함수는 rio 구조체를 초기화 하는 함수이다.

파일 디스크립터와 내부 버퍼를 연결해서 성능을 개선시킬 수 있다.

효율성, 인터페이스 일관성, 에러처리 개선, 라인단위 읽기 등의 기능을 제

공한다.

이제 통신이 끝날때까지 정보를 주고 받음으로 while문을 사용한다.

Fgets를 이용해서 표준 입력에서 한줄을 읽고 buffer에 쓴다. 이때 값을 입력하지 않으면 통신을 끝낸다.

Rio_writen을 사용해서 입력받은 데이터를 서버로 전송한다.(client 파일 디스크립터에 쓴다. 데이터를 쓰면 운영체제가 알아서 전송해줌)

Rio_readlined를 통해서 이후 서버에서 데이터를 전송 받으면 Fputs를 사용해서 표준 출력으로 출력한다.

만약 통신이 끝나면 파일 디스크립터를 닫고 코드를 종료한다.

이제 서버 코드를 분석해보겠다.

#include "csapp.h"

void echo(int connfd);

int main(int argc, char **argv){
    int listenfd, connfd;
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;
    char client_hostname[MAXLINE], client_port[MAXLINE];

    if(argc !=2){
        fprintf(stderr, "usage: %s <port>\\n", argv[0]);
        exit(0);
    }

    listenfd = Open_listenfd(argv[1]); //argv[1] == port
    while(1) {
        clientlen = sizeof(struct sockaddr_storage);
        connfd = Accept(listenfd, (SA*)&clientaddr, &clientlen);
        Getnameinfo((SA *) &clientaddr, clientlen, client_hostname, MAXLINE,    // sockaddr = sa
                    client_port, MAXLINE,0);        // 연결된 클라이언트의 정보에 접근
        printf("Connected to (%s %s)\\n", client_hostname, client_port);
        echo(connfd);
        Close(connfd);
    }
    exit(0);
}

서버에서는 클라이언트가 접속을 요청을 하는지 확인을 해야하고

클라이언트가 접속을 했으면 접속한 클라이언트의 소켓(파일 디스크립터)를 만들어 주어야 하고, 클라이언트의 정보를 저장해놔야한다.

아래는 위의 과정을 저장해 놓을 변수이다.

listenfd, connfd: 리스닝 소켓과 연결 소켓의 파일 디스크립터

clientlen: 클라이언트 주소 구조체의 길이

clientaddr: 클라이언트 주소 정보를 저장할 구조체

client_hostname, client_port: 클라이언트의 호스트 이름과 포트를 저장할 문자열

Open_listenfd 함수를 호출하여 지정된 포트에서 리스닝 소켓을 생성한다.

무한루프를 돌면서 클라이언트와의 연결을 계속 받아드린다.

Accept 함수를 사용하여 클라이언트의 연결을 수락한다.

Getnameinfo 함수를 사용하여 연결된 클라이언트의 호스트 이름과 포트 정보를 가져온다.

Getnameinfo 로 가지고 온 클라이언트 정보를 출력하고 echo함수를 통해 클라이언트와 통신을 한다.

통신이 끝나면 연결을 끊는다.

이제 이를 컴파일 해줄 Makefile만들어서 컴파일 하면 끝이다.

CC = gcc
CFLAGS = -O2 -Wall -I .

# This flag includes the Pthreads library on a Linux box.
# Others systems will probably require something different.
LIB = -lpthread

all: echoclient echoserver

echoserver: echoserver.c csapp.o
	$(CC) $(CFLAGS) -o echoserver echoserver.c csapp.o $(LIB)
	
echoclient: echoclient.c csapp.o
	$(CC) $(CFLAGS) -o echoclient echoclient.c csapp.o $(LIB)

csapp.o: csapp.c
	$(CC) $(CFLAGS) -c csapp.c

clean:
	rm -f *.o echoserver echoclient *~

간단하게 echoserver을 예시로 들면 echoserver.c와 csapp.o를 컴파일 하고 링크하여 echoserver을 만들겠다 라는 명령이고 아래도 동일하게 돌아간다.

client 실행결과

telnet으로 서버에 접속한 결과

서버에 남은 log

'Jugle > Today I Learned' 카테고리의 다른 글

sockaddr, sockaddr_in 차이  (0) 2024.08.18
네트워크 OSI 7Layer TCP/IP 4 Layer  (0) 2024.08.18
쉽게 이해하는 socket통신  (0) 2024.08.17
동적 메모리 할당  (0) 2024.08.12
R-B Tree의 삭제  (0) 2024.08.08