Post

2018/10/04 - [Programing/Server] - IOCP window 채팅 클라이언트 소스

IOCP window 채팅 클라이언트 소스


아래는 IOCP window 채팅 클라이언트 프로젝트이다

WinChatClnt.zip

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "resource.h"
#include 

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

struct SOCKDATA
{
    SOCKET hSocket;
    SOCKADDR_IN servAdr;
};

SOCKET hSocket = 0;     // 소켓. 메시지 처리에서도 사용해야되서 전역변수로 선언
HWND hMsgWnd = 0;     // 메시지 받고 나서 ui처리해야 되서 전역변수로 선언


#pragma region RegionName
#define BUF_SIZE 1024
void ErrorHandling(const char* message);
void StartServerConnect(WSADATA& wsaData, SOCKET& hSocket, SOCKADDR_IN& servAdr);       // 소켓 초기화
DWORD WINAPI ServerThreadMain(LPVOID socket);                                   // 서버와 데이터를 주고 받는 Thread

BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam) {
    // WM_INITDIALOG: Dialog Box 초기화 코드 작성
    return (TRUE);
}

void Dlg_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) {

    if (0 == hMsgWnd)
    {
        hMsgWnd = hWnd;
    }

    //WM_COMMAND
    switch (id) {
    case IDOK:
    {
        TCHAR buff[BUF_SIZE];
        GetDlgItemText(hWnd, IDC_CHAT_ENTER, buff, BUF_SIZE);
        char temp[BUF_SIZE];
        WideCharToMultiByte(CP_ACP, 0, buff, BUF_SIZE, temp, BUF_SIZE, NULL, NULL);
        printf("%s \n", temp);

        int len = strlen(temp);
        send(hSocket, temp, len, 0);

        memset(buff, 0, BUF_SIZE);

        // 글자 없애기
        SetDlgItemText(hWnd, IDC_CHAT_ENTER, buff);

        break;
    }
    case WM_DESTROY:
        exit(0);

        break;
    }
}

// DialogBox Procedure
INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_INITDIALOG:
        return (SetDlgMsgResult(hWnd, uMsg, HANDLE_WM_INITDIALOG((hWnd), (wParam), (lParam), (Dlg_OnInitDialog))));

    case WM_COMMAND:
        return (SetDlgMsgResult(hWnd, uMsg, HANDLE_WM_COMMAND((hWnd), (wParam), (lParam), (Dlg_OnCommand))));
    }
    return (FALSE);
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR    lpCmdLine,
    int       nCmdShow)
{
    WSADATA wsaData;
    SOCKADDR_IN servAdr;
    
    StartServerConnect(wsaData, hSocket, servAdr);

    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);


    // DialogBox생성 함수      
    DialogBox(hInstance,
        MAKEINTRESOURCE(CHAT_DIALOG/*DialogBox ID*/),
        NULL,
        Dlg_Proc/*DialogBox Procedure*/);

    return(0);
}

DWORD WINAPI ServerThreadMain(LPVOID data)
{
    SOCKDATA* socketInfo = (SOCKDATA*)data;
    if (NULL == socketInfo)
    {
        return 0;
    }

    if (connect(socketInfo->hSocket, (SOCKADDR*)(&socketInfo->servAdr), sizeof(socketInfo->servAdr)) == SOCKET_ERROR)
    {
        DWORD dwError = GetLastError();
        ErrorHandling("connect() error!");
    }
    else
        puts("Connected...........");

    char message[BUF_SIZE];
    memset(message, 0, BUF_SIZE);
    TCHAR uniMessage[BUF_SIZE];
    memset(uniMessage, 0, BUF_SIZE * sizeof(TCHAR));
    
    int strLen = 0;
    int readLen = 0;

    while (true)
    {
        //fputs("Input message(Q to quit): ", stdout);
        //fgets(message, BUF_SIZE, stdin);
        //if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
        //    break;

        //strLen = strlen(message);
        //send(socketInfo->hSocket, message, strLen, 0);

        readLen = 0;
        while (1)
        {
            memset(message, 0, BUF_SIZE);
            readLen += recv(socketInfo->hSocket, &message[readLen], BUF_SIZE - 1, 0);
            if (readLen >= strLen)
                break;
        }
        strLen = readLen;
        message[strLen] = 0;

        // 유니코드로 변경
        memset(uniMessage, 0, BUF_SIZE * sizeof(TCHAR));
      

        // 유니코드 변경 전 유니코드 길이 구하기
        int nUniLen = MultiByteToWideChar(CP_ACP, 0, message, strlen(message), NULL, NULL);

        MultiByteToWideChar(CP_ACP, 0, message, strLen+1, uniMessage, nUniLen);
        

        if (0 != hMsgWnd)
        {
            //SetDlgItemText(hMsgWnd, CHAT_VIEW, uniMessage);
            SendDlgItemMessage(hMsgWnd, CHAT_VIEW, LB_GETCURSEL, 0, 0);
            SendDlgItemMessage(hMsgWnd, CHAT_VIEW, LB_ADDSTRING, 0, (LPARAM)uniMessage);
        }
        strLen = 0;
        //printf("Message from server: %s", message);
    }
    
    delete socketInfo;
    closesocket(socketInfo->hSocket);
    WSACleanup();

    return 0;
}

