🧼C, C++/네트워크

다중 스트림서버 C++ TCP #1

Mawile 2020. 12. 31.
728x90

 

개발환경 >> Visual Studio

언어 >> C++17

운영체제 >> Windows10

 

 


안녕하세요... 오랜만입니다..!

이번에 시험이 끝나고 쓰는 정식적인 첫 강좌 글이네요.

이번에는 예전부터 쓴다고 예고했던 소켓 다중 스트림 강좌입니다.

 

다중 스트림이랑 다중 클라이언트와의 차이점은 밑에 그림을 그렸습니다 보시죠~~

 

 

 


우선 저가 이것을 연구하고 싶게 된 계기가

통신을 하면서 데이터를 동시에 보내야 하는데 혼합되지 않도록 할 때가 필요하더라고요

 

예를 들어서 키로거를 포함한 해킹 툴을 만드는데 키로깅 내용과 다른 작업을 동시에 할 때

데이터가 섞이지 않는 등등.. 이렇게 있기 때문에 연구하기 시작했습니다.

 

금방 만들 줄 알았는데 2시간 정도 더 걸렸네요...ㅠㅠ

 

우선 소스코드 보시죠!

 


[[[   서버   ]]]

//서버

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

#define PACKET_SIZE 1024

#include <winsock2.h>
#include <iostream>
#include <thread>

using namespace std;

int cnt = 0, cntport = 4444;

template<typename T> class vec { //자료구조
private:
	T* data = NULL;
	int size, maxsize;
public:
	vec() : size(0), maxsize(1) { }
	void add(T t) {
		if (size + 1 >= maxsize) {
			maxsize *= 2;
			T* tmp = new T[maxsize];
			for (int i = 0; i < size; i++) tmp[i] = data[i];
			tmp[size++] = t;
			delete[] data;
			data = tmp;
		}
		else data[size++] = t;
	}
	int length() { return size; }
	T& operator[](const int index) { return data[index]; }
};

class stream { //캡슐화된 하나의 스트림클래스
public:
	SOCKET server, _stream;
	SOCKADDR_IN addr = { 0 }, client_sock = { 0 };
	int client_size, port;
	stream() { }
	stream(int port) {
		this->port = port;
		server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

		addr.sin_family = AF_INET;
		addr.sin_addr.s_addr = htonl(INADDR_ANY);
		addr.sin_port = htons(port);

		bind(server, (SOCKADDR*)&addr, sizeof(addr));
		listen(server, SOMAXCONN);

		client_size = sizeof(client_sock);
		_stream = accept(server, (SOCKADDR*)&client_sock, &client_size);
	}
}; vec<stream>streams;

void recvs(SOCKET s, int p) { //특정스트림으로부터 값받기
	char buffer[PACKET_SIZE];

	while (true) {
		ZeroMemory(buffer, PACKET_SIZE);
		recv(s, buffer, PACKET_SIZE, 0);

		if (WSAGetLastError()) break;
		cout << p << " ::: " << buffer << endl;
	}
}

int main() {
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);

	SOCKET server, _stream;
	SOCKADDR_IN addr = { 0 }, client_sock = { 0 };
	int client_size;

	server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(8080);

	bind(server, (SOCKADDR*)&addr, sizeof(addr));
	listen(server, SOMAXCONN);

	client_size = sizeof(client_sock);
	_stream = accept(server, (SOCKADDR*)&client_sock, &client_size);

	char msg[PACKET_SIZE];

	while (!WSAGetLastError()) {
		ZeroMemory(msg, PACKET_SIZE);
		cin >> msg;

		if (!strcmp(msg, "create")) { //명령어 "create"는 스트림객체 하나를 생성한다.
			send(_stream, "Create", PACKET_SIZE, 0);
			streams.add(stream(cnt + cntport));
			thread(recvs, streams[cnt]._stream, streams[cnt].port).detach();
			cnt++;
		}
	}

	closesocket(_stream);
	closesocket(server);
	for (int i = 0; i < streams.length() - 1; i++) {
		closesocket(streams[i]._stream);
		closesocket(streams[i].server);
	}
	WSACleanup();
}

 

 

[[[   클라이언트   ]]]

//클라이언트

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

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define PACKET_SIZE 1024

#include <winsock2.h>
#include <iostream>
#include <thread>

using namespace std;

int cnt = 0, cntport = 4444;

template<typename T> class vec { //서버와 설명이 같다.
private:
	T* data = NULL;
	int size, maxsize;
public:
	vec() : size(0), maxsize(1), data(0) { }
	void add(T t) {
		if (size + 1 >= maxsize) {
			maxsize *= 2;
			T* tmp = new T[maxsize];
			for (int i = 0; i < size; i++) tmp[i] = data[i];
			tmp[size++] = t;
			delete[] data;
			data = tmp;
		}
		else data[size++] = t;
	}
	int length() { return size; }
	T& operator[](const int index) { return data[index]; }
};

class stream { //캡슐화된 하나의 스트림클래스
public:
	SOCKET server;
	SOCKADDR_IN addr = { 0 };
	int port;

	stream() { }
	stream(int port) {
		this->port = port;
		server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

		addr.sin_family = AF_INET;
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");
		addr.sin_port = htons(port);

		while (connect(server, (SOCKADDR*)&addr, sizeof(addr)));
		cout << port << "연결됨" << endl;
	}
}; vec<stream>streams;

void recvs(SOCKET s) { //명령을 받는다
	char msg[PACKET_SIZE];

	while (true) {
		ZeroMemory(msg, PACKET_SIZE);
		recv(s, msg, PACKET_SIZE, 0);

		if (WSAGetLastError()) return;

		if (!strcmp(msg, "Create")) {
			streams.add(stream(cnt + cntport));
			cnt++;
		}
	}
}

bool crtPort(int portnum) {
	for (int i = 0; i < streams.length(); i++) if (streams[i].port == portnum) return true;
	return false;
}

int main() {
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);

	SOCKET server;
	SOCKADDR_IN addr = { 0 };

	server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	addr.sin_port = htons(8080);

	while (connect(server, (SOCKADDR*)&addr, sizeof(addr)));

	thread(recvs, server).detach();

	int portnum;
	char buffer[PACKET_SIZE];

	while (!WSAGetLastError()) {
		ZeroMemory(buffer, PACKET_SIZE);
		cin >> portnum >> buffer;

		if (crtPort(portnum)) { //만약 존재하는 스트림이라면
			send(streams[portnum - cntport].server, buffer, PACKET_SIZE, 0); //메세지전송
		}
		else cout << "존재하지 않는 스트림입니다." << endl;
	}

	closesocket(server);
	for (int i = 0; i < streams.length()-1; i++) closesocket(streams[i].server);
	WSACleanup();
}

 

 


[[[   시연 영상   ]]]

 

 

 


[[[   다운로드   ]]]

 

DRAGONPROCESS/MultiStreamServer

Contribute to DRAGONPROCESS/MultiStreamServer development by creating an account on GitHub.

github.com

 


 

궁금한 부분 있으면 제발 질문 주세요....!!! ㅠㅠㅠ

 

소스코드는 오늘 낮부터 유동적으로 저가 생각난 대로 쓴 것입니다.

프로젝트 다운로드 링크는 밑에 github링크로 올려놓겠습니다.

 

 

감사합니다!!

 

728x90

댓글