-
시스템 프로그래밍 실습 10주차 : SocketsSystem Programming/Ubuntu Linux 2021. 11. 1. 12:10728x90
시스템 프로그래밍 실습 10주차 : Sockets
[목차]
- Internet Connections
- Client-Server Model
Client
Server
- Socket
TCP / UDP
socket 함수들 ( socket(), connect(), bind(), listen(), accept() )
- 예제: Echo Client & Echo Server
Internet Connections
+) 꼭 먼저 알아야 할 기본!
=> 인터넷 등의 네트워크에서 통신을 원할하게 하기 위해서 필요한 공통된 통신규약, 즉 프로토콜을 지정하였다!
- ISO(International Standardization Organiztion)의 OSI(Open System Interconnection)는 네트워크 계층 표준 구조
- TCP/IP Protocol은 인터넷 표준 프로토콜
이 중 인터넷 표준 프로토콜인 TCP/IP 계층은 크게 5계층(하드웨어 계층과 네트워크 접속계층을 묶어서 4개로도 본다)으로 나뉘며 다음의 특징을 가지고 있다.
- Application layer : 사용자에게 서비스를 제공하기 위한 계층.
- Transport layer : 패킷의 전송을 담당하는 계층.
- TCP : Transmission control Protocol
- UDP : User Datagram Protocol
- Network layer : 인터넷 계층이라고도 하며, 패킷이 전달되는 경로를 담당한다. 통상 TCP/IP라고 부르는 프로토콜에서 IP(Internet Protocol)가 이 계층에 속한 프로토콜이다.
- IP : Internet Protocol
- ICMP : Internet Control Message Protocol, IP 진단이나 제어 및 오류 응답
- IGMP : Internet Group Management Protocol, 호스트 컴퓨터와 주위 라우터 관리용 프로토콜. TTL (Time To Live)
- ARP : Address Resolution Protocol, IP주소를 물리적 네트워크 주소 (MAC Address)로 변환 (랜카드)
- RARP : Reverse Address Resolution Protocol, 물리적 네트워크 주소 (MAC Address)를 IP 주소로 변환
- Network access layer 와 Hardware layer : 물리적인 네트워크와의 연결을 담당. 일반적으로 이더넷 카드나 랜카드라고 부르는 부분이 이 계층에 속한다.
- 이더넷 카드 / 카드
Client-Server Model
- 하나의 Server process와 하나 이상의 client process들 = Client-Server Model
- 소통 순서
1. Client가 Server로 request를 보낸다
2. Server는 받은 request을 가지고 있는 Resource들을 활용하여 handle한다
3. Server는 Client로 response를 보낸다
4. Client는 response를 받는다
1. Client
- Client가 Server를 찾는 방법
=> IP Address와 Port 번호를 통해! / IP Address와 Port 번호를 통해 TCP/IP 프로토콜을 이용하여 통신할 수 있다
: 명령줄 인수로 원하는 Server의 이름과 Port 번호를 받아 해당 Server에 Socket 연결
1) Server Socket Addreess에 있는 IP 주소를 통해 Host를 확인한다
2) Server Socket에 있는 port를 통해 Service를 확인하고, 그 Service를 수행하기 위한 Server Process를 암묵적으로 확인한다
1) IP Address
- IP주소는 데이터가 전송될 목적지 호스트를 알려주는 역할을 한다.
- IP 주소는 인터넷을 이용할 때 사용하는 주소로, 점으로 구분된 32비트 숫자로 표시한다
- Server Socket Addreess에 있는 IP 주소를 통해 Host를 확인한다
- IP 주소는 네트워크 주소라고도 부르며, A~C 클래스로 구분된다
=> 사람은 숫자로 표현된 주소보다 글자로 표현된 주소를 더 잘 파악하기 때문에 IP 주소와 더불어 hostname을 지정한다!
- 인터넷에서 사용하는 호스트명은 호스트명 + 도메인의 형태로 구성된다
- www.abc.co.kr 에서 www은 호스트명, abc.co.kr 은 도메인명이 된다
- 호스트명과 도메인을 관리하는 시스템을 DNS(Domain Name System)이라고 한다.
2) Port
- Port는 IP 주소가 알려준 목적지 호스트에서 일어나고 있는 여러 Service Process들 중 하나를 특정해준다
- 2바이트 정수로 되어 있으므로 0~65535까지 사용할 수 있다
1) Ephemeral Port : 임의 포트 - 클라이언트에서 서버로 연결을 맺을때, 특별히 bind() 시스템 콜로 출발지 포트를 지정(bind)하지 않는다면, 커널은 임의의 포트를 할당!
2) Well Known Port : 서버에서 제공하는 service를 쓰기 위해 ...
- 위 그림에서 동일한 IP 주소(128.2.194.242)를 통해 Server Host에 접근하지만, 사용하고자 하는 Service에 따라 Port번호를 달리하여 (80은 웹 서버, 7은 에코 서버) 사용한다!
2. Server
- Server는 컴퓨터가 켜질 때부터 꺼질 때까지 돌아가야 하기 때문에 daemon이라 할 수 있으며, 그렇기 때문에 init process에서 만들어진다
1) 반복 서버 : 데몬 프로세스가 직접 클라이언트의 요청을 처리하는 형태의 서버
2) 동시 동작 서버
- 데몬 프로세스가 직업 처리하지 않음.
- 서비스와 관련 있는 다른 프로세스를 fork() 함수로 생성한 후 이 프로세스가 클라이언트와 연결해 서비스를 제공하는 서버
- inetd 데몬
- 각 서버는 request들을 기다린다
- server process가 돌아가는 기계를 server라고 부르기도 한다
Socket
- 응용 계층과 전송 계층을 연결 기능 제공
- 네트워크에 대한 사용자 수준의 인터페이스를 제공
- 소켓은 양방향 통신 방법으로 클라이언트-서버 모델을 기반으로 프로세스 사이의 통신에 매우 적합 (연결지향, 비연결지향 - 대부분 인터넷)
- 전송 서비스에서는 UDP와 TCP 두 종류가 있다
1) TCP (Transmission Control Protocol) : 연결형 서비스를 지원하는 전송계층 프로토콜
- 인터넷 환경에서 기본으로 사용
- 호스트간 신뢰성 있는 데이터 전달과 흐름제어 및 혼잡제어 등을 제공하는 전송계층
- 가상 회선 연결 방식, 연결형 서비스를 제공
- 특징
- 높은 신뢰성(Sequence Number, Ack Number를 통한 신뢰성 보장)
- 데이터 흐름 제어 (수신자 버퍼 오버플로우 방지) 및 혼잡 제어 (네트워크 내 패킷 수가 과도하게 증가하는 현상 방지)
- 전이중 (Full-Duplex), 점대점 (Point to Point) 서비스
- 소켓 통신 과정
- 서버 : 소켓을 생성, 주소 할당, 연결 요청 기다림, 요청에 대한 응답
- 클라이언트 : 소켓을 생성, 주소 할당, 연결 요청
2) UDP (User Datagram Protocol) : 비연결형 서비스를 지원하는 전송계층 프로토콜
- 사용자 데이터그램형 프로토콜
- 인터넷상에서 서로 정보를 주고받을 때 보내는 신호나 받는 신호를 거치지 않고, 보내는 쪽에서 일방적으로 데이터를 전달하는 통신 프로토콜
- 보내는 쪽에서는 받는 쪽이 데이터를 받았는지 받지 않았는지 확인할 수 없고, 또 확인할 필요도 없도록 만들어진 프로토콜
- 특징
- 비연결형 (port만 확인하여 소켓을 식별하고 송수신)
- 패킷 오버헤드가 적어 네트워크 부하 감소
- 비신뢰성
- 오류검출(헤더에 오류 검출 필드를 포함하여 무결성 검사)
- DNS, NFS, SNMP, RIP 등 사용
- 소켓 통신 과정
- 서버 : 소켓을 생성, 주소 할당, 데이터를 송수신
- 클라이언트 : 소켓 생성 후 데이터 수신- 모든 Internet Application들의 기본이 된다
- kernel의 입장에서 Socket은 communication의 끝이다
- application의 입장에서 Socket은 file descriptor다
=> client와 Server는 Socket descriptor에 쓰고 그것을 읽으면서 소통한다!
- 아래의 사진은 Socket Interface
: TCP/IP 프로토콜을 이용해 응용 프로그램을 작성할 때 TCP 계층에서 제공하는 인터페이스 함수를 직접 사용해 프로그래밍하면 매우 복잡할 뿐 아니라 관련 프로토콜의 내부 구조를 잘 알고 있어야 한다.
: 이런 복잡한 작업을 간편하게 해주는 것이 Socket Interface이다. 소켓 인터페이스는 응용 계층에서 전송 계층의 기능을 사용할 수 있도록 제공하는 응용 프로그래밍 인터페이스 (API, Application Programming Interface)이다.
: 소켓 인터페이스는 응용 프로그램과 TCP 계층을 연결하는 역할을 한다. 소켓 인터페이스를 이용하면 TCP/IP 프로토콜의 전송 계층이나 네트워크 계층의 복잡한 구조를 몰라도 쉽게 네트워크 프로그램을 작성할 수 있다.
- Socket은 Domain, 다른 말로 Family가 누구냐에 따라 두 종류로 나뉜다
- AF_UNIX : 유닉스 도메인 소켓 : 같은 호스트에서 프로세스 사이에 통신할 때 사용
- AF_INET : 인터넷 소켓 : 인터넷을 통해 다른 호스트와 통신할 때 사용
- 또한 Socket에서는 하부 프로토콜(전송 계층에서 사용하는 프로토콜)로 TCP와 UDP 둘 중 하나를 선택해야 함
- SOCK_STREAM : TCP 프로토콜 사용
- SOCK_DGRAM : UDP 프로토콜 사용
- hostent 구조체 / 유닉스에서는 호스트명과 IP주소를 변환하는 함수를 여러가지 형태로 제공한다!
struct hostent{ char *h_name; //호스트명 char **h_aliases; //호스트명을 가리키는 다른 이름들 int h_addrtype; //호스트 주소의 형식 int h_length; //주소의 길이 char **h_addr_list; //해당 호스트의 주소목록을 저장한다. }
- gethostbyname() : host 명으로 정보 검색
#include <netdb.h> struct hostent *gethostbyname(const char *name); *name : 검색하려는 호스트명
: gethostbyname함수는 호스트명을 인자로 받아 데이터베이스에서 해당 항목을 검색해 hostent구조체에 저장하고 그 주소를 리턴한다.
- gethostbyaddr(): IP주소로 정보 검색
#include <netdb.h> struct hostent *gethostbyaddr(const char *addr, int len, int type); * addr : 검색하려는 IP주소 * len : addr 길이 * type : IP 주소 형식
: 이 함수는IP주소를 인자로 받아 데이터베이스에서 해당항목을 검색해서 hostent 구조체에 저장하고 그 주소를 리턴한다.
- 소켓 구조체는 유닉스 도메인 소켓과 인터넷 소켓에서 다른 형태를 사용한다
: 인터넷 소켓 구조체의 경우, 주소 패밀리명, 포트 번호, IP주소가 들어간다
Socket 관련 함수들
- socket : 소켓 파일 기술자 생성
- connect : 클라이언트가 서버에 접속 요청
- bind : 소켓 파일 기술자를 지정된 IP주소/포트 번호와 결합(bind)
- listen : 클라이언트의 접속 요청 대기
- accept : 클라이언트의 접속 허용
1. socket() : 소켓 파일 기술자 생성
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); * domain : 소켓 종류 (유닉스 도메인 또는 인터넷 소켓) * type : 통신 방식 (TCP or UDP) * protocol : 소켓에 이용할 프로토콜
- domain에는 유닉스 도메인 소켓을 생성할 경우 AF_UNIX를 지정하고, 인터넷 소켓을 생성할 경우 AF_INET을 지정
- type에는 통신 방식에 따라 SOCK_STREAM이나 SOCK_DGRAM을 지정
- 성공하면 소켓 기술자를, 실패시 -1을 리턴
2. connect() : 클라이언트가 서버에 접속 요청
- Client에서는 connect() 이전에 bind() 부를 필요 없다
- connection이 만들어지기 전까지 Client Process는 block된다
#include <sys/types.h> #include <sys/socket.h> int connect(int s, const struct sockaddr *name, int namelen); * s : socket 함수가 생성한 소켓 기술자 * name : 접속하려는 서버의 IP 정보 / 소켓 구조체 * namelen : name의 크기
3. bind() : 소켓 파일 기술자를 지정된 IP주소/포트 번호와 결합(bind)
- bind() 함수를 이용하여 socket에 server socket 에 필요한 정보를 할당하고 커널에 등록
- 이 socket에 sockaddr_in 구조체를 이용해 주소를 할당하고 port 번호를 할당해서 커널에 등록해야 다른 시스템과 통신할 수 있는 상태가 된다
- Server들은 well-known port를 bind한다
- TCP server가 socket에 특정 IP 주소를 bind한다면, 그 IP 주소로만 향하게 client connection을 제한할 수 있다
- 일반적으로 TCP client는 kernel이 ephemeral port와 client IP 주소를 고르도록 한다
#include <sys/types.h> #include <sys/socket.h> int bind(int s, const struct sockaddr *name, int namlen); * s: socket함수가 생성한 소켓 기술자 * name : 소켓의 이름을 표현하는 구조체 * namelen: name의 크기
4. listen() : 클라이언트의 접속 요청 대기
- kernel이 client로부터 온 connection 요청을 받아들일 때까지 passive socket으로 대기하도록 함
- 지정한 descripter는 listening socket이 되고, backlog만큼의 큐 공간을 갖게 된다 (즉 backlog 만큼의 Client 연결 요청을 받을 수 있다).
#include <sys/types.h> #include <sys/socket.h> int listen(int s, int backlog); * s : socket함수가 생성한 소켓 기술자 * backlog : 최대 허용 클라이언트 수
5. accept() : 클라이언트의 접속 허용
- accept()로 접속 요청을 허락하게 되면 클라이언트와 통신을 하기 위해서 커널이 자동으로 소켓을 생성
- accept의 반환값은 성공/실패에 대한 정수값이 아닌, 새로운 디스크립터 번호, 즉 새로 생성받은 Connected Socket! (위 그림에서는 Server 쪽의 Client Socket, Client 쪽의 Client Socket 아님)
- 이 새로운 Socket을 이용하여 데이터 송수신을 할 수 있다!
#include <sys/types.h> #include <sys/socket.h> int accept(int s, struct sockaddr *addr, socklen_t *addrlen); * s : socket함수가 생성한 소켓 기술자 * addr : 접속을 수락한 클라이언트의 IP정보 * addrlen : addr크기 <기존 소켓 기술자 sd를 통해 연결이 수락되면 새로운 기술자 new_sd가 리턴된다. clisin에는 클라이언트 주소가 리턴된다.>
- 즉 listen() 함수에 지정한 descriptor가 위 그림에서의 listenfd (Listening descriptor), accept() 함수의 결과로 새롭게 생성되어 return된 descriptor가 connfd (Connection descriptor)가 된다!
- 그리고 이 connfd를 통해 Client와 Server가 소통할 수 있다
=> 요약
https://jihooyim1.gitbooks.io/unixbasic/content/contents/11.html
https://munsonghabnida.tistory.com/87
https://munsonghabnida.tistory.com/89
예제: Echo Client & Echo Server
1. Echo Client
변수 소개
cfd : Socket descriptor 받을 int 변수
hostent *h : host에 관련된 정보를 담는 구조체
sockaddr_in saddr : 소켓 구조체 중 인터넷 소켓의 주소 구조체
1. socket() 함수를 통해 소켓을 생성하고 socket descriptor를 cfd에 담는다
2. gethostbyname() 함수를 통해 host의 이름으로 데이터 베이스에서 host를 찾아 hostent 구조체 h에 저장한다
3. 소켓 구조체 saddr를 0으로 초기화한 후, 구조체 내에 주소 패밀리명, 포트 번호, IP주소 등을 지정한다
htons(port) : htons()함수는 short형의 데이터 즉, 포트번호를 호스트 바이트 순서에서 네트워크 바이트 순서로!
무슨 CPU을 쓰느냐에 따라 호스트 바이트 순서가 다르기 때문에 만약 서로 다른 CPU를 사용하는 컴퓨터들이 데이터를 주고 받을 때 문제가 발생!
=> 이 문제점 때문에 네트워크를 통해 데이터를 전송할 때는 통일된 방식을 이용해 데이터를 전송하기로 약속을 했는데 이것이 바로 네트워크 바이트 순서 !4. connect() 함수를 통해 Server에 연결을 요청한다. 연결이 되기 전까지 Client의 Process는 block된다
5. connect된 후 while의 조건문에서 read()를 통해 표준 입력으로 buf에 받는다. while문을 돌면서 buf에 적고 표준 출력으로 쓴다
2. Echo Server
변수 설명
listenfd : listen descriptor
connfd: connect descriptor
hostent *h : host에 관련된 정보를 담는 구조체
sockaddr_in saddr, caddr: 소켓 구조체 중 인터넷 소켓의 주소 구조체
1. socket() 함수를 통해 Server의 socket을 생성 후 listenfd에 socket descriptor를 받는다
2. 서버의 소켓 구조체 saddr를 0으로 초기화한 후, 구조체 내에 주소 패밀리명, 포트 번호, IP주소 등을 지정한다
htonl(INADDR_ANY) : long형의 데이터 즉, IP주소를 호스트 바이트 순서에서 네트워크 바이트 순서로!
무슨 CPU을 쓰느냐에 따라 호스트 바이트 순서가 다르기 때문에 만약 서로 다른 CPU를 사용하는 컴퓨터들이 데이터를 주고 받을 때 문제가 발생!
=> 이 문제점 때문에 네트워크를 통해 데이터를 전송할 때는 통일된 방식을 이용해 데이터를 전송하기로 약속을 했는데 이것이 바로 네트워크 바이트 순서 !3. bind() 함수를 통해 socket에 server socket 에 필요한 정보를 할당하고 커널에 등록! 이 socket에 sockaddr_in 구조체를 이용해 주소를 할당하고 port 번호를 할당해서 커널에 등록해야 다른 시스템과 통신할 수 있는 상태가 된다
4. listen() 함수를 통해 kernel이 client로부터 온 connection 요청을 받아들일 때까지 passive socket으로 대기하도록 하고, 지정한 descripter는 listening socket이 되고, backlog만큼의 큐 공간을 갖게 된다
5. accept() 함수를 통해 접속 요청을 허락하게 되면 클라이언트와 통신을 하기 위해서 커널이 자동으로 소켓을 생성 후 connfd에 descriptor를 받음! 이 새로 생성된 connect socket으로 Client와 Socket이 소통할 수 있다 (즉 client의 cfd와 socket의 connfd가 소통함 )
6. connfd를 읽어서 printf 함
https://jihooyim1.gitbooks.io/unixbasic/content/contents/12.html
728x90'System Programming > Ubuntu Linux' 카테고리의 다른 글
시스템 프로그래밍 실습 12주차 : Concurrent Programming (0) 2021.11.15 시스템 프로그래밍 실습 11주차 : Pthreads (0) 2021.11.08 Shell 만들기 참고할 것 (0) 2021.10.28 시스템 프로그래밍 실습 9주차 : System V IPC (0) 2021.10.22 시스템 프로그래밍 실습 8주차 : IPC (0) 2021.10.17