void StartServerConnect(WSADATA& wsaData, SOCKET& hSocket, SOCKADDR_IN& servAdr)
{
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        ErrorHandling("WSAStartup() error!");
    
    hSocket = socket(PF_INET, SOCK_STREAM, 0);
    if (hSocket == INVALID_SOCKET)
        ErrorHandling("socket() error");

    memset(&servAdr, 0, sizeof(servAdr));
    servAdr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &(servAdr.sin_addr));
    servAdr.sin_port = htons(atoi("7777"));

    SOCKDATA* sockData = new SOCKDATA();
    sockData->hSocket = hSocket;
    sockData->servAdr = servAdr;

    // 서버와 통신할 thread  생성
    _beginthreadex(NULL, 0, (_beginthreadex_proc_type)ServerThreadMain, (LPVOID)sockData, 0, NULL);
}


void ErrorHandling(const char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

#pragma endregion

Dialog ID : CHAT_DIALOG

채팅 ListView ID : CHAT_VIEW (Sort 하지말것)

채팅 메시지 입력 Edit ID : IDC_CHAT_ENTER 

확인 버튼 ID : IDOK


이전에 올린 IOCP 채팅 서버와 대응하는 클라이언트다

Post

IOCP 채팅 서버 소스 예제


윤성우의 TCP/IP 에코 채팅 서버에서

다른사람들에게 채팅 내용이 전송되도록 수정한 소스이다.


#include 
#include 
#include 
#include 
#include 
#include 
#include 


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

#define BUF_SIZE 100
#define READ 3
#define WRITE 5

int const NAME_SIZE = 10;

typedef struct          // socket intfo

{
    SOCKET hClntSock;
    SOCKADDR_IN clntAdr;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

typedef struct // buffer info
{
    OVERLAPPED overlapped;
    WSABUF wsaBuf;
    char buffer[BUF_SIZE];
    int rwMode;             // READ or WRITE
    
}PER_IO_DATA, *LPPER_IO_DATA;

typedef struct
{
    char id[NAME_SIZE];
    unsigned int level;
    
}USER_INFO, *PUSER_INFO;        // 나중에 PER_HANDLE_DATA 상속하자

std::list UserList; // 다른사람에게 채팅 전달해야하므로 user list 추가

DWORD WINAPI EchoThreadMain(LPVOID CompletionPortIO);

CRITICAL_SECTION cs;        // UserList 동기화를 위한 Critical Section


int main()
{
    WSADATA wsaData;
    HANDLE hComPort;
    SYSTEM_INFO sysInfo;
    LPPER_IO_DATA ioInfo;
    LPPER_HANDLE_DATA handleInfo;

    SOCKET hServSock;
    SOCKADDR_IN servAdr;
    int recvBytes, i, flags = 0;

    if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData))
    {
        printf("WSAStartup() error!");
        exit(1);
    }

    hComPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    GetSystemInfo(&sysInfo);

    for (i = 0; i < (int)sysInfo.dwNumberOfProcessors; ++i)
    {
        // Thread 핸들 정리 추가할 것
        _beginthreadex(NULL, 0, (_beginthreadex_proc_type)EchoThreadMain, (LPVOID)hComPort, 0, NULL);
    }

    hServSock = WSASocketW(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    memset(&servAdr, 0, sizeof(servAdr));
    servAdr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &(servAdr.sin_addr));
    servAdr.sin_port = htons(atoi("7777"));

    bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr));
    listen(hServSock, 5);

    InitializeCriticalSection(&cs);

    while (1)
    {
        SOCKET hClntSock;
        SOCKADDR_IN clntAdr;
        int addrLen = sizeof(clntAdr);

        hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &addrLen);
        
        handleInfo = (LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA));        // 누군가 접속할 때 마다 소켓 정보를 생성하고
        handleInfo->hClntSock = hClntSock;                                      // hClntSock은 지역변수이므로 저장한다
        memcpy(&(handleInfo->clntAdr), &clntAdr, addrLen);

        EnterCriticalSection(&cs);
        UserList.push_back(handleInfo);
        LeaveCriticalSection(&cs);

        CreateIoCompletionPort((HANDLE)hClntSock, hComPort, (DWORD)handleInfo, 0);  // 접속한 클라 소켓을 IOCP에 등록함

        ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));                        // 신규 유저 접속 시 overlapped도 생성한다.
        memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));                       // 그리고 WSARecv 함수로 소켓 io완료 시 반환되는 wasBuf, overlapped 주소를 등록한다
        ioInfo->wsaBuf.len = BUF_SIZE;
        ioInfo->wsaBuf.buf = ioInfo->buffer;
        ioInfo->rwMode = READ;

        WSARecv(handleInfo->hClntSock, &(ioInfo->wsaBuf), 1, (LPDWORD)&recvBytes, (LPDWORD)&flags, &(ioInfo->overlapped), NULL);    // recv가 완료되었을 때 GetQueue...에서 반환됨
    }   

    DeleteCriticalSection(&cs);

    return 0;
}

