Ch 5. 윈도우 네트워크 프로그래밍(1) - 윈도우 소켓과 관련 함수


윈도우 네트워크 프로그래밍

클라이언트 - 서버 모델

  • 서버와 클라이언트는 단일 프로그램으로 작동
  • 서버는 클라이언트의 연결요청을 대기, 클라이언트에게 정보 및 서비스를 제공
  • 클라이언트는 서버에게 정보 및 서비스를 요청하고 응답을 기다림
  • ex) 웹 사이트는 아파치와 같은 웹 서버가 서버 역할을 수행, 사용자가 사용하는 웹 브라우저(크롬, 엣지)는 클라이언트의 역할을 수행
  • 클라이언트는 서버와 동일한 컴퓨터에서 다른 프로세스로 존재할 수 있고 다른 컴퓨터에서 연결된 네트워크로 존재할 수 있음

네트워크 프로그래밍

  • 원 거리 사용자간의 원활하고 빠른 의사 소통을 위해 활용
  • 네트워크로 연결되어 있는 두 호스트 간의 데이터 송/수신이 이루어짐
  • 파일 입/출력과의 차이점은
  • 소켓(socket)을 사용한 프로그래밍 방식

소켓(socket)

  • 네트워크를 통한 입/출력을 위해 사용자에게 필요한 수단을 제공하는 응용 프로토콜 인터페이스
  • 네트워크를 경유하는 프로세스 통신의 종착점
  • 전송 계층(4계층)과 응용 계층(5계층) 사이의 인터페이스로서 응용 프로그램으로부터 데이터를 목적지 프로세스까지 캡슐화하여 전달하는 역할을 수행
  • 소켓을 활용한 네트워크 응용 프로그램을 통해 네트워크 상에서 데이터를 송/수신

TCP/UDP 소켓

  • TCP: 연결형, 신뢰성 보장을 위해 포트의 연결이 필요, 클라이언트의 연결 요청과 서버의 연결이 수행됨
  • UDP: 사용자 데이터그램 프로토콜(User Datagram Protocol)의 약자, 데이터그램으로 알려진 단순 메시지를 교환하기 위해 사용, 서버와 클라이언트는 별도의 연결 수행 없이 데이터의 송/수신이 이루어짐

좌: TCP 소켓,       우: UDP 소켓

윈도우 소켓 프로그래밍

윈도우 소켓(WinSock)

  • BSD 계열 유닉스 소켓을 참고로 설계된 윈도우 기반 소켓, 리눅스 기반 소켓과 유사
  • 윈속(윈도우 소켓)사용 방법
    • Winsock2.h 헤더 파일을 포함
    • Ws2_32.lib 라이브러리 링크
    • 윈속 사용을 위한 라이브러리 초기화 및 해제

윈도우 소켓 초기화

1
2
3
4
5
6
#include<winsock2.h>
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
//사용 예시
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
ErrorHandling("WSAStartup() error!");
  • 윈속 사용을 위한 라이브러리 초기화
  • wVersionRequested: 사용하고자 하는 윈속 버전을 명시
    • 16비트의 unsigned short인 WORD로 버전을 입력받음
    • MAKEWORD(2,2)는 윈속 버전 2.2를 의미
  • lpWSAData: WSADATA 구조체 타입 변수 포인터
  • 성공시 리턴 값 0, 실패시 에러코드값 반환

윈도우 소켓 해제

1
2
3
4
#include<winsock2.h>
int WSAClenaup(void);
//사용 예시
WSACleanup();
  • 소켓 연결이 종료되었을 경우 할당 받은 리소스를 해제하는 작업을 수행
  • 윈속 해제 이후 관련 함수의 호출이 불가능해짐
  • 성공시 리턴 값 0, 실패시 SOCKET_ERROR

소켓 주소 구조체

기본형

1
2
3
4
5
#include<sys/socket.h>
struct sockaddr{
unsigned short sa_family;
char sa_data[14];
}
  • sa_family: 주소 체계를 나타내는 변수
  • sa_data: 소켓의 주소를 나타내는 char배열, 포트 및 IP주소 정보가 담겨져 있음

AF 소켓 구조체

1
2
3
4
5
6
7
8
9
10
11
#include<sys/socket.h>
struct in_addr{
unsigned long s_addr;
}

struct sockaddr_in{
short sin_family;
short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
}
  • sin_family: 주소 체계를 나타내는 변수, AF_INET/AF_UNIX/AF_NS 등의 주소 체계가 적용되며 TCP에서는 AF_INET체계를 사용
  • sin_port: 포트번호를 나타내는 16비트 변수
  • sin_addr: IP주소를 나타내는 32비트 변수
  • sin_zero: 소켓 구조체의 크기를 16바이트로 맞추기 위해 0으로 채우는 목적의 변수

소켓 생성

