본문 바로가기

Computer Network

[Lecture] Socket programming

Socket programming?

* socket programming reference : TCP/IP 소켓 프로그래밍 - C버전, Michael J.Donahoo, 박준철 번역

os에서 제공하는 서비스를 이용하기 위해 인터페이스를 사용해야한다. 다른 컴퓨터의 두 process사이의 소통을 위해선 socket이라는 api가 필요하다.

응용 프로그램과 네트워크 간의 인터페이스
응용 프로그램이 소켓을 만든다
소켓 유형은 통신 스타일을 지시한다

reliable vs. best effort
connection-oriented vs. connectionless

 

Two essential types of sockets

소켓의 통신 유형에 따라 두가지로 나뉜다. (TCP, UDP)

 

  • SOCK_STREAM
  • TCP socket
    • reliable delivery
    • in-order guaranteed
    • connection-oriented

 

 

  • SOCK_DGRAM
  • UDP socket
    • unreliable delivery
    • no order guarantees
    • no notion of "connection"-app indicates dest. for each packet
    • can send of receive

 

Socket 소통 방식

1. 서버에서 socket()을 생성한다

2. bind()로 특정포트에 연결한다

ㄴ소켓을 서버의 IP주소와 포트번호와 결합시킨다

3. listen()함수는 연결 요청을 대기하는 것(듣기 용도)이다 (non-blocking)

4. accept()함수는 클라이언트의 요청을 받을 준비가 됐다는 뜻이다. (blocking)

 

클라이언트로부터 connection이 들어올때까지 멈춘다

 

5. 클라이언트도 socket()을 생성한다

6. connect()함수(blocking)로 원하는 서버에 연결한다.

 

이후 클라이언트와 서버 사이의 단단한 연결고리(소켓생성)가 생긴다

: 맨 처음에 생성했던 소켓을 통신에 사용하지 않고, 새로 들어온 손님 전용으로 소켓을 만든다

: 기존 소켓은 닫아도 된다

write(), read()함수를 통해 data를 주고받다가 할말이 끝났을 때 close()를 한다

close의 호출순서는 상관없다

 

 

Socket Creation and Setup

