tcp
- 소켓프로그래밍을 할 때 식당운영에서 손님, 안내원, 대표 이런식으로 설명을 하였다.
DummyClient 부분
#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 errorCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errorCode << endl;
return 0;
}
//연결할 목적지
// 출처 : msdn connect...
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
SOCKADDR_IN serverAddr;
::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, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777); // port 포트 번호
//htons host to network short
//Little-Endian -> low [0x56][0x35][0x12].. high
//Big- Endian -> low [0x12][0x34][0x56].. high
if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errorCode = ::WSAGetLastError();
cout << "Connect ErrorCode : " << errorCode << endl;
}
cout << "Connected To Server" << endl;
while (true)
{
char sendBuffer[100] = "Hello world";
for (int i = 0; i < 10; i++)
{
int32 resultCode = ::send(clientSocket, sendBuffer, sizeof(sendBuffer), 0);
cout << "Send Data Len : " << sizeof(sendBuffer) << endl;
}
//-------------------------server에서 보낸 것을 recv
//char recvBuffer[1000];
//int recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
////SOCKET_ERROR가 -1인데 0보다 작으면 에러라고 보자!
//if (recvLen <= 0)
//{
// int32 errCode = ::WSAGetLastError();
// cout << "Rcv ErrorCode : " << errCode << endl;
// return 0;
//}
//cout << "Rcv Data : " << recvBuffer << endl;
//this_thread::sleep_for(1s);
}
::closesocket(clientSocket);
::WSACleanup();
}
server 부분
#include "pch.h"
#include <iostream>
#include <atomic>
#include <mutex>
#include <future>
//#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;
// ad : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
// protocol : 0
// return : descriptor
//현재 listenSocket는 접속을 할 때만 사용
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 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);
//clientSocket실질적인 데이터 주고 받는 소켓
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)
{
//recvBuffer는 상대방이 보낼 크기를 예측할 수 없다.
char recvBuffer[1000];
this_thread::sleep_for(1s);
int recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
//SOCKET_ERROR가 -1인데 0보다 작으면 에러라고 보자!
if (recvLen <= 0)
{
int32 errCode = ::WSAGetLastError();
cout << "Rcv ErrorCode : " << errCode << endl;
return 0;
}
cout << "Rcv Data : " << recvBuffer << endl;
cout << "Rcv Len : " << recvLen << endl;
////-------echo server느낌!
//char sendBuffer[100] = "Hello world";
//int32 resultCode = ::send(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
//if (resultCode == SOCKET_ERROR)
//{
// int32 errCode = ::WSAGetLastError();
// cout << "send Error : " << errCode << endl;
// return 0;
//}
//cout << "Send Data Len : " << sizeof(sendBuffer) << endl;
}
}
// -----------------------------
// 윈속 종료
::WSACleanup();
}
- server코드의 listen은 딱 처음 접속할 때만 사용된다 실질적으로 client쪽과 통신할 소켓은 accept할 때 나오는 clientSocket이다..
tcp의 패킷전송에서 경계가 없다는 것을 확인하는 부분
char sendBuffer[100] = "Hello world";
for (int i = 0; i < 10; i++)
{
int32 resultCode = ::send(clientSocket, sendBuffer, sizeof(sendBuffer), 0);
cout << "Send Data Len : " << sizeof(sendBuffer) << endl;
}
- 해당 소스에서 100짜리를 10번 보내면 받는쪽에서 100씩 나눠서 받는 것이 아니라 1000으로 받는 현상이 발생했습니다. 이러한 것은 tcp에서 전송을 할 때 경계의 개념이 없다는 것을 볼 수 있다.
- 식당에서 전화를 걸때랑 비교를 계속하면서 흐름을 생각하자.
리틀 엔디안 vs 빅엔디안
- 루키스님의 서버 강의를 보며 학습하였습니다.
-출처 : https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect
'네트워크프로그래밍' 카테고리의 다른 글
Overlapped모델(이벤트기반) (0) | 2022.04.11 |
---|---|
WSAEventSelect 모델 (0) | 2022.04.11 |
select 모델 (0) | 2022.04.10 |
소켓옵션 설정 (0) | 2022.03.30 |
UDP 프로그래밍 (0) | 2022.03.30 |