[soc] 소켓

2013. 8. 16. 11:15Soc

 

 

 소켓(Socket)

 

  소켓

  1. 소켓, 이용되는 함수, 기본 배경 지식 
  2. 소켓 주소체계 
  3. Server/Client 연결 과정 

 

소켓(socket) ;서로다른 두 컴퓨터가 데이터를 주고받기위해,  운영체제에서 제공하는 신 방법

- 서로 연락하기위해 주소를 할당해주어야 함 (IP, PORT)
- 연결당한는 쪽(server)과 연결하려는 쪽(client)로 나뉘어짐  



- 기본 적인 소켓 연결 과정을 나타낸 그림, 네모 칸의 함수들이 사용됨 
- 대략적인 함수 사용 용도

 SERVER

 1. socket() ; 소켓을 생성 
 2. bind() ; 소켓에 주소정보 할당
 3. listen() ; 연결 대기 ( client 쪽에서 connect 하기전까지 대기 )
 4. accept() ; 연결 허용, 수락
 5. send() / recv() ; 데이터 수신, 송신 
 6. close() ; 소켓 연결 해제

 CLIENT

 1. socket() ; 소켓을 생성
 2. connect() ; server에 연결 요청
 3. recv() / send() ; 데이터 송신, 수신
 4. close() ; 소켓 연결 해제  

- 간단한 server/client 예제
[hello_server.c]


[hello_client.c]

[hello_client.c] 실행 결과     


# 소켓에 기본적으로 사용되는 함수

 #include <sys/socket.h>
  int socket (int domain, int type, int protocol);
  -> 성공 시 파일
디스크립터, 실패 시 - 1 반환

- domain ; 소켓이 사용할 프로토콜 체계(Protocol Family) 정보 전달 

 이름

프로토콜 체계(Protodol Family) 

 PF_INET
 PF_INET6
 PF_LOCAL
 PF_PACKET
 PF_IPX

 IPv4 인터넷 프로토콜 체계
 IPv6 인터넷 프로토콜 체계
 로컬 통신을 위한 UNIX 프로토콜 체계
 Low Level 소켓을 위한 프로토콜 체계
 IPX 노벨 프로토콜 체계

// 소켓 통신에 사용되는 프로토콜도 부류가 나뉘기에 정보 전달 (주로 사용되어짐)


- type ; 소켓의 데이터 전송방식에 대한 정보 전달 

 연결 지향형 소켓(SOCK_STREAM) 

 1. 중간에 데이터가 소멸되지 않고 목적지로 전송됨
 2. 전송 순서대로 데이터가 수신
 3. 전송되는 데이터의 경계가 존재하지 않음 

( 데이터 송수신하는 소켓은 내부적으로 버퍼(바이트 배열)을 가지고 있음, 전송되는 데이터는 일단 이 배열에 저장이 되어, 한번 수신했다고, 무조건 한번 송신해야되는 것은 아님 , 배열의 용량 초과하지 않는 선에서 최대한 적게 read해도 되고 적은 데이터를 읽으며 많이 read 해도 됨 )
  => read(), write() 함수의 호출횟수는 연결지향형 소켓의 경우 큰 의미를 갖지 않음 
  => 데이터의 경계가 존재하지 않음
 4. 소켓 대소켓의 연결은 반드시 1:1 
 "신뢰성 있는 순차적인 바이트 기반의 연결지향 데이터 전송 방식 " 

  비 연결 지향형 소켓 (SOCK_DGRAM)

 1. 전송된 순서에 상관없이 가장 빠른 전송을 지향 
 2. 전송된 데이터는 손실의 우려가 있고, 파손의 우려가 있음
 3. 전송되는 데이터의 경계가 존재

( 데이터 수신시 3번의 write() 를 사용했다면 송신시 3번의 read()를 사용해야됨 ) 
 4. 한번에 전송할 수 있는 데이터의 크기가 제한됨  
"신뢰성과 순차적 데이터 전송을 보장하지 않는, 고속의 데이터 전송을 목적으로 하는 소켓 "

//PF_INET에 해당하는 프로토콜 체계에도 둘 이상의 데이터 전송방식이 존재하기에, 세부적인 정보 전달 (대표적인 전달방식) 

 - protocol ; 두 컴퓨터간 통신에 사용되는 프로토콜 정보 전달 
"IPv4 인터넷 프로토콜 체계에서 동작하는 연결지향형 데이터 전송 소켓"
    int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
"IPv4 인터넷 프로토콜 체계에서 동작하는 비 연결 지향형 데이터 전송 소켓"
    int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