1
2
3
4
5
#include<winsock2.h>
SOCKET socket (int af, int type, int protocol);
//사용 예시
SOCKET hServSock;
hServSock = socket(PF_INET,SOCK_STREAM,0);
  • 소켓을 생성하여 통신을 위한 end-point를 정의
  • af: 소켓에 사용되는 프로토콜 패밀리
    • PF_INET: IPv4 인터넷 프로토콜
    • PF_INET6: IPv6 인터넷 프로토콜
    • PF_LOCAL: 로컬 통신을 위한 UNIX 프로토콜
    • PF_PACKET: Low Level socket을 위한 인터페이스
    • PF_IPX: IPX 노벨 프로토콜
  • type: 소켓의 유형
    • SOCK_STREAM: 스트림 방식의 연결형 서비스 소켓 생성, 전달된 순서대로 수신, 전송된 모든 데이터는 에러 없이 원격지에 도달
    • SOCK_DGRAM: 데이터그램 방식의 비연결형 서비스 소켓 생성
  • protocol: 소켓에 사용되는 프로토콜 (대부분의 응용프로그램에서 0)
  • socket함수는 소켓 번호를 리턴

소켓 주소 할당

1
2
3
4
5
6
7
8
9
10
#include<winsock2.h>
int bind(SOCKET s, const struct sockaddr* name, int namelen);
//사용 예시
SOCKADDR_IN servAddr;
ZeroMemory(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(13451);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(hServSock,(SOCKADDR*)&servAddr, sizeof(servAddr))==SOCKET_ERROR)
ErrorHandling("bind error");
  • 소켓에 IP주소와 포트번호를 할당
  • 서버 프로그램에서 사용, 소켓에 포트번호와 주소를 명시하여 클라이언트가 해당 소켓에 접근할 수 있도록 함
  • name: 소켓에 할당하려는 소켓 구조체 정보
    • SOCKADDR 기본형은 sa_data 배열에 포트 및 IP주소를 모두 명시해야함
    • SOCKADDR_IN형의 객체에 포트번호와 주소를 명시하고 SOCKADDR형으로 캐스팅하여 사용함으로서 가독성을 높일 수 있음
  • namelen: 소켓 구조체 크기
  • 성공시 리턴 값 0, 실패시 SOCKET_ERROR(-1)

소켓 연결대기

1
2
3
4
5
#include<winsock2.h>
int listen(SOCKET s,int backlog);
//사용 예시
if(listen(hServSock,5)==SOCKET_ERROR)
ErrorHandling("listen error");
  • 서버는 클라이언트로부터 연결 요청을 기다리기 위해 포트를 열어두어야함
  • backlog: 접속 요청을 동시에 받아들일 수 있는 큐의 크기
    • listen상태의 소켓은 여러 클라이언트로부터 연결 요청을 받을 수 있음
  • 성공시 리턴 값 0, 실패시 SOCKET_ERROR(-1)

소켓 연결

1
2
3
4
5
#include<winsock2.h>
int connect(SOCKET s, const struct sockaddr *name, int namelen);
//사용 예시
if(connect(hSocket,(SOCKADDR*)&servAddr,sizeof(servAddr))==SOCKET_ERROR)
ErrorHandling("connect error");
  • 클라이언트에서 사용하는 함수로 listen상태의 서버 소켓에 연결을 요청
  • 클라이언트는 미리 알아둔 서버의 IP주소 및 포트번호가 포함되어 있는 소켓 구조체를 생성하여 함수에 사용
  • 성공시 리턴 값 0, 실패시 SOCKET_ERROR(-1)

소켓 연결 수락

1
2
3
4
#include<winsock2.h>
SOCKET accept(SOCKET s,struct sockaddr *addr,int* addrlen);
//사용 예시
SOCKET hCliSock = accept(hServSock,(SOCKADDR*)&cliAddr,&szCliAddr);
  • 서버에서 사용하는 함수, 클라이언트로부터 연결 요청을 받은 listen 상태의 소켓은 해당 클라이언트와의 연결을 담당하는 새로운 소켓을 생성
  • 기존 listen 상태의 소켓은 다른 클라이언트로부터의 연결 요청을 대기함
  • 성공시 리턴 값은 생성된 소켓 번호, 실패시 SOCKET_ERROR(-1)

데이터 송/수신

1
2
3
4
5
6
7
8
#include<winsock2.h>
int send(SOCKET s,const char* buf, int len, int flags);
int recv(SOCKET s,char *buf, int len, int flags);

//사용 예시
char msg[] = "msg"
send(hCliSock,msg,suzeof(msg),0);
int strlen = recv(hCliSock,msg,sizeof(msg)-1,0);
  • 데이터를 송/수신하는 함수
  • recv함수의 len은 수신할 수 있는 최대 바이트 수
  • 데이터 수신시 len을 sizeof(msg)-1로 하는 이유는 배열이 0부터 시작하기 때문.
  • 성공시 리턴값은 송/수신된 바이트 수, 실패시 SOCKET_ERROR(-1)

소켓 종료

1
2
3
4
5
#include<winsock2.h>
int closesocket(SOCKET s);
//사용 예시
closesocket(hCliSock);
closesocket(hServSock);
  • 소켓을 종료하는 함수
  • 생성된 소켓은 반드시 프로그램 종료 전에 소켓을 종료해야함

댓글