DWORD WINAPI EchoThreadMain(LPVOID pComPort)
{
    HANDLE hComPort = (HANDLE)pComPort;
    SOCKET sock;
    DWORD bytesTrans;
    LPPER_HANDLE_DATA   handleInfo;
    LPPER_IO_DATA       ioInfo;
    DWORD flags = 0;
    char msg[BUF_SIZE];

    while (1)
    {
        GetQueuedCompletionStatus(hComPort, &bytesTrans, (LPDWORD)&handleInfo, (LPOVERLAPPED*)&ioInfo, INFINITE);
        sock = handleInfo->hClntSock;

        if (READ == ioInfo->rwMode)
        {
            puts("message received!");

            if (0 == bytesTrans)
            {
                EnterCriticalSection(&cs);
                std::list::iterator iter;
                for (iter = UserList.begin(); iter != UserList.end(); ++iter)
                {
                    if (sock == (*iter)->hClntSock)
                    {
                        UserList.erase(iter);
                        break;
                    }
                }
                LeaveCriticalSection(&cs);

                closesocket(sock);
                free(handleInfo);
                free(ioInfo);
                continue;
            }

            memset(msg, 0, BUF_SIZE);
            memcpy(msg, ioInfo->buffer, BUF_SIZE);
            free(ioInfo);
            
            EnterCriticalSection(&cs);                      // UserList는 여러 스레드가 접근하는 공유자원이므로 접근 시 cs 필수 
            std::list::iterator iter;
            for (iter = UserList.begin(); iter != UserList.end(); ++iter)  
            {
                // 소켓마다 overlapped를 새로 만들어야 한다. 왜냐하면 하나의 overlapped를 여러 소켓이 쓸 수 없기 때문이다. 
                ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));
                memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));
                ioInfo->wsaBuf.buf = msg;                   // msg는 스레드마다 가지고 있는 지역변수이므로 공유자원이 아니므로 cs 사용을 하지 않는다
                ioInfo->wsaBuf.len = bytesTrans;
                ioInfo->rwMode = WRITE;
                WSASend((*iter)->hClntSock, &(ioInfo->wsaBuf), 1, NULL, 0, &(ioInfo->overlapped), NULL);
            }
            LeaveCriticalSection(&cs);

            ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));
            memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));
            ioInfo->wsaBuf.len = BUF_SIZE;
            ioInfo->wsaBuf.buf = ioInfo->buffer;
            ioInfo->rwMode = READ;
            WSARecv(sock, &(ioInfo->wsaBuf), 1, NULL, &flags, &(ioInfo->overlapped), NULL);
        }
        else
        {
            puts("message sent!");
            free(ioInfo);
        }
    }
    
    return 0;
}





