티스토리 뷰
윈속으로 개발하기 위한 준비
일반적으로 WinSock2.h를 include 해야 한다.
WinSock2.h로 프로그래밍을 하기 위해서는 ws2_32.lib를 추가해줘야 한다.
추가는 프로젝트 속성 -> 링커 -> 입력 -> 추가 종속성에 ws2_32.lib를 추가
int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);
wVersionRequested : 로드하기를 원하는 윈속 라이브러리 버전을 지정 MAKEWORD(x,y) 매크로 사용
lpWSAData : LPWSADATA의 포인터, 로드된 라이브러리에 대한 정보가 리턴
typedef struct WSAData
{
WORD wVersion; //앞으로 사용하게 될 윈속 버전
WORD wHighVersion; //윈속 라이브러리의 가장 높은 버전
char szDescription[WSADESCRIPTION_LEN+1]; //중요하지 않다
char szSystemStatus[WSASYS_STATUS_LEN+1]; //중요하지 않다
unsigned short iMaxSockets; //이용하지 않는게 좋다
unsigned short iMaxUdpDg; //이용하지 않는게 좋다
char FAR * ipVendorInfo; //윈도우 플랫폼에서는 사용되지 않는다.
}WSADATA, * LPWSADATA;
윈속 인터페이스의 사용이 끝났을 때 WSACleanup 함수 호출하기
int WSACleanup(void);
WSAStartup이 호출된 만큼 위 함수도 호출해야 한다.
에러 검출 및 처리
대부분의 윈속 함수는 에러 발생하면 SOCKET_ERROR를 리턴한다. 실제 값은 -1
에러를 리턴 받았다면 WSAGetLastError 함수를 호출하여 어떤 원인에 의하여 에러가 발생되었는지 알 수 있다.
int WSAGetLastError(void);
이 함수를 통해 얻은 정수값은 에러에 대한 코드값이다. 이 코드값들은 WinSock2.h에 정의되어 있다.
//위 과정을 코드로 설명
#include <iostream>
#include <WinSock2.h>
using namespace std;
int main()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
cout << "WSAStartUp Failed\n";
return 0;
}
if (WSACleanup() == SOCKET_ERROR)
{
cout << "WSACleanUp FAIL\n";
}
return 0;
}
sockaddr 구조체
소켓의 주소를 담는 기본 구조체 역할.
소켓 주소란 주소 체계(address family), IP 주소, 포트 번호 이 3가지 정보를 묶어서 부르는 말이다.
struct sockaddr{
u_short sa_family;
char sa_data[14];
};
sa_family : 주소체계를 구분하기 위한 변수.
sa_data : 실제 주소를 저장하기 위한 변수 // IP + Port number
IPv4의 주소 지정
주소로 32bit를 사용. 클라이언트가 TCP나 UDP를 이용해 서버와 통신하기를 원할 때 포트와 함께 IP주소를 지정,
또한 서버가 클라이언트로부터 들어오는 요청을 수신하기 위해서도 IP주소와 포트를 지정해야 한다.
윈속에서 IP주소와 포트는 SOCKADDR_IN 구조체를 이용하여 지정
sockaddr을 그대로 사용할 경우, sa_data에 IP주소와 Port번호가 조합되어 있어 쓰거나 읽기가 불편하다
그래서 SOCKADDR_IN을 사용한다.
struct sockaddr_in
{
short sin_family; // 주소 체계
u_short sin_port; // 16비트 포트 번호
struct in_addr sin_addr; // 32비트 ip 주소
char sin_zero[9]; // 전체 크기를 16비트로 맞추기 위한 dummy
};
struct in_addr{
u_long s_addr; // 32비트 IP 주소를 저장 할 구조체
};
sin_family : IP를 사용할 경우 반드시 AF_INET으로 설정
sin_port : 통신하기를 원하는 서버측의 UDP/TCP 포트, 주의점은 well-known-port를 사용하지 말자,
sin_addr : IPv4의 주소를 담는다. 상황에 따라 자신/상대방의 주소를 표시.
인터넷 표준인 점으로 구분된 형식("255.255.255.0")
각각의 문자는 한 바이트 크기의 숫자로 표시, 그리고 왼쪽에서 오른쪽으로 배열된 4바이트
unsigned long integer가 된다
ex) 10진수 "255.255.255.0"이 1byte씩 숫자로 바꾸면 0xff.0xff.0xff.0x00이 되어 4byte의 0xffffff00이 된다
sin_zero : sockaddr_in의 크기를 SOCKADDR과 같은 크기로 만들기 위한 패딩 필드(빈공간),
구조체의 전체 크기를 16비트로 맞춘 것
점으로 구분된 주소를 32bit 정수형으로 바꾸는 함수
unsigned long inet_addr(
const char FAR *cp
);
cp필드는 NULL로 끝나는 문자열로 점으로 구분된 주소를 의미한다.
이 함수는 주소에 해당하는 네트워크 바이트 순서로 된 32bit 정수형 형식의 주소를 리턴한다.
위에서 설명한 sockadder_in을 이용한 IPv4형식의 주소 지정 방법
SOCKADDR_IN InternetAddr;
int nPortId = 5150;
InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.S_un.S_addr = inet_addr("136.149.3.29");
InternetAddr.sin_port = htons(nPortId);
소켓 생성
SOCKET socket {
int af,
int type,
int protocol
};
af : 프로토콜의 어드레스 패밀리(address family)를 의미한다. / AF-INET
type : 프로토콜의 소켓 타입을 의미. TCP/IP 기반은 SOCK_STREAM, UDP/IP기반은 SOCK_DGRAM
protocol : 프로토콜 지정, 동일한 af에 대해 여러 개의 프로토콜이 존재할 경우 사용 IPPROTO_TCP/IPPROTO_UDP
연결지향 통신
IP에서 연결지향 통신은 TCP/IP를 통해서 이루어진다.
TCP는 신뢰성 있는 데이터 전송을 제공한다. 응용프로그램이 TCP를 통해 통신하면
두 개의 지점간 가상 연결이 이루어지며 양방향으로 스트림 방식의 통신이 가능해진다.
Server
서버는 클라이언트의 요청에 응답하기 위해 클라이언트의 연결을 대기.
클라이언트가 접속 가능한 주소로 연결을 기다려야 한다. 이런 주소는 IP주소와 포트 번호를 이용한다
첫번째. socket이나 WSASocket으로 소켓 생성
두번째. 생성된 소켓에 접속 가능한 주소로 바인드(bind)
세번째. 바인드된 소켓을 접속 대기 상태(listen)으로 만듦
네번째. 클라이언트의 접속을 수락(accept)
binding
int bind(
SOCKET s,
const struct sockaddr FAR* name,
int namelen
);
s : 클라이언트의 연결을 기다릴 소켓
name : sockaddr의 포인터로 프로토콜에 해당하는 구조체에 값을 채운 후 sockaddr의 포인터로 형변환하여 입력
namelen : 두번째 파라미터로 입력된 프로토콜의 구조체의 크기를 입력
아래 코드는 TCP연결을 위한 바인딩 작업을 어떻게 하는지 보여준다.
#include <iostream>
#include <WinSock2.h>
using namespace std;
int main()
{
SOCKET S;
SOCKADDR_IN tcpaddr;
int port = 5150;
S = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
tcpaddr.sin_family = AF_INET;
tcpaddr.sin_port = htons(port);
tcpaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
bind(S, (SOCKADDR*)&tcpaddr, sizeof(tcpaddr));
}
bind를 호출할 때 에러가 발생되면 SOCK_ERROR를 리턴하게 된다.
일반적인 에러는 WSAEADDRINUSE이다. 이미 다른 소켓에 바인드되었거나 해당 포트번호가 TIME_WAIT 상태일 경우
발생하게 된다. 또한 이미 바인드된 소켓에 대해 또 bind를 호출하면 WSAEFAULT 에러가 발생한다.
listening(연결 대기)
bind는 소켓에 주소를 할당했을 뿐이다. 소켓이 연결을 기다릴 수 있도록 하는 것은 listen이다.
int listen(
SOCKET s,
int backlog
);
s : 바인드된 소켓
backlog : 들어오는 연결을 대기시킬 수 있는 최대 개수
여러 개의 연결이 동시에 들어올 때 중요하다.
예를 들어 backlog가 2이면 같은 시간에 3개의 연결이 시도되었을 때
먼저 들어온 2개의 연결요청은 대기 큐에 들어가서 정상적으로 처리되지만
나머지 하나는 WSAE-CONNREFUSE에러를 발생시키며 실패한다.
backlog의 한계치는 프로토콜 제공자가 정하게 되어 있다. 정확한 값을 찾는 함수는 제공되지 않음
listen에서 발생하는 에러는 WSAEINVAL로 bind하기전 listen을 호출했을 때 발생한다.
accepting Connections(연결 수락)
SOCKET accept(
SOCEKT s,
struct sockaddr *FAR addr,
int FAR* addrlen
);
s : 연결 대기(listening mode)에 있는 소켓
addr : 구조체 SOCKADDR_IN의 주소
addrlen : SOCKADDR_IN의 길이의 주소값
다른 프로토콜의 소켓에서는 SOCKADDR_IN이 해당 프로토콜에 맞는 구조체 SOCKADDR로 대체되어야 한다.
accep의 호출은 대기 큐에 있는 첫번째 연결 요청을 처리한다.
함수가 호출되면 addr은 연결을 요청한 클라이언트의 IPv4 주소 정보로 채워진다.
그리고 addrlen에는 addr의 구조체 크기가 리턴된다.
또한 accept 함수는 새로운 socket을 return하게 되는데 이 socket은 클라이언트의 연결에 해당하는 새로운 소켓이다.
원래 소켓은 계속 연결 대기 모드로 남아서 다른 연결을 기다리게 된다.
accept에서 에러 발생 시 INVALID_SOCKET을 리턴한다.
일반적인 에러는 WSA-EWOULDBLOCK으로 연결 대기중인 소켓이 비동기이거나 넌블록모드이고
들어온 연결이 없을 경우 발생된다.
Client
클라이언트는 서버보다 더 간단하고 단순한 과정으로 이루어진다.
첫번째. 소켓을 생성한다.
두번째. SOCKADDR 구조체를 연결하고자 하는 서비스에 알맞은 서비스 이름으로 채운다.(프로토콜 기반)
TCP/IP 프로토콜에서 서비스 이름은 서버의 IP와 포트번호를 의미한다.
세번째. connect나 WSAConnect를 호출하여 연결을 시도한다.
connect
int connect(
SOCKET S,
const struct sockaddr FAR* name,
int namelen
);
s : 연결하길 원하는 소켓
name : 연결할 서버의 주소를 담을 구조체(sockaddr_in)
namelen : name 변수의 크기
connect에서 발생하는 에러
포트에 연결을 기다리는 프로세스가 없다면 connect는 WSAE-CONNREFUSED를 리턴하며 실패한다.
또한 연결하고자 하는 목적지로 연결이 불가능한 상태라면 WSAETIMEDOUT이 발생한다.
- Total
- Today
- Yesterday
- 조건문
- TAG
- 선택자
- 언리얼엔진
- Link
- 정렬
- css
- PHP&MySQL
- 네트워크 프로그래밍
- 언리얼엔진4
- 생활코딩
- 생활코딩#동영상을#글로#html
- 글로
- GRID
- 문자열
- visual studio code
- 동영상을
- 관계형데이터베이스
- 안드로이드 스튜디오
- 생활코딩#MySQL
- 알고리즘
- php
- C언어
- HTML
- 객체
- 차이점
- inline
- javascript
- 변수
- 기초
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |