Client와 Server가 연결되면 아래와 같이 Kernel 영역에 Recv, Send Buffer가 생성된다.
| Client | | Server |
| | | |
| | | |
| | | |
-------------- --------------
| Kernel | | Kernel |
| | | |
| Recv Buf | | Recv Buf |
| Send Buf | | Send Buf |
| | | |
-------------- --------------
// 만약 Send를 한다고 하면 Client가 직접 Server로 보내는 것이 아니라
// Send Buf에 데이터를 채우게 된다.
// 따라서 아래에 나오겠지만 Send를 한다고해서 코드가 블럭이 되지않는다
// (단순 Send Buf에 데이터를 옮기기만 했기에)
| Client | | Server |
| | | |
| | | |
| | | |
------------------------ --------------
| Kernel | | Kernel |
| | | |
| Recv Buf | | Recv Buf |
| Send Buf[SendData] | | Send Buf |
| | | |
------------------------ --------------
// 반대로 Recv를 했다면?
// Recv Buffer에서 데이터를 긁어온다.
// 없다면 무한대기를 하게된다.
| Client | | Server |
| | | |
| | | |
| | | |
-------------- ------------------------
| Kernel | | Kernel |
| | | |
| Recv Buf | | Recv Buf[RecvData] |
| Send Buf | | Send Buf |
| | | |
-------------- ------------------------
// 만약 Send Buf가 다 차버린 상황이라면?
// Send Buf에 자리가 날때까지 코드가 대기하게된다.
| Client | | Server |
| | | |
| [SendData] | | |
| | | |
---------------------------- --------------
| Kernel | | Kernel |
| | | |
| Recv Buf | | Recv Buf |
| Send Buf[SendDataFull] | | Send Buf |
| | | |
---------------------------- --------------
그런데 이런식으로 코드가 블럭되는 형태로 게임을 구현하는게 좋은방법일까??
그에 대한 설명은 다음강좌에서 진행
Client 측 데이터 보내기
#include "pch.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 clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET)
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
SOCKADDR_IN serverAddr; // IPv4
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777); // 80 : HTTP
if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Connect ErrorCode : " << errCode << endl;
return 0;
}
// ---------------------------
// 연결 성공! 이제부터 데이터 송수신 가능!
cout << "Connected To Server!" << endl;
while (true)
{
// TODO
char sendBuffer[100] = "Hello World!";
// 100바이트 * 10 = 1000바이트를 거의 한 순간에 보내게 된다.
for (int32 i = 0; i < 10; i++)
{
int32 resultCode = ::send(clientSocket, sendBuffer, sizeof(sendBuffer), 0);
// 서버에서 recv를 하지 않아도 아래로 내려간다(블락이 안됨)
if (resultCode == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Send ErrorCode : " << errCode << endl;
return 0;
}
}
cout << "Send Data! Len = " << sizeof(sendBuffer) << endl;
/*
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! Data = " << recvBuffer << endl;
cout << "Recv Data! Len = " << recvLen << endl;
*/
this_thread::sleep_for(1s);
}
// ---------------------------
// 소켓 리소스 반환
::closesocket(clientSocket);
// 윈속 종료
::WSACleanup();
}
Server측 데이터 받기
#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <atomic>
#include <mutex>
#include <windows.h>
#include <future>
#include "ThreadManager.h"
#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;
}
SOCKADDR_IN serverAddr; // IPv4
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = ::htonl(INADDR_ANY); //< 니가 알아서 해줘
serverAddr.sin_port = ::htons(7777); // 80 : HTTP
if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Bind ErrorCode : " << errCode << endl;
return 0;
}
// 영업 시작!
if (::listen(listenSocket, 10) == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Listen ErrorCode : " << errCode << endl;
return 0;
}
// -----------------------------
while (true)
{
SOCKADDR_IN clientAddr; // IPv4
::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;
// TODO
while (true)
{
char recvBuffer[1000];
this_thread::sleep_for(1s);
// 100바이트 * 10 = 1000바이트를 거의 한 순간에 보냈는데
// 100바이트씩 10번 들어올까??
// Nope! = 1000바이트로 들어온다.
// 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! Data = " << recvBuffer << endl;
cout << "Recv Data! Len = " << recvLen << endl;
/* 에코서버
int32 resultCode = ::send(clientSocket, recvBuffer, recvLen, 0);
if (resultCode == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Send ErrorCode : " << errCode << endl;
return 0;
}*/
}
}
// -----------------------------
// 윈속 종료
::WSACleanup();
}