실행하면 아래처럼 여러사람들에게 메시지가 전송된다.

내가 개인적으로 저장할 용도로 올리는거라서 수정, 추가할 부분이 많다:Q...

( 메시지큐를 이용해서 데이터 받는 부분, 처리부분 분리하기, 패킷 덜 왔을 때 합치기, acceptEx , 메모리풀 등..)



Post

예전 서버 소스코드를 실행시키려고 하니 C4996에러가 났다.


C4996

'WSASocketA': Use WSASocketW() instead  or define      _WINSOCK_DEPRECATED_NO_WARNINGS      to disable deprecated API warnings


C4996 'inet_addr': Use inet_pton() or InetPton()      instead or define      _WINSOCK_DEPRECATED_NO_WARNINGS      to disable deprecated API warnings

OverlappedSend_Win_Wori


처음 C4996은 WSASocket 대신 WSASocketW 함수를 사용하라고 해서, 사용하니 해결.

두번째 c4996 Wanring은 inet_pton() or InetPton()  함수를 사용하라고 해서 수정하니 해결


#include <WS2tcpip.h>  // inet_pton 함수 사용시 include 해야함

inet_pton(AF_INET, "127.0.0.1", &(sendAdr.sin_addr));



Post

1>main.obj : error LNK2019: __imp__closesocket@4 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__connect@12 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__htons@4 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__inet_addr@4 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__WSAStartup@8 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__WSACleanup@0 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__WSAGetLastError@0 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__WSACloseEvent@4 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__WSACreateEvent@0 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__WSAGetOverlappedResult@20 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__WSASend@28 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__WSASocketA@24 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.

1>main.obj : error LNK2019: __imp__WSAWaitForMultipleEvents@20 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.



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

lib 포함시켜주면 된다.

'이전게시판 > Server' 카테고리의 다른 글

IOCP window 채팅 클라이언트 소스  (0) 2018.10.04
IOCP 채팅 서버 소스 예제  (0) 2018.10.04
C4996 에러  (0) 2018.09.28
구글 프로토콜 버퍼 C++ Window 예제 (Google Protocol buffer)  (0) 2018.07.27
기본 서버 예제의 문제점  (0) 2018.07.05
C4996 inet_addr Error Solution  (0) 2018.06.30
ip주소 추적  (0) 2018.04.21
hosts파일, DNS  (0) 2018.04.21

Post

구글 프로토콜 버퍼 C++ Window 예제 (Google Protocol buffer)
 
구글 프로토콜 버퍼 프로젝트 다운로드 url : https://github.com/google/protobuf/releases/tag/v2.6.1
c++ 튜토리얼 url : https://developers.google.com/protocol-buffers/docs/cpptutorial
 
이 글은 아래 구글 프로토콜 버퍼 유튜브 강의를 정리한 내용입니다. 
 
구글 프로토콜 버퍼 : 구글에서 만든 데이터 직렬화 라이브러리.
 
1. Window용 구글 프로토콜 버퍼 프로젝트 다운로드
아래 사이트에서 Protocol Buffers v2.6.1 Source code(zip) 다운로드 후 압축풀기
 
 
2. protoc.exe 파일 만들기
protobuf-2.6.1\vsprojects\protobuf.sln 을 실행한다.
(이 때 gtest, gtest_main은 컨버터 실패로 사용할 수 없음 떠도 안쓰니까 괜찮다)
libprotobuf, libprotoc 프로젝트 속성 - C/C++ - 전처리기 정의
_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS 를 추가한다
libprotobuf, libprotoc, protoc 프로젝트를 빌드한다.(빌드성공)
 
빌드 후 프로젝트 Debug 폴더에 들어가면 protoc.exe 실행파일이 생성되어있다.
이제 폴더를 새로 만들고 새폴더 안에 넣는다.
(D드라이브에 만들었고 폴더 이름은 protobufapply이다.
위치는 D:\protobufapply)
 
3. proto 파일 만들고 h, cc 파일 생성
텍스트 파일을 생성한다. 이름은 addressbook.proto
그리고 proto 파일에 아래와 같이 입력한다.
syntax = "proto2";
 
package tutorial;
 
message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;
 
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
 
  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
 
  repeated PhoneNumber phones = 4;
}
 