// 첫 번째, 두 번째 인자만으로도 충분히 원하는 유형의 소켓을 생성할 수 있어, 대부분 0을 넘겨줘도 원하는 소켓을 생성 가능 하지만 하나의 프로토콜 체계 안에 데이터 전송방식이 동일한 프로토콜이 둘 이상 존재하는 경우 정보 전달 


 #include <sys/socket.h>
   int bind (int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
   -> 성공 시 0, 실패 시 -1 반환

 - sockfd ; 주소 정보를(IP, PORT) 할당할 소켓의 파일 디스크립터
 - myaddr ; 할당하고자 하는 주소정보를 지니는 구조체 변수의 주소 값
 - addrlen ; 두 번째 인자로 전달된 구조체 변수의 길이 정보 
struct sockaddr_in


    sa_family_t        sin_family;     // 주소체계(Address Family)
    unit16_t            sin_port;       // 16비트 TCP/UDP PORT 번

    struct in_addr    sin_addr;      // 32비트 IP 주소
    char                sin_zero[8]; // 사용되지 않음 

}
struct in_addr
{
    in_addr_t    s_addr;    // 32 비트 IPv4 인터넷 주소 
}

자료형 이름

자료형에 담길 정보

선언된 헤더파일

int8_t
uint8_t
int16_t
uint16_t
int32_t
uint32_t

signed 8-bit int
unsigned8-bitint(unsignedchar)
signed16-bitint
unsigned16-bitint(unsignedshort)
signed32-bitint
unsigned32-bitint(unsignedlong)

sys/types.h

sa_family_t
socklen_t

주소체계(address family)
길이정보(length of struct)

sys/socket.h

in_addr_t
i
n_port_t

IP주소정보, uint32_t로 정의되어 있음
PORT번호정보, uint16_t로 정의되어 있음

netinet/in.h

// POSIX에서 정의하고 있는 자료형이다. 

 - sin_family ; 프로토콜 체계마다 적용하는 주소체계가 다르기에 정보 전달

주소체계( Address family )  

의미 

AF_INET 
AF_INET6
AF_LOCAL

 IPv4 인터텟 프로토콜에 적용하는 주소체계 
 IPv6 인터텟 프로토콜에 적용하는 주소체계 
 로컬 통신을 위한 유닉스 프로토콜의 주소체계

- sin_port ; 16비트 PORT 번호 저장 (네트워크 바이트 순으로 저장)
- sin_addr ; 32비트 IP주소 정보 저장 (네트워크 바이트 순으로 저장)
- sin_zero ; 구조체 sockaddr_in의 크기를 구조체 sockaddr와 일치시키기 위해 삽입된 멤버(반드시 0으로 채워야 함)
- 두 번째 전달인자 (struct sockaddr*)&serv_addr
( 원래 bind()는 sockaddr 구조체 변수의 주소값을 요구하지만, sockaddr은 sockaddr_in에 비해 정보를 담기 불편하게 되어있어 sockaddr_in으로 구조체 선언해준뒤 sockaddr로 형변환을 시켜주는 것 )
struct sockaddr 
{
    sa_family_t    sin_family ;        // 주소체계 
    char             sa_data[14] ;    // 주소 정보 ; IP, PORT 번호 남은 부분은 0으로 채워줌
}
// sockaddr_in은 IPv4의 주소정보를 담기 위해 정의된 구조체 인데, 주소체계 정보를 구조체 멤버 sin_family에 저장
( sockaddr은 IPv4 주소 정보만을 담기 위해 정의된 구조체가 아님, 따라서 sockaddr과 동일한 바이트 열 구성하기위해 sin_family에 저장 )


 #include <sys/socket.h>
  int listen ( int sock, int backlog);
   -> 성공 시 0, 실패 시 -1 반환 


 - sock ; 연결요청 대기상태에 두고자 하는 소켓의 파일 디스크립터 전달, 해당 디스크립터의 소켓이 서버 소켓(리스닝)이 됨
 - backlog ; 연결 요청 큐(Queue)의 크기정보 전달, 5 가 전달 되면 큐의 크기가 5가 되어 클라이언트의 연결요청을 5개까지 대기 시킬 수 있음 
 - 연결 요청 대기상태 ; 서버 소켓과 연결요청 대기 큐가 완전히 준비되어서 클라이언트의 연결요청을 받아들일 수 있는 상태 
 - 연결 요청 대기 큐 ;  listen 함수의 두 번째 인자로 전달되는 정수의 크기에 해당하는 대기실 


 #include <sys/socket.h>
  int accept (int sockfd, struct sockaddr * , socklen_t * addrlen);
  -> 성공 시 파일 디스크립터, 실패 시 -1 반환 

 - sock ; 서버 소켓의 파일 디스크립터 전달
 - addr ; 연결요청 한 클라이언트의 주소정보를 담을 변수의 주소값, 함수 호출뒤 클라이언트 주소정보가 채워짐 
 - addrlen ; addr에 전달된 주소의 변수 크기를 바이트 단위로 전달, 함수 호출이 완료되면 크기 정보로 채워져 있던 변수에는 클라이언트의 주소정보 길이가 바이트 단위로 계산되어 채워짐
 - listen 함수 호출 이후에 클라이언트의 연결요청이 들어왔다면, 들어온 순서대로 연결요청을 수락, 연결요청을 수락한다는 것은 클라이언트와 데이터를 주고받을 수 있는 상태임을 의미  
 - 데이터를 주고 받기 위해서는 소켓이 필요한데, 서버 소켓은 문지기 역할로 사용되기에 새로운 소켓이 필요 
