Client-Server 매커니즘
가정 : 서로 다른 네트워크 머신 (M1, M2) 가 상호 소통하는 프로그램을 만들고자 한다. 한 사람이 M1에서 첫번재 프로그램을 시작하면 이 프로그램은 M2에 있는 프로그램으로 메시지를 보낸다. 컴퓨터는 사람보다 빠르기 때문에 M1의 프로그램은 M2로 부터 응답을 받는데 몇 ms 밖에 걸리지 않는다. 두번째 프로그램은 M1으로부터 메시지를 받는데 몇 ms가 걸리고, 첫번째 프로그램이 아직 활성화가 되지 않았다고 결론을 내고 에러 메시지를 보여주고 나간다. 그럼 한쪽은 계속 프로그램을 실행시키더라도 상호소통할 수 없다.
Socket
소캣의 개념
- 호스트의 네트워크 스택에 연결된 어플리케이션을 위한 인터페이스
소켓 API
- 32-bit 노드 주소 IP address
- 16-bit 포트 넘버
소켓 종류
- SOCK_STREAM : TCP, 신뢰 가능한 전달, 순서 보장, 연결 지향, 양방향 연결
- SOCK_DGRAM : UDP, 신뢰 불가능, 순서 보장 X, 받거나 전달할 수 없고 양방향 소통은 불가능
TCP vs. UDP
- TCP : 용량 큰 데이터를 전달에 사용, 지속적 연결
- UDP : 빠른 lookup, 하나의 query-response에 사용
소켓 프로그래밍 절차
socket creation with socket()
int sockid = socket(family, type, protocol);
/*
sockid : 소켓 구분자, integer
famliy : integer, 커뮤니케이션 도메인
PF_INET (IPv4, 인터넷 주소) PF_UNIX(로컬 소통, 파일 주소)
type : SOCK_STREAM, SOCK_DGRAM
protocol: IPPROTO_TCP, IPPROTO_UDP, 보통 0/ 디폴트로 세팅
실패 시 -1 반환
데이터의 목적지나 출발지를 특정하는 콜이 아님
*/
socket close()
status = close(sockid);
/*
성공 0, 실패 -1 반환
스트림 소켓의 경우 연결이 끊기고 포트 넘버를 해제
*/
specifying address
struct sockaddr {
unsigned short sa_famliy // address family
char sa_data[14]; // famliy-specific address information
}
struct in_addr {
unsigned long s_addr; // 32bit internet addr
}
struct sockaddr_in {
unsigned short sin_family // internet protocol
unsigned short sin_port; // address port (16bits)
struct in_addr sin_addr; // internet addr (32bits)
char sin_zero[8]; // not used
}
assign address to socket with bind()
int status = bind(sockid, &addrport, size);
/*
sockid : int
addrport : sockaddr 구조체, ip 주소, 포트넘버
size : byte단위로 addrport 구조체크기
실패시 -1 반환
*/
int sockid;
struct sockaddr_in addrport;
sockid = socket(PF_INET, SOCK_STREAM, 0);
addrport.sin_family = AF_INET;
addrport.sin_port = htons(5100);
addrport.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sockid, (struct sockaddr *) &addrport, sizeof(addrport)) != -1 ) {
...
}
- bind는 두 소켓 타입 모두 스킵 가능
- datagram 소켓 보내기만 하는건 바인드 필요 없고, 받는건 필요함
- stream 소켓은 연결 셋팅하는 중에 목적지가 결정됨 그래서 수신지의 포트를 알 필요 없음
listen()
int status = listen(sockid, queueList);
/*
sockid : integer
queueList: integer, 연결을 기다릴 수 있는 활성화된 참가자들의 수
listen하고 있으면 0, 에러시 -1 반환
새로운 소켓을 얻기 위해 서버만! 사용
*/
establish connection with connect()
int status = connect(sockid, &foreignAddr, addrlen);
/*
sockid
foreignAddr : sockAddr 구조체, 수동적 참가자의 주소
addrlen : 정수, name의 크기
성공시 0 실패시 -1 반환
blocking
*/
accept () : 서버가 들어오는 클라이언트 연결을 위한 소켓을 얻기 위해 호출하는 함수
int s = accept(sockid, &clientAddr, &addrLen);
/*
s : 정수, 새로운 소켓, 데이터 전달을 위해 사용하는 소켓
sockid : 기존에 listen되고 있는 소켓
clienAddr : sockAddr 구조체, 활성화된 참자가의 주소
addrLen : clientAddr의 크기,
blocking - 반환되기 전에 연결을 기다려야 함
소켓 큐에서 다음 연결을 dequeue
*/
exchange data with stream socket : send(), recv()
int count = send(sockid, msg, msgLen, flags);
/*
msg: const void[], 전달할 메시지
msgLen : 정수, 전달할 메시지 길이 (바이트)
flags : 정수, 옵션, 디폴트는 0
count : 전달된 바이트 수 / 실패시 -1 반환
*/
int count = recv(sockid, recvBuf, bufLen, flags);
/*
recvBuf : void[], 받은 바이트를 저장
bufLen : 받은 바이트 수
flags : 정수, 옵션 값, 디폴트 0
count : 받은 바이트 값, 실패시 -1
*/
// 오직 데이터가 전달되거나 받은 이후에만 반환 (blocking)
exchange data with datagram socket : send(), recv()
int count = sendto(sockid, msg, msgLen, flags, &foreignAddr, addrlen);
/*
foreignAddr : sockaddr 구조체, 목적지 주소
addrLen : foreignAddr의 크기
*/
int count = recvfrom(sockid, secvDub, dufLen, flags, &clientAddr, addrlen);
/*
clientAddr : 클라이언트 주소
*/
Windows socket programming
- WinSock 2 라이브러리 사용
- 독립적인 전송 인터페이스 제공하는 프로토콜, 실시간 멀티미디어 소통이 가능한 네트워킹 서비스
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
WSADA wsadata;
int err;
err = WSAStartup(WSVERS, &wsadata);
if (err != 0) {
WSACleanup();
printf("WSAStartup failed with an error: %d\n", err);
}
Practice example
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
Stream socket
Server
1. create a TCP socket
2. assign a port to socket
3. set socket to listen
4. accept new connection
5. communicate
6. close connection
7. repeat 4 5 6
Client
1. create a TCP socket
2. establish connection
3. communicate
4. cloase the connection
Datagram socket
자료 참고
'프로그래밍 언어 > C' 카테고리의 다른 글
자료구조/C) Tree 트리 구현 (0) | 2021.04.09 |
---|---|
[C/Error] 'for' loop initial declarations are only allowed in C99 or C11 mode[Note] use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code : for loop 에러 문제 해결 (0) | 2021.03.30 |
C/C++ programming tutorial (0) | 2021.03.02 |
Hash Table : open addressing (0) | 2021.02.15 |
분할 정복 Divide and Conquer 알고리즘으로 행렬 곱셈 matrix multiplication 풀기 (0) | 2021.02.15 |