#include <iostream>
#include <WS2tcpip.h>
#include <future>
#pragma comment(lib, "ws2_32")
#define PORT 28888
#define PACKET_SIZE 36
#define SERVER_IP "127.0.0.1"
using std::cout;
using std::endl;
void server()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET hListen;
hListen = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN tListenAddr;
tListenAddr.sin_family = AF_INET;
tListenAddr.sin_port = htons(PORT);
tListenAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(hListen, (SOCKADDR*)&tListenAddr, sizeof(tListenAddr));
listen(hListen, SOMAXCONN); // conenction 을 대기하는 형태로 소켓의 상태 변경.
SOCKADDR_IN tCIntAddr; // 클라이언트 측에서 접속하면 해당 클라이언트의 정보를 저장할 구조체이다.
int iCIntSize = sizeof(tCIntAddr);
cout << "server accept start" << endl;
SOCKET hClient = accept(hListen, (SOCKADDR*)&tCIntAddr, &iCIntSize); // 소켓, accept 할 client 주소정보 Struct, Struct SizeOf 를 이용해
// 접근한 Client 를 식별하고, 허용한다.
// 해당 accept 함수는 '동기' 형태이므로
// 클라이언트가 접근하지 않는다면 계속 유지.
static char cBuffer[PACKET_SIZE];
cout << "server start recv " << endl;
if (recv(hClient, cBuffer, PACKET_SIZE, 0) == SOCKET_ERROR)
{
cout << "server error recv" << endl;
}
cout << "server end recv : " << cBuffer << endl;
static char cMsg[] = "Server MSG";
cout << "server start send" << endl;
send(hClient, cMsg, strlen(cMsg), 0);
cout << "server end send" << endl;
closesocket(hClient);
closesocket(hListen);
WSACleanup();
}
void client()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET hSocket;
hSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN tAddr;
tAddr.sin_family = AF_INET;
tAddr.sin_port = htons(PORT);
inet_pton(AF_INET, SERVER_IP, &(tAddr.sin_addr.s_addr)); // 취약한 inet_addr 함수 대신 inet_pton 함수 사용.
// inet_addr 은 255.255.255.255 주소 리턴값이 INADDR_NONE 타입과 같아
// 구분 불가능한 문제 존재.
char temp_;
cout << "client input" << endl;
std::cin >> temp_;
connect(hSocket, (SOCKADDR*)&tAddr, sizeof(tAddr)); // Client 에서는 Bind 가 아닌 connect 함수를 이용,
// SOCKET에 SOCKADDR_IN 을 매칭하게 함.
// 해당 코드에서는 같은 IP / Port 를 이용하였으므로
// 일종의 LAND Attack 과 같은 통신이 됨.
static char cMsg[] = "client MSG";
cout << "client start send" << endl;
send(hSocket, cMsg, strlen(cMsg), 0);
cout << "client end send" << endl;
static char cBuffer[PACKET_SIZE];
if (recv(hSocket, cBuffer, PACKET_SIZE, 0) == SOCKET_ERROR)
{
cout << "client recv Error" << endl;
return;
};
cout << "client recv : " << cBuffer << endl;
closesocket(hSocket);
WSACleanup();
}
int main()
{
auto O_server = std::async(server);
auto O_client = std::async(client);
};
해당 코드는 https://kevinthegrey.tistory.com/26 을 참고하여 변형하였다.
우선, TCP, UDP 등 통신관련된 코딩은 웹에 좋은 예제들이 많이 있으므로
이를 참고하는게 더 좋다고 생각한다. 굉장히 잘 작성한 글들이고, 덧붙일 설명이 없다
바퀴의 재생산은 필요 없다 생각하는 주의이기에....
해당 글은 필자가 간단하게 생각을 정리하기 위해 작성한 글이다.
해당 코드는 다음과 같이 동작한다.
O_server, O_client 가 작동함과 동시에
O_server 의 경우는
소켓 만들고 → bind → listen
으로 클라이언트의 connection 을 대기하는 형태로까지 만든 뒤,
cout << "server Accept Start" << endl;
메세지를 출력 후
accept() 를 통해 client 의 접속 요청이 들어올 때 까지 기다린다.
accept() 는 TCP 전용이며 UDP 에서는 필요하지 않다.
이 때 다른 thread 에서 실행된 O_client 에서는
마찬가지로
소켓 만들고 → connect (서버단이랑 (ip, port 가 같다)) → listen 한다.
여기까지 출력 메세지가 다음과 같다.
이 후에 메세지를 입력시
이와 같이 메세지가 뜨는데
순서대로 접근 해 보면.
O_server 의 경우는 accept 에서 대기중인 상태이므로,
O_client 에서 무언가 신호를 전송 해 주어야 한다.
따라서 "Send Client1 start" 가 출력되고,
그 이후에 send() 함수를 통해 Server 측으로 메세지를 전달하므로,
accept 가 풀린 server 의 "server Recv Start" 메세지가 출력되게 되며,
그리고 "server Recv End : 메세지" 가 출력되며
이 후 동작에서 server 가 send 로 client 에게 메세지를 전달하므로, "Server Send start" 메세지가 출력된다.
그 후에 Client Recv 에서 Server 가 보낸 메세지를 받게 되고
"Server Send End" 메세지를 끝으로 WSACleanup() 함수를 호출하여 코드가 종료되게 된다.
위 코드가
이와 같은 일련의 소켓 프로그래밍의 과정이다.
비록 위 이미지는 PHP 의 예제이지만, 일련의 과정은 C++ 의 그것과 크게 다를바가 없다
왜냐면 '프로토콜' 이니까
모든 운영체제에서 동일하게 동작하고,
모든 프로그래밍 언어에서 동일하게 수행된다.
물론 언어마다 wrap 되어있는 형태가 다를 순 있지만 근본(어셈블리) 형태는 같을것이다.
'Programming > C++' 카테고리의 다른 글
[C++] C# 측으로 Struct 를 UDP 로 전송 (0) | 2021.04.15 |
---|---|
[C++] OpenCV digit_svm 예제 간단 분석 (0) | 2021.03.31 |
[C++] 람다식 mutable 의 쓰임세. (0) | 2020.12.07 |
[C++] push_back vs emplace_back 차이점 (2) | 2020.12.07 |
[C++] C4819 Warning 해결 방법, 한글 깨짐 문제 (0) | 2020.12.03 |