message AddressBook {
  repeated Person people = 1;
}
 
 
그리고 cmd 창을 열고 다음 명령어 실행한다.
그러면 addressbook.pb.h, addressbook.pb.cc 파일이 생성되어있다.
protoc -I=. --cpp_out=. addressbook.proto
( 다음과 같이 사용하라고 적혀있다 protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto )
 
4. 직렬화 샘플 프로젝트 생성
protobufapply 폴더안에 ProjectProto 폴더 생성한다.
그리고 ProjectProto 안에 새 vs 프로젝트를 생성한다.
(콘솔응용프로그램이고 EmptyProject, Precompiled header, Security Development 전부 체크 해제)
vs 프로젝트 폴더안에 addressbook.pb.cc , addressbook.pb.h 파일을 넣어준다.
프로젝트 소스파일에서도 넣어준다.
 
protobuf-2.6.1\vsprojects\protobuf.sln 솔루션 폴더에서 Debug 폴더 안에 있는 libprotobuf.lib 라이브러리를 복사해서
프로젝트 솔루션창에 붙여넣는다.
( 내 솔루션 폴더 위치 : D:\protobuf-2.6.1\protobuf-2.6.1\vsprojects\Debug )
 
그리고 
D:\protobuf-2.6.1\protobuf-2.6.1\src 위치를 복사 한 다음 
프로젝트 속성 - 구성속성 - VC++ 디렉터리 - 포함디렉터리에 
src 위치를 붙여넣는다.
그리고 빌드.
 
성공이라면 아무것도 없는 Main 함수 대신 
듀토리얼 페이지에 있는 샘플코드 Writing A Message 를 붙여넣는다.
빌드 성공이라면
프로젝트 속성 - 구성속성 - 디버깅 - 명령인수에 D:\protobufapply\myPhonebook.bin 입력.
 

프로젝트 실행 후 알맞은 데이터를 입력한다.

그리고 모든 입력을 마쳤다면 leave blank to finish 메시지가 보일 때 엔터를 눌러 빠져나온다.
그리고 D:\protobufapply 폴더로 가면 myPhonebook.bin 파일이 생성되어있다.
 
 
 
 

Post

아래의 기본 서버 예제는 문제점이 있다.

클라이언트가 많아지면 접속을 제 때 받지 못한다.

또 처리하는 부분(send)도 시간이 많이 걸리면 클라이언트 접속 대기 시간이 생긴다.

둘 다 분리되어있지 않고, 원스레드에서 해결하기 때문이다.

아래는 원스레드 서버의 처리시간이 길어지면 어떤문제점이 있는지 보여주는 예제이다.


상황설정

서버는 원스레드로, 클라이언트 접속 후에 1초걸리는 일을 처리하고 패킷을 전달한다.

클라이언트는 배치 파일을 만들어서 7개가 동시에 Connect를 요청하고 서버의 응답을 기다린다.


bat 파일은 아래와 같이 작성했다.

( ConsoleApplication2.exe 가 클라이언트다.. .;; )

 start /d "C:\Users\MyDirectory\Documents\Visual Studio 2017\ConsoleApplication2\Debug\" ConsoleApplication2.exe

 start /d "C:\Users\MyDirectory\Documents\Visual Studio 2017\ConsoleApplication2\Debug\" ConsoleApplication2.exe

 start /d "C:\Users\MyDirectory\Documents\Visual Studio 2017\ConsoleApplication2\Debug\" ConsoleApplication2.exe

 start /d "C:\Users\MyDirectory\Documents\Visual Studio 2017\ConsoleApplication2\Debug\" ConsoleApplication2.exe

 start /d "C:\Users\MyDirectory\Documents\Visual Studio 2017\ConsoleApplication2\Debug\" ConsoleApplication2.exe

 start /d "C:\Users\MyDirectory\Documents\Visual Studio 2017\ConsoleApplication2\Debug\" ConsoleApplication2.exe

 start /d "C:\Users\MyDirectory\Documents\Visual Studio 2017\ConsoleApplication2\Debug\" ConsoleApplication2.exe

<server> 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
 
#define BUF_SIZE 30
 
void ErrorHandling(const char* message);
 
