VisualStudio/C++서버

[C++] 블로킹방식 TCP/IP 소스

usingsystem 2024. 5. 23. 14:36
728x90

Client

소켓 생성 (Socket Creation)

  • 클라이언트 소켓을 생성합니다.  AF_INET은 IPv4, SOCK_STREAM은 TCP를 의미합니다.
SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);

서버에 연결 (Connect)

  • 서버의 IP 주소와 포트 번호로 연결 요청을 보냅니다. 
if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{}

전체 코드

#include "pch.h"
#include <iostream>
#include "CorePch.h"

#include <WinSock2.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

int main()
{
	//윈소켓 초기화(w2_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), UDP(SOCK_DGRAM)
	//protocol : 0
	//return : 성공여부
	SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
	if (clientSocket == INVALID_SOCKET)
	{
		int32 errCode = ::WSAGetLastError();
		cout << "socket error code" << 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);//PORT
	//htons(host to network short) Little-Endian, Big-Endian

	//서버에 연결
	if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		int32 errCode = ::WSAGetLastError();
		cout << "Connect error code" << errCode << endl;
		return 0;
	}

	//--------------------------
	///연결성공

	cout << "Connected To Server!" << endl;

	while (true)
	{
		char sendBuffer[100] = "Hellow World!";
		int32 resultCode = ::send(clientSocket, sendBuffer, sizeof(sendBuffer), 0);
		if (resultCode == SOCKET_ERROR)
		{
			int32 errCode = ::WSAGetLastError();
			cout << "Send error code" << 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 error code" << errCode << endl;
			return 0;
		}
		cout << "Recv Data = " << recvBuffer << endl;

		this_thread::sleep_for(1s);
	}
	//--------------------------

	//소켓 리소스 반환
	::closesocket(clientSocket);

	//윈도우 소켓 종료 WSADATA호출 수만큼 정리해줘야함(권고사항)
	::WSACleanup();
}

Server

소켓 생성 (Socket Creation)

  • 클라이언트 소켓을 생성합니다.  AF_INET은 IPv4, SOCK_STREAM은 TCP를 의미합니다.
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);

소켓 바인드

 

  • 소켓을 IP 주소와 포트 번호에 바인드합니다. INADDR_ANY는 모든 사용 가능한 인터페이스에서 연결을 수신하도록 설정하며, htons는 포트 번호를 네트워크 바이트 순서로 변환합니다.
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(7777);

if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)))
{
    int32 errCode = ::WSAGetLastError();
    cout << "Bind error code" << errCode << endl;
    return 0;
}

연결 대기(Listnener)

 

  • 소켓을 연결 대기 상태로 만듭니다. listen 함수는 최대 10개의 대기열을 설정합니다.
if (::listen(listenSocket, 10) == SOCKET_ERROR)

연결 수락(accept)

  • accept는 연결 요청이 있을 때까지 대기하며, 클라이언트와의 새 연결에 대한 소켓을 반환합니다.
SOCKET clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);

전체코드

#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()
{
	//윈소켓 초기화(w2_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), UDP(SOCK_DGRAM)
	//protocol : 0
	//return : 성공여부
	SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
	if (listenSocket == INVALID_SOCKET)
	{
		int32 errCode = ::WSAGetLastError();
		cout << "socket error code" << 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);//PORT
	//htons(host to network short) Little-Endian, Big-Endian

	//socket과 주소 연동
	if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr))== SOCKET_ERROR)
	{
		int32 errCode = ::WSAGetLastError();
		cout << "Bind error code" << errCode << endl;
		return 0;
	}

	//서버오픈
	if (::listen(listenSocket, 10) == SOCKET_ERROR)//10은 대기열 이걸넘으면 못들어옴
	{
		int32 errCode = ::WSAGetLastError();
		cout << "Listen error code" << 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 error code" << 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 error code" << errCode << endl;
				return 0;
			}

			cout << "Recv Data = " << recvBuffer << endl;

			int32 resultCode = ::send(clientSocket, recvBuffer, recvLen, 0);
			if (resultCode == SOCKET_ERROR)
			{
				int32 errCode = ::WSAGetLastError();
				cout << "Send error code" << errCode << endl;
				return 0;
			}
		}
	}
	//--------------------------

	//윈도우 소켓 종료 WSADATA호출 수만큼 정리해줘야함(권고사항)
	::WSACleanup();
}
728x90