카테고리 없음

230712 서버수업 1

슬뷔 2023. 7. 12. 18:05

서버베이스라고 서버에서 충돌 처리하고 돈을 먹었으니까 허락해주겠다.

 

오로지 A 컴퓨터에서 B컴퓨터로 데이터를 보낼 수 있는지 

페킷을 주고 받을 수 있는 정도 수준의 서버만 익히면 된다.

 

ABCDE 컴퓨터가 있다고 치면

 

A컴퓨터에서 캐릭터가 움직이면

BCDE가 똑같이 움직여야하는 것은 기본이다.

 

ABCDE  + 서버

A가 서버에게 보낸다 나 이동했다고

그럼 서버가 이동을 할 수 있는지 적절한지 체크하고 BCDE에 보내는 형식의 서버 => 한국 온라인 게임의 서버

 

A가 무조건 믿는 서버 

A가 서버 역할을 하고 방을 연다

BCDE는 그냥 거기에 접속한다.

 

그리고 스킬쓰고 공격하고 이동하고 무조건 그냥 한다.

검증은 없다.. 그냥 무조건 믿는 것 

알려주면 그냥 믿고 가있고 행동 하는 것.

 

누가 뭔가를 행동했든 그냥 믿는다 

 

김성우의 TCP/IP 정도 밖에 안됨..

 

릴레이서버라고 하는 서버를 만들 것이다.

A라고 하는 방장이 있고 방장이 방을 열어야한다.

 

그냥 나머지는 원래 클라이언트가 하던대로 할 뿐이다. (이동, 충돌 등)

 

그대신 다른건 내가 몹을 죽이거나 이동을 했을 때, 서버에게 나 몹 죽였어 이동했어 라는 걸 보내는 과정을 추가해야한다.

 

A라는 서버는 B가 몹을 죽였으면 A라는 서버에게 보낸다.

그럼 A는 CDE에게 b가 몹을 죽였대 하고 알려준다

그럼 CDE는 몹을 죾였대 하고 어떤 중간 체크도 없이 그냥 몹을 죽였다고 인식하고 없앤다.

 

오로지 동기화만 시켜주는 서버가 되는데 이걸 릴레이 서버라고 한다. => 기성게임에서도 많이 사용함 

 

그럼 다른 사람들은 거기에 ip 주소로 접속해야 한다.

내부 네트워크망을 사용할 것 => 학원 안에선 그냥 x , 공유기 사용 안하는 포트 포워딩 할 필요가 없는 컴퓨터에서만 사용 가능.. 학원 네트워크를 사용할 것이기 때문에 선생님께 포트포워딩 해달라고 부탁해야함.

 

누가 뭔가를 행동을 했든 그냥 믿고 충돌 체크

 

온리 TCP 를 만들고 UDP 안쓴다 .

 

TCP 와 UDP의 차이

네트워크 기술의 근본을 이해하려면 osi 7계층을 볼 수 밖에 없다.

TCP 하기 전에 가장 기본적인 서버 형태만 봄

 

클라이언트와 서버를 따로 솔로션을 분리해서 두고 좌 우로 두고 디버깅을 따로 한다 ( 서버에서는 중요함 )

 

윈도우에서 네트우ㅓ크를 열려면 include winsock2.h 를 해야하는데, windows.h 랑 같이 안된다..

둘이 충돌이 있기 때문이다..

 

윈속2안에 윈도우가 다 들어있기 때문에 윈도우라는 헤더가 필요 없어진다.

그래서 윈속2만 사용하는게 낫다 아니면 윈속2가 무조건 윈도우보다 순서적으로 위에 있기 해야한다.

 

ex) 

#include <WS2tcpip.h>

#include <WinSock2.h>

#include <Windows.h>

 

#pragma => 라이브러리

 

* Thread 쓰레드

스택을 여러개 만드는 것, 

cpu를 하나 더 두는 것..

운영체제의 허락을 받지 않고 cpu를 하나 더 쓸 수 없다.

std를 하더라도 운영체제를 무시할 수 없고, 

_beginthreadex <= window가 지원해주는 thread를 만들어주는 함수

 

 

 

프로세스가 하나 있으면 그 안에는 Main Thread가 붙게 된다.