int main()
{
    WSADATA wsaData;
    SOCKET hServSock, hClntSock;
    SOCKADDR_IN servAddr, clntAddr;
    int strLen = 0;
 
    int szClntAddr;
    char message[] = "Hello World!";
    
    if (0 != WSAStartup(MAKEWORD(22), &wsaData))
    {
        ErrorHandling("WSAStartup() error!");
    }
 
    hServSock = socket(PF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == hServSock)
        ErrorHandling("socket() error");
 
    memset(&servAddr, 0sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servAddr.sin_port = htons(atoi("8888"));
 
    if (SOCKET_ERROR == bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)))
    {
        ErrorHandling("bind error \n");
    }
 
    if(SOCKET_ERROR == listen(hServSock, 5))
    {
        ErrorHandling("listen error \n");
    }
 
    szClntAddr = sizeof(clntAddr);
    int i = 0;
    while(true)
    {
        hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr);
        if (-1 == hClntSock)
        {
            ErrorHandling("accept() error \n");
        }
        else
        {
            printf("Connected clinet : %d \n", i + 1);
        }
        ++i;
        Sleep(1000);
 
        send(hClntSock, message, BUF_SIZE, 0);
 
        closesocket(hClntSock);
    }
    
    closesocket(hServSock);
    WSACleanup();
 
    return 0;
}
 
void ErrorHandling(const char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}
cs


<Client>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <ws2tcpip.h>
#include <windows.h>
 
#define BUF_SIZE 30
void ErrorHandling(const char* message);
 
int main()
{
    WSADATA wsaData;
    SOCKET hSocket;
    SOCKADDR_IN servAddr;
    char message[30];
    int strLen;
 
    if (0 != WSAStartup(MAKEWORD(22), &wsaData))
    {
        ErrorHandling("WSAStartup() error!");
    }
 
    hSocket = socket(PF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == hSocket)
    {
        ErrorHandling("WSAStartup() error!");
    }
    
    memset(&servAddr, 0sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    //servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    inet_pton(AF_INET, "127.0.0.1"&servAddr.sin_addr);
    servAddr.sin_port = htons(atoi("8888"));
 
    if(SOCKET_ERROR == connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)))
    {
        printf("error:%d \n", WSAGetLastError());
        ErrorHandling("connect() error");
    }
    else
    {
        puts("Connected......");
    }
    
    int beginTime = GetTickCount();        
    strLen = recv(hSocket, message, BUF_SIZE - 10);
    int endTime = GetTickCount();
    int passTime = endTime - beginTime;
    message[strLen] = 0;
    printf("Message from server ms:%d second:%f message:%s \n", passTime, (passTime * 0.001), message);
 
    closesocket(hSocket);
    WSACleanup();        
 
    system("pause");
    return 0;
}
 
void ErrorHandling(const char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}
cs


<결과>

서버는 1초짜리 일을 했지만 접속순서대로 차례대로 일을 처리했기 때문에,

후순위로 Connect한 클라이언트는 5~6초나 지나서 결과를 받았다.

멀티스레드 서버를 사용해야하는 이유다.

 


Post

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// ConsoleApplication2.cpp: 콘솔 응용 프로그램의 진입점을 정의합니다.
//
 
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <ws2tcpip.h>
 
void ErrorHandling(const char* message);
 
int main()
{
    WSADATA wsaData;
    SOCKET hSocket;
    SOCKADDR_IN servAddr;
    char message[30];
    int strLen;
 
    if (0 != WSAStartup(MAKEWORD(22), &wsaData))
    {
        ErrorHandling("WSAStartup() error!");
    }
 
    hSocket = socket(PF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == hSocket)
    {
        ErrorHandling("WSAStartup() error!");
    }
 
    memset(&servAddr, 0sizeof(servAddr));
    servAddr.sin_family = AF_INET;
//    servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    inet_pton(AF_INET ,"127.0.0.1"&servAddr.sin_addr);
    servAddr.sin_port = htons(atoi("8888"));
 
    if(SOCKET_ERROR == connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)))
    {
        ErrorHandling("connect() error!");
    }
 
    strLen = recv(hSocket, message, sizeof(message) - 10);
    if (-1 == strLen)
    {
        ErrorHandling("read() error!");
    }
 
    printf("Message from server : %s \n", message);
 
    closesocket(hSocket);
    WSACleanup();        
 
    return 0;
}
 
void ErrorHandling(const char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
 
}
 
 
cs



오류 C4996 'inet_addr': Use inet_pton() or InetPton() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API 

