※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 소켓은 훨씬 단순함, 소켓 생성하고 커넥션 개념 없이 바로 보내면 됨
'[CS] > [컴퓨터네트워크]' 카테고리의 다른 글
전송계층3 - TCP Protocol (0) | 2021.03.11 |
---|---|
전송계층2 - Reliable한 데이터 전송을 위한 기능들 (0) | 2021.03.01 |
전송계층1 - 전송계층이 제공하는 2가지 기본 기능(Multiplexing and Demultiplexing, 에러체크) (0) | 2021.02.28 |
애플리케이션계층1 - 사용자계층 네트워크애플리케이션종류 (0) | 2021.02.16 |
컴퓨터 네트워크 기본1 - 컴퓨터네트워크 및 인터넷 역사 (0) | 2021.02.08 |