IPv4에서 범위를 초과한 ip를 위해 생긴 IPv6
흐름/혼잡 제어
sendBuffer에 데이터를 저장하고 송신한 후, recvBuffer에서 데이터를 정확하게 수신한다는 보장이 없다.
100바이트의 데이터를 송신하여도 100바이트로 받지 않을 수 있다.
Client
소켓 준비 - 서버 연결
Server
소켓 준비 - Bind - Listen - Accept
기본 헤더
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
/*
af: Address Family (AF_INET = IPv4, AF_INTE6 = IPv6)
type: TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
protocol: 0
return: descriptor
*/
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-socket
socket 함수(winsock2.h) - Win32 apps
소켓 함수는 특정 전송 서비스 공급자에 바인딩된 소켓을 만듭니다.
learn.microsoft.com
serverAddr.sin_port = htons(PORT);
/*
host to network short
Little-Endian vs Big-Endian
little: [0x78][0x56][0x34][0x12]
big: [0x12][0x34][0x56][0x78] -> network 표준
*/
MSDN 홈페이지에서 함수 예제코드를 볼 수 있다.
Client
#include "pch.h"
#include <iostream>
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
// 1. Socket 초기화
WSAData wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET)
{
int32 errCode = WSAGetLastError();
cout << "Socket ErrorCode: " << errCode << endl;
return 0;
}
// 2. IP, PORT 설정
char IP[] = "127.0.0.1";
u_short PORT = 7777;
SOCKADDR_IN serverAddr; // IPv4
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
//serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); <- deprecated
inet_pton(AF_INET, IP, &serverAddr.sin_addr);
serverAddr.sin_port = htons(PORT);
// 3. Socket 연결
if (connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errCode = WSAGetLastError();
cout << "Socket ErrorCode: " << errCode << endl;
return 0;
}
cout << "Connected To Server!" << endl;
// 4. 데이터 송수신
while (true)
{
char sendBuffer[100] = "Hello World!";
int32 resultCode = send(clientSocket, sendBuffer, sizeof(sendBuffer), 0);
if (resultCode == SOCKET_ERROR)
{
int32 errCode = WSAGetLastError();
cout << "Send ErrorCode: " << errCode << endl;
return 0;
}
cout << "Send Data! Len = " << sizeof(sendBuffer) << endl;
// Echo Receiver
char recvBuffer[1000];
int32 recvLen = recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
if (recvLen <= 0)
{
int32 errCode = WSAGetLastError();
cout << "Recv ErrorCode: " << errCode << endl;
return 0;
}
cout << "Recv Data! Len = " << recvLen << endl;
cout << "Recv Data! Data = " << recvBuffer << endl;
this_thread::sleep_for(1s);
}
// 5. Socket 종료
closesocket(clientSocket);
WSACleanup();
}
Server
#include "pch.h"
#include "CorePch.h"
#include "CoreMacro.h"
#include "ThreadManager.h"
#include <iostream>
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSAData wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET)
{
int32 errCode = WSAGetLastError();
cout << "Socket ErrorCode: " << errCode << endl;
return 0;
}
u_short PORT = 7777;
SOCKADDR_IN serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(PORT);
// bind
if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errCode = WSAGetLastError();
cout << "Bind ErrorCode: " << errCode << endl;
return 0;
}
// listen
int32 MAX_LIMIT_SOCKET = 10;
if (listen(listenSocket, MAX_LIMIT_SOCKET) == SOCKET_ERROR)
{
int32 errCode = WSAGetLastError();
cout << "Listen ErrorCode: " << errCode << endl;
return 0;
}
while (true)
{
SOCKADDR_IN clientAddr;
memset(&clientAddr, 0, sizeof(clientAddr));
int32 addrLen = sizeof(clientAddr);
SOCKET clientSocket = accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);
if (clientSocket == INVALID_SOCKET)
{
int32 errCode = WSAGetLastError();
cout << "Accept ErrorCode: " << errCode << endl;
return 0;
}
// 연결 성공
char ipAddress[16];
inet_ntop(AF_INET, &clientAddr.sin_addr, ipAddress, sizeof(ipAddress));
cout << "Client Connected! IP: " << ipAddress << endl;
while (true)
{
char recvBuffer[1000];
int32 recvLen = recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
if (recvLen <= 0)
{
int32 errCode = WSAGetLastError();
cout << "Recv ErrorCode: " << errCode << endl;
return 0;
}
cout << "Recv Data! Len = " << recvLen << endl;
cout << "Recv Data! Data = " << recvBuffer << endl;
// Echo Server
int32 resultCode = send(clientSocket, recvBuffer, recvLen, 0);
if (resultCode == SOCKET_ERROR)
{
int32 errCode = WSAGetLastError();
cout << "Send ErrorCode: " << errCode << endl;
return 0;
}
}
}
WSACleanup();
}
블로킹 함수
블로킹 함수는 특정 조건을 만족할 때까지 대기하는 함수이다.
TCP 통신에서 데이터를 송수신하는 send, recv 함수는 블로킹 함수이다.
CPU를 소모하는 대기상태가 아닌, 소유권을 커널에 넘겨주고(컨텍스트 스위칭) 대기하는 condition_variable처럼 sleep과 비슷한 대기상태이다.
send | recv |
sendBuffer에 데이터를 저장하는 역할 | recvBuffer에서 데이터를 가져오는 역할 |
sendBuffer가 데이터를 저장할 공간이 부족하면 저장할 때까지 대기한다. | recvBuffer에서 가져올 데이터가 없으면 데이터를 가져올 때가지 대기한다. |
Client - Server간 데이터의 크기 차이
// client
char sendBuffer[100] = "Hello World!";
int32 resultCode = send(clientSocket, sendBuffer, sizeof(sendBuffer), 0);
cout << "Send Data! Len = " << sizeof(sendBuffer) << endl;
// sizeof(sendBuffer) = 100
// server
char recvBuffer[1000];
int32 recvLen = recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
cout << "Recv Data! Len = " << recvLen << endl;
// recvBufferLen = 1000
위 코드처럼 클라이언트와 서버간의 다루는 동일한 데이터의 크기가 다를 수 있다.
서로 SendBuffer와 RecvBuffer의 크기가 다르기 때문이다.
VS 시작 설정
'Server > C++' 카테고리의 다른 글
UDP 소켓 프로그래밍 (0) | 2024.08.06 |
---|---|
TCP vs UDP (0) | 2024.08.06 |
TypeCase (0) | 2024.08.06 |
메모리 풀링 (0) | 2024.08.05 |
STL Allocator (0) | 2024.08.05 |