2017 되면서 inet_pton() 함수를 쓰면 에러가 난다.
메시지에 inet_pton() 함수를 대신 사용하라고 한다.
(_WINSOCK_DEPRECATED_NO_WARNINGS define을 추가하라고 되어있으나 에러가 나서 그냥 inet_pton()함수 사용하기로..)
해결 방법은 헤더에 ws2tcpip.h 를 추가하고 아래와 같이 inet_pton 함수를 사용하면 된다.

#include <ws2tcpip.h>
inet_pton(AF_INET(ipc4주소체계) , ip주소 , 연결할 서버주소 저장 변수);


Post

https://www.ip-tracker.org/

출발지 ip를 와이어샤크에서 확인 후 위 사이트에서 찾으면 어디쯤있는지 알 수 있다.

하지만 ip주소를 중간에서 바꿔주면 확인이 어렵다.


ip주소 추적

메일을 이용하여 ip 추적하는 법

메일의 경우 '원문보기' 라는 버튼을 누르면 더 많은 내용을 볼 수 있다.

여러 메일 서버를 거치기 때문에 전송과정이 추가적으로 붙는다.

메일 내용으로 n차 메일 서버 정보... 2차 메일서버 정보, 1차 메일서버 정보, 원문 순이다.


Post

hosts파일이란?

DNS가 만들어지기 전에 로컬에서 별명을 설정해서, IP대신 별명으로 인터넷에 접속할 수 있도록 만든 것.

DNS 서버가 작동하지 않을 때 임의로 사용. 또 악성코드로 주소를 임의로 등록하여 사용자가 원하는 사이트가 아닌

다른 사이트로 연결되게 할 수도 있다.

hosts파일 위치 : C:\Window\system32\drivers\etc\hosts 


hosts에 별명추가하고 작동확인

hosts파일에 ip , 도메인, 별명 을 차례로 추가한다.

cmd를 열어 ping ip , ping 별명을 치면 동일한 ip에 패킷을 전송하는 것을 확인할 수 있다.

또 홈페이지에서 http://별명 으로 쳐도 홈페이지에 접속할 수 있다.


125.209.222.142 www.naver.com nnnn


나는 네이버, 구글로 해봤는데.. ping ip든 ping 별명이든 요청시간 만료:Q.... 



DNS란?

숫자 IP주소를 문자로된 이름으로 사용하는 시스템이다.

DNS 이름은 다음으로 나뉜다

루트 도메인

최상위 레벨 도메인 : com, org, kr, jp

2계층 레벨 도메인 : google(희망문자열 선택), isc2, co. or

3계층 레벨 도메인 : (희망 문자열 선택)

4걔층 레벨 도메인 : www


DNS 서버 설정

제어판 - 네트워크 및 공유센터 - 이더넷 - 어댑터 설정 변경

DNS 자동으로 IP 주소받기 시 DNS 서버가 꺼져있다면 IP할당이 되지 않는 단점이 있다.


우리집에는 '자동으로 IP 주소 받기'가 되어있길래 다음 IP주소 사용으로

임의의 IP 입력 후 ipconfig로 ip 주소를 확인하였으나 자동으로 받은 IP주소와 같았다..

찾아보니 대역대 설정도 해야한다는데 좀 더 공부한 뒤에 다시 해보자


클라이언트가 임의의 홈페이지에 접속하는 과정은 아래와 같다.

임의 사이트 ip 주소가 캐시에 남아있는가?->없다면 hosts파일 참조->없다면 로컬 DNS 서버에 문의

->없다면 루트 DNS 서버에 문의->없다면 com DNS서버에 문의..


ipcofnig /displaydns : 이미 조회한 DNS 정보 보기 명령어

ipconfig / flushdns  : 캐시된 DNS 정보 지우기


DNS 서버 도메인 별 정리

주 DNS 서버 : 도메인의 중심 DNS 서버

부 DNS 서버 : 주 DNS 서버의 백업 서버

캐시 DNS 서버 : 주 DNS 서버와 부 DNS 서버에 대한 접속이 불가능할 때를 대비한 임시 DNS 서버

Post

dhcp 란 ? ip, 네임 서버, 게이트웨이 자동으로 할당해주는 프로토콜 ( Dynamic Host Configuration Protocol )
dhcp 서버가 꺼지면 그 이전에 ip할당받은 컴퓨터는 사용가능하지만
ip 할당받지 못한 컴퓨터(늦게 접속한 컴퓨터)는 서버 접속이 안된다.

▲ top