( 서버 소켓의 경우 전체적 맥락에서 통신을 관리해주는 느낌이고 그안에서 데이터를 송수신하기 위한 소켓이 하나 더 필요한 것 )
 - 소켓이 자동으로 생성되어, 연결요청을 한 클라이언트 소켓에 연결됨
 - 대기 큐가 비어있다면, 대기 큐가 찰 때까지 accept 함수는 반환하지 않음

 

 #include <sys/socket.h>
  int connect ( int sockfd, sturct sockaddr * serv_addr, socklen_t addrlen);
  -> 성공 시 0, 실패 시 -1 반환 
 


 - sock ; 클라이언트 소켓의 파일 디스크립터 전달 
 - servaddr ; 연결요청 할 서버의 주소정보를 담은 변수의 주소 값 전달
 - addrlen ; servaddr에 전달된 주소의 변수 크기를 바이트 단위로 전달
 - 클라이언트에 의해 connect 함수가 호출되면 다음 둘 중 한가지 상황이 되어야 함수가 반환 됨 
  1. 서버에 의해 연결요청이 접수됨 -> 클라이언트의 연결정보가 서버의 연결요청 대기 큐에 등록된 상황 
  ( connect 함수가 반환했더라도 당장에 서비스가 이뤄지지 않을 수도 있음 ) 
  2. 네트워크 단절 등 오류상황이 발생해서 연결요청이 중단됨 
// 클라이언트 소켓의 주소할당은 connect 함수가 호출될때 운영체제에서( 정확히는 커널에서 ) , IP는 컴퓨터에 할당된 IP, PORT는 임의로 선택해서 소켓에 주소 할당해서 따로 bind 함수가 필요하지 않음


# 서버/ 클라이언트 차이/ 연결 과정

왼쪽애 server 오른쪽은 client에서 일어나는 일련의 함수들 호출 과정임


1. server와 client의 역할에서의 차이점은 연결을 요청 받고, 연결을 요청 
2.   server는
 

client는   이점으로 알수있는건 server는 소켓을 2개, client는 소켓을 1개 생성해낸다는 점 

이런 점들과 밑의 과정을 연결 시켜 보자.

[Server]
    1. serv_addr에 server의 주소정보들을 저장하고,
 bind()함수에서 그 정보들을  serv_sock(소켓)에 할당해줍니다.
    2. serv_sock이 listen()을 통해 서버 소켓(리스닝 소켓)이 되고, 해당 소켓이 연결 요청 대기 상태가 됨
[Clinet]
    3. serv_addr에 server(연결 요청할)의 주소정보들을 저장하고, 따로 bind함수의 소켓에 주소할당없이도 connect함수 자체에서 소켓에 주소할당을 해주고, server쪽에서 연결을 허용(연결 대기 큐에 등록)되면 함수반환됨 ( PORT는 임의로 )
[Server]
    4. 연결 대기 큐에 등록된 요청이 있으므로 accept()을 통해 client의 소켓 정보를 받아와 clnt_sock에 주소 할당을 해주며 소켓을 생성(새로운 포트), 연결요청을 한 클라이언트 소켓과 연결됨 , serv_sock 은 연결되지 않은 상태로 계속 연결을 기다림 

+ connect()의 반환이 server-client의 완벽한 연결을 의미하는게 아니라고 위에서 말했는데, connect 함수 호출 이후 소켓에 read()/ write()을 할 때 블락되지 않으려면 서버측에서 accept()하는 이벤트를 감지 하는 방법이 필요함 또는 recv()를 사용하는 경우 블락되지않고, 수신 데이터 존재 여부를 확인 하는 방법(MSG_PEEK) 또는 select, epoll등의 해결법이 있는데 이것들은 나중에 I/O 분분과 함께 올림 

+ read(), write(), close() 함수들은 파일디스크립터와 함께 올림  


[출처 ; 윤성우-TCP/IP 소켓 프로그래밍 ]
[참고 ; URL - http://database.sarang.net/study/glibc/11.htm#8.3 ]

'Soc' 카테고리의 다른 글

[Soc] 멀티프로세스 기반 서버 구현  (0) 2013.07.19