실행흐름이 무조건 붙게되고 이를 Main Thread 라고 부르는데 이 옆에다 새로운 실행 흐름을 둘 수 있다.

 

프로세스 안에는

코드

데이터

 

#include <thread> 를 사용하게 되면, 

왜 쓰레드가 필요한가 ?

 

서버에서 가장 중요한건 티키타카이다.

양쪽에 컴퓨터a와 컴퓨터b를 두고 

 

       A                              B

서버를 연다       a서버에 접속한다

 

a도 언제 b한테 뭘 보낼지 모르고

b도 언제 a한테 보낼지 모른다.

그런데 거기서 send 와 recv 를 하는데, 

 

* 메모리 동시접근의 문제

why ? 디버그 그래픽창 디스어셈블리 하면 3줄을 함 정확하게 3줄인지도 모름 한단계가 아님

한 번에 처리된다고 생각하지만, 한 번에 처리되지 않는다.

a의 메모리를 준비한다.

1의 메모리를 준비한다.

a+1의 메모리를 준비한다.

a+1의 결과는 a+1를 담을 수 있는 결과를 담는다.

a에다가 결과를 담는다

 

스택 하나가 cpu 에 명령을 보냄

메모리를 동시에 처리하려고 하기 때문에 발생한다.

 

이를 막기위해 만들어진게 #include <mutex> 이다.

공유되는 메모리 하나당 

Mutex.lock();

Mutex.unlock();

을 걸어준다.

 

공유되는 메모리 1개당 하나의 뮤텍스가 필요하다.

 

쓰레드가 순위를 다퉈서 Mutex.lock();에 걸리면 키를 획득하게 되고,

그 다음부터 다른 쓰레드들은 Mutex.lock() 을 넘지 못하고 기다려야한다.

 

단점 => 보통 안쓰고 처리하는데.. 5명정도 짜리 서버에서는 메모리가 꼬이고 느려지고 하지 않는다.

 

_getch

std::cin  >> Value;

 

정보가 들어오게 되면 메인쓰레드에 접근할 수 밖에 없다.

이때 lock으로 접근하게 한다.

 

내 프로그램이 네트워크를 사용한다고 하면 아래를 선언

 

WSAData WsaData;

 

이 프로그램이 서버를 사용하겠다고 알려주는 것.

int errorCode = WSAStartup(MAKEWORD(2, 2), &WsaData);

 

if (SOCKET_ERROR == errorCode)

{

MsgBoxAssert("socket Error");

return;

}

위의 형광펜을 쳐줘야 서버를 열고 소켓을 만들거나 다른 컴퓨터에 접속할 수가 있다.

 

소켓이라는 것을 만들어야하낟.

소켓은 윈도우가 우리에게 제공하는 다른 컴퓨터와의 연결을 의미하는 키라고 보면 된다.

A와 B가 서로 통신을 하게 되면, 서로간의 TCP로 연결할 경우에 서로간의 스트림(stream)이라는게 생기고 

그 stream에 대한 윈도우가 우리에게 hwnd처럼 정수형 숫자를 부여해준다.

(일반적으로 윈도우 모르게 상대에게 보낸ㄴ건 불가능)

 

연결이 제대로 이루어지게 되면 그다음부터는 이 소켓을 통해서 상대와의 연결 상대가 보낸 데이터 등을 받아볼 수 있다.

그걸 하는 함수들 모두에게 이 소켓을 요구한다.

send, recv

 

소켓을 만드는 방법은 2가지가 있다

연결을 받을 쪽

서버 역할을 할 쪽

커넥트를 걸 쪽

클라이언트 역할을 할 쪽

 

서버역할을 할 쪽은 자기 주소를 걸고 해야한다.

커넥터는 자기를 알릴 필요는 없고, 어디에 접속하겠다는 걸 주소를 알려줘야한다.

ip 주소 없이 연결 할 수는 없다.

 

내 컴퓨터의 외부 IP를 알고 싶으면 네이버가서 내 IP라고 검색하면 내 컴퓨터가 외부에 보여주는 IP를 볼 수 있다.

cmd에서 ipconfig를 검색하면 나오는게 내부IP

 

