Programming/C ,C++,C#

[WinSocket] 소켓을 이용한 접속

Makuri 2019. 12. 23. 01:47
728x90

평소에 별로 네트워크에 대해선 관심을 안 가졌는데,

최근데 조금 관심이 생겨서 책꽃이에 묵혀있떤 윈도우 API 프로그래밍 책을 꺼냈다.

 

네트워크를 보려는데 윈도우 API 책을 꺼낸 이유는 후반에 간략하게 다루고 있는 부분이 Winsocket이기 때문이다.

 

본래는 WinMain에서 다뤄지는 코드 내용이지만 소켓을 다루는 데에는 필요 없는거 같아서 일단은 콘솔 상에서 간단히 진행해 봤다.

 

-WinSock2.h

일단 윈도우 소켓을 사용하려면 헤더 파일과 라이브러리를 추가해야 한다.

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

ws2_32.lib는 윈도우 소켓을 위한 라이브러리이다.(라이브러리는 프로젝트 설정에서 추가 종속성에서 추가해도 된다.)

헤더 파일은 WinSock2.h를 추가해 준다.

그리고 WS2tcpip.h는 뒤에서 쓸 inet_pton()함수 때문에 추가했다.

 

이제, 작성해본 코드의 목적은 간단하다.

 

서버와 클라이언트가 있고,

서버가 클라이언트를 기다리다가,

클라이언트가 접속하면서 메세지를 던져준다.

서버는 클라이언트의 메세지를 받고 그대로 출력한다.

 

딱히 비동기 식도 아니고, 쓰레드를 사용하지도 안고 일단 처음이니까 이렇게만 해볼거다.

 

server.cpp

#pragma comment(lib,"ws2_32.lib")

#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>

using namespace std;

int main()
{
	WSADATA wsadata;
	WSAStartup(MAKEWORD(2, 2), &wsadata);
	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
	SOCKADDR_IN addr = { AF_INET,20 };
	inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.S_un.S_addr);
	bind(s, (LPSOCKADDR)&addr, sizeof(addr));
	if (listen(s, 5) == -1)
	{
		cout << "서버 열기 실패!" << endl;
		return 0;
	}

	char message[200];

	cout << "서버 열기 성공!" << endl;

	while (true)
	{
		cout << "서버 대기중" << endl;

		SOCKADDR_IN c_addr;
		int size = sizeof(c_addr);
		int msgLen;
		char buffer[100];
		SOCKET cs = accept(s, (LPSOCKADDR)&c_addr, &size);
		msgLen = recv(cs, buffer, 100, 0);

		if (msgLen > 0)
		{
			buffer[msgLen] = NULL;
			wsprintfA(message, "클라이언트 메시지: %s, 서버를 종료하시겠습니까? : ", buffer);
			cout << message;
			char input;
			cin >> input;
			if (input == 'y')
				return 0;
		}
	}

	closesocket(s);
	WSACleanup();
	return 0;
}

 

client.cpp

#pragma comment(lib,"ws2_32.lib")

#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>

using namespace std;

int main()
{
	WSADATA wsadata;
	WSAStartup(MAKEWORD(2, 2), &wsadata);
	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
	SOCKADDR_IN addr = { AF_INET,20 };
	inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.S_un.S_addr);
	if (connect(s, (LPSOCKADDR)&addr, sizeof(addr)) == -1)
	{
		cout << "연결 실패!" << endl;
	}
	else
	{
		send(s, "Hello Server!", 13, 0);
		cout << "전송 성공!" << endl;
	}
	closesocket(s);
	WSACleanup();
	return 0;
}

많은 수정을 한거도 아니고,

'윈도우 API 프로그래밍'에 있던 코드에서 C방식에서 C++방식으로 조금 줄였을 뿐이고 

원래 inet_addr()를 사용하던 부분을 inet_pton()함수로 바꿨을 뿐이다.

 

ip는 127.0.0.1로 루프백 주소를 썼고, 클라이언트가 보내는 메세지는 "Hello Server"이다.

 

책이 VS2008당시 기준이라서 안전성때문에 inet_addr대신 inet_pton함수를 권장하고 있었다.

(물론 #pragma warning(disable:4995)로 무시할 수 있다)

 

빌드를 완료하고 먼저 서버를 실행하고 클라이언트를 실행해본다.

 

서버: 서버는 기다린다.
클라이언트: 클라이언트를 실행한다
서버: 클라이언트를 실행하고 나면 메세지를 받았다고 알려준다.

이제 y를 넣고 엔터를 치면 서버 창이 닫힌다.

 

-후기

사실 서버가 대기 중일때는 '서버 대기중'이라는 문구가 무한 루프로 출력될거라고 생각했었다.

하지만 생각과는 달리 디버그 모드에서 일시정지 해보면 항상 accept 함수에서 멈춰있다.

클라이언트로부터 통신이 오기까지 코드가 멈춰있는 것이다.

 

사실 책에는 뒤로 논-블로킹과 채팅 프로그램 예제가 있다.

윈도우API기반인 책이라 메세지 루프를 이용하고 있었고, 논-블로킹을 위해 WSAStartup함수를 사용하는데, 

메세지 기반이라 WinMain 에서 만들어야 했다.

 

그래서 Select함수라고, 논-블로킹을 하는 함수를 따로 찾아봤는데, 아무래도 TCP/IP책을 보게 되면 알게 될 부분인거 같았다.

 

조만간 TCP/IP도 볼 생각이니 그때 가서 논 블로킹 이후 이야기를 해보려고 한다.

 
728x90