[CS]/[컴퓨터네트워크]

애플리케이션계층2 - 소켓프로그래밍

broship 2021. 2. 20. 16:19

※kocw에서 제공하는 이석복 교수님의 컴퓨터네트워크 수업을 듣고 필기한 내용입니다.

출처를 따로 밝히지 않는 한 전부 해당 수업에서 제공한 자료들이며 제가 작성한 부분에 있어 틀린 부분이 있을 수도 있다는 점 양해바랍니다.

 


 

소켓 프로그래밍 


애플리케이션 계층에서 사용되는 소켓을 알아보고, c 코드로 직접 구현해보기

 

소켓이란?

소켓은 os에서 제공하는 api 중, 프로세스 간의 통신을 위한 api를 소켓이라고 함. 다양한 function들을 가지고 있음

 

어플리케이션에서 사용할 수 있는 서비스는 전송계층에서 제공해주는 서비스 밖에 이용 못하는데, 전송계층에서는 TCP. UDP 만을 제공함. 이 중에서 하나를 선택해서 사용해야됨

tcp: SOCK_STREAM

udp: SOCK_DGRAM

 

 

 

 

TCP 소켓 프로그래밍 진행 순서


 

웹서버 진행 순서

1. 소켓 생성 (tcp 소캣) socket()

- int socket(int domain, int type, int protocol);

returns file descriptor or -1

2. bind() 방금 생성한 소켓을 특정 포트에 바인드 한다(ex 80번 포트)

- int bind(int sockfd, struct sockaddr* myaddr, int addrlen);

3. listen() 이 소켓은 listen 하는 형태로 사용하겠다

4. accept() 나는 클라이언트로부터 요청을 받을 준비가 되어있다 이제 들어와라

이때 서버는 클라이언트가 요청을 할때까지 blocks(가만히 있음) 형태가 됨 - blocks until connection from client

 

웹클라이언트(웹브라우저) 진행 순서

1) 소켓 생성

2) 바로 connect() -> 내가 원하는 서버의 프로세스에 접근하겠다

 

이 과정까지 끝나면 서버와 클라이언트 사이에  연결고리가 생성됨

이 이후로는 단순함 read()와 write()의 반복임

계속 이렇게 진행되다가 끝나면 close() 해서 연결 끊으면 됨

 

 

 

소켓 프로그래밍 중요 함수들


1. int socket(int domain, int type, int protocol);

중요한건 2번째 파라미터 type, tcp 소켓인지 udp 소켓인지를 정함(SOCK_STREAM or SOCK_DGRAM를적으면됨)

SOCK_STREAM for TCP

SOCK_DGRAM for UDP

 

리턴값은 방금 생성된 소켓의 아이디값이 반환됨 그걸로 이 소켓을 지칭하면 됨

 

2. int bind(int sockfd, struct sockaddr* myaddr, int addrlen);

방금 생성한 소켓의 아이디를 사용해서 이 소켓을 특정 어드레스(ip주소,포트)에 바인딩 하겠다

 

3. int listen(int sockfd, int backlog);

이 소켓에 요청이 들어오면 최대 몇개까지 큐에 담아놓고 처리를 하겠다, backlog가 10이면 동시 10개까지 들어오면 그걸 처리해 주겠다

 

4. int accept(int sockfd, struct sockaddr* cliaddr, int* addrlen);

준비동작이 다 끝났으니까 이제 클라이언트로부터 요청을 기다리겠다

중요한건 2번째 파라미터, 참조 포인트 accept 후에는 block 대기하고 있다가 요청이 오면 그때 값이 반환이 되는데 그 리턴될때 클라이언트의 ip와 포트번호가 이 2번째 파라미터에 저장이 됨 이때 서버도 클라이언트의 ip주소와 포트번호를 알게됨

 

클라이언트는 connect() 밖에 없음

2) int connect(int sockfd, struct sockaddr* servaddr, int addrlen);

클라이언트에는 bind가 없는 이유는 그냥 안쓰는 포트 아무거나 쓰면 되기 때문, 특정 포트를 사용하고 싶으면 클라이언트에서도 bind 사용 가능

 

 

최종 정리

서버에서 먼저 소켓 만듬(tcp 소켓)

bind로 특정 포트에 바인드 함

listen 용도로 지정

accept 상태가 됨

...

이 후에는 단순한 read&write

 

 

 

 

 

c언어 소켓 프로그래밍 예시 코드(TCP)


서버 코드

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define PORT 3490 /*포트는 3490번을 사용*/
#define BACKLOG 10 /*최대 접속 가능한 큐 크기*/

main()
{
	int sockfd, new_fd; /*listen on sock_fd, new connection on new_fd */
    struct sockaddr_in my_addr; /*my address*/
    struct sockaddr_in their_addr; /*connecttor addr*/
    int sin_size;
    
    /*
    	sockaddr_in 구조:
        struct sockaddr_in {
        	short sin_family;
            u_short sin_port;
            struct in_addr sin_addr;
        };
        sin_family = AT_INET
        sin_port: port #(0-65535)
        sin_addr: IP-address
    */
    
    if((sockfd = socket(PF_INET, SOCK_STREAM, 0))==-1){
    	perror("socket");
        exit(1);
    }
    
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(MYPORT); /*short, network byte order */
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    /* INADDR_ANY allows clients to connect to any one of the host's IP address*/
    
    if(bind(sockfd,(struct sockaddr *)&my_addr, sizeof(struct sockaddr))==-1){
    	perror("bind");
        exit(1);
    }
    
    if(listen(sockfd, BACKLOG) == -1) {
    	perror("listen");
        exit(1);
    }
    while(1) { /*main accept() loop*/
    	sin_size = sizeof(struct sockaddr_in);
        /*accept()가 실행되면 클라이언트에서 요청이 올때까지 block 상태가 됨*/
        if((new_fd = accept(sockfd, (struct sockaddr*)&their_addr,&sin_size))==-1){
        	perror("accept");
            continue;
        }
        /*접속한 클라이언트 정보 출력*/
        printf("server: got connection form %s\n". inet_ntoa(their_adddr.sin_addr));
    }
}

 

 

클라이언트 코드

if((sockfd = socket(PF_INET, SOCK_STREAM, 0))==-1){
	perror("socket");
    exit(1);
}

their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(Server_Portnumber);
their_addr.sin_addr = htonl(Servel_IP_address);

if(connect(sockfd,(struct sockaddr*)&their_addr, sizeof(struct sockaddr))==-1){
	perror("connect");
    exit(1);
}

 

 

 

 

 

UDP


udp 소켓은 훨씬 단순함, 소켓 생성하고 커넥션 개념 없이 바로 보내면 됨