2013. 8. 16. 11:15ㆍSoc
|
소켓(Socket) |
소켓 |
소켓(socket) ;서로다른 두 컴퓨터가 데이터를 주고받기위해, 운영체제에서 제공하는 통신 방법
- 서로 연락하기위해 주소를 할당해주어야 함 (IP, PORT)
- 연결당한는 쪽(server)과 연결하려는 쪽(client)로 나뉘어짐
- 기본 적인 소켓 연결 과정을 나타낸 그림, 네모 칸의 함수들이 사용됨
- 대략적인 함수 사용 용도
SERVER 1. socket() ; 소켓을 생성 |
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> |
- domain ; 소켓이 사용할 프로토콜 체계(Protocol Family) 정보 전달
이름 |
프로토콜 체계(Protodol Family) |
PF_INET |
IPv4 인터넷 프로토콜 체계 |
// 소켓 통신에 사용되는 프로토콜도 부류가 나뉘기에 정보 전달 (주로 사용되어짐)
- type ; 소켓의 데이터 전송방식에 대한 정보 전달
연결 지향형 소켓(SOCK_STREAM) |
1. 중간에 데이터가 소멸되지 않고 목적지로 전송됨 |
비 연결 지향형 소켓 (SOCK_DGRAM) |
1. 전송된 순서에 상관없이 가장 빠른 전송을 지향 |
//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> |
- 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 |
signed 8-bit int |
sys/types.h |
sa_family_t |
주소체계(address family) |
sys/socket.h |
in_addr_t |
IP주소정보, uint32_t로 정의되어 있음 |
netinet/in.h |
// POSIX에서 정의하고 있는 자료형이다.
- sin_family ; 프로토콜 체계마다 적용하는 주소체계가 다르기에 정보 전달
주소체계( Address family ) |
의미 |
AF_INET |
IPv4 인터텟 프로토콜에 적용하는 주소체계 |
- 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> |
- sock ; 연결요청 대기상태에 두고자 하는 소켓의 파일 디스크립터 전달, 해당 디스크립터의 소켓이 서버 소켓(리스닝)이 됨
- backlog ; 연결 요청 큐(Queue)의 크기정보 전달, 5 가 전달 되면 큐의 크기가 5가 되어 클라이언트의 연결요청을 5개까지 대기 시킬 수 있음
- 연결 요청 대기상태 ; 서버 소켓과 연결요청 대기 큐가 완전히 준비되어서 클라이언트의 연결요청을 받아들일 수 있는 상태
- 연결 요청 대기 큐 ; listen 함수의 두 번째 인자로 전달되는 정수의 크기에 해당하는 대기실
#include <sys/socket.h> |
- sock ; 서버 소켓의 파일 디스크립터 전달
- addr ; 연결요청 한 클라이언트의 주소정보를 담을 변수의 주소값, 함수 호출뒤 클라이언트 주소정보가 채워짐
- addrlen ; addr에 전달된 주소의 변수 크기를 바이트 단위로 전달, 함수 호출이 완료되면 크기 정보로 채워져 있던 변수에는 클라이언트의 주소정보 길이가 바이트 단위로 계산되어 채워짐
- listen 함수 호출 이후에 클라이언트의 연결요청이 들어왔다면, 들어온 순서대로 연결요청을 수락, 연결요청을 수락한다는 것은 클라이언트와 데이터를 주고받을 수 있는 상태임을 의미
- 데이터를 주고 받기 위해서는 소켓이 필요한데, 서버 소켓은 문지기 역할로 사용되기에 새로운 소켓이 필요
( 서버 소켓의 경우 전체적 맥락에서 통신을 관리해주는 느낌이고 그안에서 데이터를 송수신하기 위한 소켓이 하나 더 필요한 것 )
- 소켓이 자동으로 생성되어, 연결요청을 한 클라이언트 소켓에 연결됨
- 대기 큐가 비어있다면, 대기 큐가 찰 때까지 accept 함수는 반환하지 않음
#include <sys/socket.h> |
- 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 |
---|