공유기가 없다면 ipconfig에 나오는 주소와 네이버 ip 주소가 동일할 것이다.

 

내부ip라는 것을 만들어낸다.

 

누군가에게 접속할 때는 외부, 내부 ip 상관없다.

 

일단 내부 ip로 서로간의 멀티 플레이가 되려면 서로 다 같은 네트워크(와이파이)를 사용하고 있는지를 알아야한다.

 

같은 와이파이를 사용하면 그냥 ip주소 만으로 접속이 가능하고, 

서로 기본 게이트웨이가 다르면 그건 선생님이 포트포워딩을 통해서 중간 작업을 해줘야 접속이 가능해진다.

(지금은 신경 쓸 필요 x)

 

로컬 host 라는 것을 사용한다.

로컬 호스트는 ip가 고정되어 있다. 127.0.0.1

 

서버를 열 때는 주소를 바인드 하는 작업부터 해주어야한다.

SOCKADDR_IN Add;

Add.sin_family = AF_INET; => ip4 주소 체계를 사용 하겠다.

 

빅 에디안 리틀 에디안

int 00000001 00000000 00000000 00000000

리틀 에디안

int 00000000 00000000 00000000 00000001

 

둘다 같은 1인데, 클라이언트에서는 같다고 하지만 서버에서는 다르다고 취급된다

cpu가 서로 다르면 일반적인 바이트 배치법으로 변환해주어야한다. => 윈도우가 함수 만들어 둠. htons 를 사용하면 됨.

 

2바이트 655536

멀리서 어떤 데이터가 날아왔다 그럼 이게 어디에서 온건지 알 수 없다..

그래서 2번째 분류가 필요하다..

프로그램마다 번호가 지정된다

Add.sin_port = htons(30000); => 내 네트워크 프로그램에 대한 번호를 30000번으로 부여한다.

 

htons => 포트를 cpu의 네트워크 통신에 유효한 에디안 방식으로 만들어준다 

 

ip주소는 4바이트로 이루어진다.

"0.0.0.0" 을 넣으면 알아서 아이피주소를 주소에 넣어준다

if (SOCKET_ERROR == inet_pton(AF_INET, "0.0.0.0", &Add.sin_addr))

{

return;

}

 

다른 사람들에게 접속자를 받는 용도로 소켓을 만들어줘야한다.

SOCKET AcceptSocket;

 

TCP 방식으로 소켓을 만들겠다는 의미.

SessionSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

 

if(INVALID_SOCKET == AcceptSocket)

{

return;

}

 

열기 위해서 바인드작업

AcceptSocket 소켓에 주소를 바인드 하고 이 주소로 이제 접속을 받을 준비가 끝났다는 이야기

if (SOCKET_ERROR == bind(AcceptSocket, (const sockaddr*)&Add, sizeof(SOCKADDR_IN))

{

return;

}

 

이걸 호출한 순간부터 서버역할을 함..

if (SOCKET_ERROR == listen(AcceptSocket, 512))

{

return;

}

 

listen 부터 서버가 시작인지?

* 512 를 외워 두라고 하심.

클라에는 connect 라는 함수가 있음. 

어떤 걸로 접속..

connect가 안되면 실패가 뜸

listen을 호출하고 accept를 호출안해도 실패라고 뜨지 않음.

즉, 대기자를 받는 함수.

 

int AddressLen = sizeof(SOCKADDR_IN);

 

while(true)

{

SOCKADDR_IN Add;

 

이제부터 접속자를 기다리는 동기화 함수이다.

접속자가 들어오기 전까지 멈춰있다.

accept-> 이미 접속자는 접속했고, 그 접속자들 중에 접속자를 꺼내오는 함수

대기열이 다 찼다면 제일 앞에 있는 대기자를 찾아오는 함수..!

SOCKET ClentSocket = accept(AcceptSocket, (sockaddr*)&ClientAdd, &AddressLen);

}

 

 

-------

내일은 tcp와 udp에 대해 설명 그래야 패킷이 어덯게 오는지 이해가 된다.

그리고 클라에서 옮기는 작업한다.  (다음주 월요일)

 

나스에 들어가서 접속 

아이디와 비번은 카카오톡에 ..