Include file <sys/socket.h>

  • Create a socket
  • - int socket (int domain(protocol family), int type(UDP/TCP를 결정), int protocol(protocol within family);

- 성공하면 file descriptor를, 실패하면 -1을 리턴하고 errono가 설정된다

- protocol을 전형적으로 0이 세팅된다. getprotobyname()을 사용하기도 한다

 

  • Bind a socket to a local IP address and port number
  • int bind (int sockfd(소켓으로부터 리턴된 소켓 파일 디스크립터),  struct sockaddr* myaddr(IP address와 port number), int addrlen(length of address structure));

- 성공하면 0을, 실패하면 -1을 리턴하고 errono가 설정된다

- IP address : 전달된 값이 INADDR_ANY이면 커널에 의해 설정되고, 아니면 호출자에 의해 설정된다

- port number : 전달된 값이 0이면 커널에 의해 설정되고, 아니면 호출자에 의해 설정된다

- addrlen : sizeof(struct sockaddr_in)

 

  • Put socket into passive state (wait for connections rather than initate a connection)
  • 소켓을 수동 상태로 전환(연결을 시작하기보다 연결을 기다린다)
  • int listen (int sockfd (소켓으로부터 리턴된 소켓 파일 디스크립터) , int backlog(동시에 대기 중인 연결 요청의 최대 큐 크기));

- 성공하면 0을, 실패하면 -1을 리턴하고 errono가 설정된다

- 동시에 request시 큐에 들어오게 하고, 처리해준다(높게 설정한다고 해서 좋은것만은 아니다)

- listen함수가 non-blocking이라는 의미는,  이 함수를 호출했을 때 프로그램이 다음 코드로 진행되어도 블로킹되지 않는다는 것을 의미한다. listen() 함수는 대부분 즉시 반환한다

 

  • Accept connections
  • int accept (int sockfd, struct sockaddr* cliadder(클라이언트의 IP address and port number), int* addrlen(length of address structrue));

- 성공하면 file descriptor를, 실패하면 -1을 리턴한다

- 클라이언트가 connection하면 struct sockaddr에 클라이언트의 port number가 저장된다

- accept() 함수는 클라이언트의 연결 요청이 도착할 때까지 블로킹된다

 

  • Connect to another socket
  • -int connect (int sockfd, struct sockaddr* servaddr(server의 IP address와 port number), int addrlen);

- 성공하면 0을, 실패하면 -1을 리턴하고 errono가 설정된다

Q. 여기서 왜 bind를 사용하지 않고 connect를 사용했는가?

A. bind는 특정 포트 번호와 IP 주소에 서버 소켓을 연결한다. 클라이언트는 아무포트나 사용하면 된다. 따라서 bind를 굳이 사용할 필요가 없다.

 

 

  • int close (int sockfd)

- 성공하면 0을, 실패하면 -1을 리턴한다

- Tip : 프로그램을 ctrl+c할 때 해당 프로그램과 연결되어 있는 소켓 자료구조들은 한동안 os자료구조들이 들고 있다. 이런것들도 release되어야 편하기 때문에 아래 코드를 기억하면 좋다

 

Sample code

 

 

 

Sending and Receiving Data

  • Write data to a stream(TCP)
  • int write (int sockfd, char* buf(data buffer), size_t nbytes(쓸 수 있는 바이트들의 숫자=쓰여질 바이트 수));

- 성공하면 쓰여진 바이트들의 숫자를, 실패하면 -1을 리턴하고 errono가 설정된다

- write is blocking; returns only after data is sent

 

  • Read data from a stream(TCP)
  • int read (int sockfd, char* buf(data buffer), size_t nbytes(읽을 수 있는 바이트들의 숫자));

- 성공하면 쓰여진 바이트들의 숫자를, 실패하면 -1을 리턴하고 errono가 설정된다

- socket closed이면 0이 리턴된다

- read is blocking; returns only after data is received

 

+ UDP Socket Functions

 

Project 1 : Web server 구현하기

A. 웹 브라우저가 TCP소켓을 열어서(웹서버도 TCP 소켓을 연다) Http request를 보낸다. Http request를 읽어서 출력을 한다.

B. 실제 웹브라우저가 하는 일. 어떤 파일을 요청하는지 parsing하고 디렉토리에서 읽어서 그 파일을 실어 응답을 작성한다.

 

 

Multiplexing and Demultiplexing

TCP UDP 상관없이 무조건 제공되는 기능

컴퓨터 내부에 프로세스들이 많이 있는 상태에서 각자의 소켓이 있을 것이다. 이 소켓을 통해서 데이터가 transport layer로 내려온다. 이 여러가지 데이터를 하나의 segment로 만들어 쭉 내려준다 : Multiplexing

receiver가 segment를 받아서 메세지를 딱 꺼내서 메세지를 받아야하는 프로세스들에게 딱 올려줘야한다 : Demultiplexing

어떻게 알맞은 곳에 올려줄까? segment의 header에 적힌 정보를 가지고 판단한다

 

segment : data and header

실제로 데이터부분은 헤더부분보다 데이터가 훨씬 크다

헤더 : 편지봉투 밖에 적힌 주소

데이터 : 편지 내용물

 

UDP의 demultiplexing

segment

source port : 자기자신 포트넘버

dest port : 상대 포트넘버

*IP정보는 Network head에 IP address라는 필드가 있어서 이때 넘어간다

 

UDP를 사용할 경우엔, dest IP와 dest port정보만을 가지고 어떤 소켓으로 올릴지 결정하게 된다

 

TCP의 demultiplexing

segment

source IP

source port : 자기자신 포트넘버

dest IP

dest port : 상대 포트넘버

 

TCP는 4개의 tuple을 가지고 어떤 소켓으로 올릴지 결정한다. 4개 중 하나만 달라도 다른 소켓으로 올라가는 것이다

 

 

단톡방 : (broad casting)되어 보이지만 각자 TCP소켓 연결이 되어있다 

 

UDP Header

 

UDP TCP IP 헤더 정보는 무조건 알고 있어야한다. 해당 프로토콜이 어떻게 동작하는지 알 수 있기 때문이다

우편배달부는 편지봉투에 적힌 것에 근거하는 것과 같다

(한bit당 필드의 크기는 16비트이기 때문에 포트넘보는 6만정도(2의 16승 -1)개가 된다) 

UDP는 포트넘버를 보고 Mul Demultiplexing을 한다

checksum은 전송 중 데이터가 에러가 있는지 없는지 확인해주는 필드이다

ㄴ 에러가 생기면 바로 drop시킨다

UDP로 메세지를 받으면 최소한 그 메세지엔 에러가 없다는 것이다