간단한 서버의 구조를 만들어보았으니 이제 데이터를 보내고 받는 작업을 해보자.
Dummyclient.cpp
#include "pch.h"
#include <iostream>
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
// 윈속 라이브러리 초기화 (ws2_32 라이브러리 초기화)
// 관련 정보가 wsaData에 채워짐
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
// af : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
// protocol : 0 (알아서 골라줌)
// return : descriptor
SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET) // 실패
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// 연결할 목적지 (IP주소 + Port)
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");
::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777); // 엔디안 이슈, 네트워크는 주로 빅 엔디안
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!";
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;
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);
// 윈속 종료, WSAStartup이 호출된 수만큼 실행해줘야 함
::WSACleanup();
}
Server.cpp
#include "pch.h"
#include "CorePch.h"
#include <Windows.h>
#include "ThreadManager.h"
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
// 윈속 라이브러리 초기화 (ws2_32 라이브러리 초기화)
// 관련 정보가 wsaData에 채워짐
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
// af : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
// protocol : 0 (알아서 골라줌)
// return : descriptor
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET) // 실패
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// 나의 주소 (IP주소 + Port)
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);
// 소켓과 서버 주소 연동
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];
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;
}
}
}
//-------------------------------
// 윈속 종료, WSAStartup이 호출된 수만큼 실행해줘야 함
::WSACleanup();
}
Hello World!라는 문장을 주고 받는 코드이다.
서로 잘 돌아가는 것을 볼 수 있다.
출력화면을 보면 서로 100바이트를 주고 받고 하는 것을 볼 수 있는데 만약 보낼 때 10개의 데이터를 한번에 보내면 받을 때 어떻게 받을까? 보낸 데이터를 구분지어서 버퍼에서 읽는지 아니면 한 번에 전부 읽는지 그것이 궁금한 것이다.
DummyClient.cpp
#include "pch.h"
#include <iostream>
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
// 윈속 라이브러리 초기화 (ws2_32 라이브러리 초기화)
// 관련 정보가 wsaData에 채워짐
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
// af : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
// protocol : 0 (알아서 골라줌)
// return : descriptor
SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET) // 실패
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// 연결할 목적지 (IP주소 + Port)
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");
::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777); // 엔디안 이슈, 네트워크는 주로 빅 엔디안
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!";
for (int i = 0; i < 10; i++)
{
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;
this_thread::sleep_for(1s);
}
//----------------------------------
// 소켓 리소스 반환
::closesocket(clientSocket);
// 윈속 종료, WSAStartup이 호출된 수만큼 실행해줘야 함
::WSACleanup();
}
Server.cpp
#include "pch.h"
#include "CorePch.h"
#include <Windows.h>
#include "ThreadManager.h"
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
// 윈속 라이브러리 초기화 (ws2_32 라이브러리 초기화)
// 관련 정보가 wsaData에 채워짐
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
// af : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
// protocol : 0 (알아서 골라줌)
// return : descriptor
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET) // 실패
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// 나의 주소 (IP주소 + Port)
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);
// 소켓과 서버 주소 연동
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);
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;
}
}
//-------------------------------
// 윈속 종료, WSAStartup이 호출된 수만큼 실행해줘야 함
::WSACleanup();
}
클라이언트에서 for문을 통해 한 번에 10개의 데이터를 보냈고 서버에서는 1초를 대기한 후 버퍼를 읽어서 한 번에 여러개의 데이터를 받을 수 있도록 하였다.
결과를 보면 알 수 있듯이 클라이언트는 100 바이트의 데이터를 여러개 보냈지만 서버는 이를 구분하지 못하고 1000 바이트를 읽은 것을 볼 수 있다. 이는 TCP의 특징 중 하나로 차후 보내는 데이터에 데이터의 크기에 대한 정보도 같이 실어보낼 것이다. 기억해야될 것은 보내는 쪽에서 100 바이트를 보낸다 하더라도 받는 쪽에서 반드시 100 바이트를 받는 것은 아니라는 사실이다. 위 처럼 여러개의 데이터가 하나의 큰 덩어리로 올 수도 있고, 버퍼의 공간이 조금밖에 남지 않았을 경우 쪼개져서 올 수도 있는 것이다.
'서버 > 네트워크' 카테고리의 다른 글
6. Select 모델 (0) | 2024.09.05 |
---|---|
5. 논블로킹(Non-Blocking) 소켓 (2) | 2024.09.03 |
4. 소켓 옵션 (3) | 2024.09.01 |
3. UDP 서버 (2) | 2024.08.30 |
1. 소켓 프로그래밍 (0) | 2024.08.29 |