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

VBA 오류들

[ etc ] 2018. 6. 22. 18:19

 

에러
1004 런타임 오류가 발생하였습니다.
응용 프로그램 정의 오류 또는 개체 정의 오류입니다.
해결
파일 위치가 잘못되어서 해당 파일을 찾을 수 없어서 나던 에러
 

 

이 프로젝트에 있는 매크로는 사용할 수 없습니다. 매크로를 사용할 수 있게 만드는 방법을
결정하려면 온라인 도움말이나 온라인 설명서를 참고하십시오.
해결
보안설정 수준을 보통으로 만듬
옵션 - 보안센터 - 보안센터 설정 버튼 클릭 - 매크로 설정 - 모든 매크로 포함(위험성 있는 코드가 실행될 수 있으므로 권장하지 않음) 

 

 
난 별로 중요한 파일아니라서 이렇게 설정함.
그 외에 신뢰할 수 있는 위치에 해당 파일 위치를 넣어주면 됨..
 

 

9 런타임 오류가 발생하였습니다.
아래 첨자 사용이 잘못되었습니다
해결
파일 이름을 잘못넣어줘서 파일을 찾을 수 없어 나는 에러였다. 
 
컴파일 오류입니다: 필요한 요소: 문의 끝
비주얼 베이직은 세미콜론을 끝에 안붙임.
 
 
보통 파일 위치나 파일 이름이 잘못되어 파일을 찾을 수 없어서 에러가 많이 났다.
 
 

 

Post

서버에서 패킷을 받아서 데이터 파서 중 이해가 안가는 부분이 생겼다.
문자열 배열이 있을 때 2Byte씩 파싱하는 중이었다.
그런데 2byte 파싱한 데이터가 결과가 내 예상과는 달랐다.
확인한 데이터는 배열 0번째는 154, 배열 1번째는 8이었다.

예시소스코드
unsigned char tempString[2];
tempString[0] = 154;
tempString[1] = 8;
unsigned short data;
memcpy(&data, tempString, 2);

154는 2진수로 10011010
8은 2진수로 1000
결과는 10011010 1000 = 100110101000 = 2472인 줄 알았다
그런데 값이 안맞아서 확인해보니
tempString[0]이 0번째이므로 0~255값을 나타냈기 때문에 뒤로 가야한다.
그래서 10011010 1000 자리를 바꿔줘서 합쳐줬더니 알맞은 값이 나온다.
1000 10011010 100010011010 = 2202

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// ConsoleApplication1.cpp: 콘솔 응용 프로그램의 진입점을 정의합니다.
//
 
#include "stdafx.h"
#include <vector>
using namespace std;
 
class Flyweight
{
public:
    struct Stock
    {
        int nType;    // 0 : 전사 1 : 마법사
        int nMaxHP;
        int nStr;
        int nDef;
    };
 
private:
    Stock m_stock;
 
public :
 
    void SetStock(Stock stock)
    {
        m_stock = stock;
    }
 
    Stock& GetStock()
    {
        return m_stock;
    }
 
    void Print(int index)
    {
        printf("유닛%d Type:%d MaxHP:%d \n", index, m_stock.nType, m_stock.nMaxHP);
    }
 
};
 
class Unit
{
private:
    Flyweight * m_pFly;
 
public :
    void SetFly(Flyweight* pFly)
    {
        m_pFly = pFly;
    }
 
    Flyweight* GetFly()
    {
        return m_pFly;
    }
};
 
class Container
{
    vector<Flyweight*> m_Container;
 
public:
    ~Container()
    {
        vector<Flyweight*>::iterator it;
        for (it = m_Container.begin(); it != m_Container.end();)
        {
            it = m_Container.erase(it);
        }
    }
 
    void Insert(int nType)
    {    
        Flyweight* pNewFly = new Flyweight();
        
        Flyweight::Stock stock;
        // 타입은 전사, 마법사로 나뉘고 타입별로 스탯은 동일하다
        if (0 == nType)
        {
            stock.nType = 0;
            stock.nMaxHP = 100;
            stock.nStr = 20;
            stock.nDef = 10;
        }
        else
        {
            stock.nType = 1;
            stock.nMaxHP = 50;
            stock.nStr = 40;
            stock.nDef = 5;
        }
        pNewFly->SetStock(stock);
        m_Container.push_back(pNewFly);
    }
 
    Flyweight* Search(int nType)
    {
        Flyweight* pFly = m_Container.at(nType);
        return pFly;
    }
};
static Container UnitContainer;
 
// 유닛 생성
class UnitManager
{
    vector<Unit*> m_UnitList;
 
public :
    ~UnitManager()
    {
        vector<Unit*>::iterator it;
        for (it = m_UnitList.begin(); it != m_UnitList.end();)
        {
            it = m_UnitList.erase(it);
        }
    }
 
    void Create(int nType)
    {
        Unit* pUnit = new Unit();
 
        Flyweight* pFly = NULL;
        pFly = UnitContainer.Search(nType);
        pUnit->SetFly(pFly);
        m_UnitList.push_back(pUnit);
    }
 
    Unit* Get(int i)
    {
        return m_UnitList.at(i);
    }
};
 
int main()
{
    // Fly 정보 추가
    UnitContainer.Insert(0);
    UnitContainer.Insert(1);
 
    UnitManager unitManager;
    for (int i = 0; i < 10++i)
    {
        unitManager.Create(0);
    }
 
    for (int i = 0; i < 10++i)
    {
        unitManager.Create(1);
    }
 
    unitManager.Get(1)->GetFly()->Print(1);
    unitManager.Get(3)->GetFly()->Print(3);
    unitManager.Get(9)->GetFly()->Print(9);
    unitManager.Get(10)->GetFly()->Print(10);
    unitManager.Get(15)->GetFly()->Print(15);
    unitManager.Get(19)->GetFly()->Print(19);
 
 
    //getchar();
}
 
 
cs



출력결과

유닛1 Type:0 MaxHP:100

유닛3 Type:0 MaxHP:100

유닛9 Type:0 MaxHP:100

유닛10 Type:1 MaxHP:50

유닛15 Type:1 MaxHP:50

유닛19 Type:1 MaxHP:50



flyweight패턴

사용상황 : 디팬스 게임에서 내가 정중앙에 있고 적들이 사방에서 엄청 몰려올 때, 적의 수 만큼 데이터가 필요하다.

너무 많은 데이터가 필요하다면 공통된 정보는 공유해서 사용하는 방법이 Flyweight 패턴이다.

위 예제는 타입, 최대hp, 공격력, 방어력이 유닛타입(전사, 마법사) 에 따라 같다고 했을 때, 이는 공통되는 속석이므로,

공통되는 속성을 Flyweight로 묶어서 Unit이 참조하게 만들었다. 

 

Post

VisualStudio 줄번호 바로가기 단축키 : Ctrl + G

Post

tortoisesvn Path

[ etc ] 2018. 5. 30. 17:21

tortoisesvn Path : 내가 A에서 작업한 내용을 B폴더로 가져가서 작업할 때 유용

A폴더를 마우스 오른쪽 클릭 - tortoiseSVN - Create Path


패치 파일 생성.

이제 B폴더로 안으로 들어가서 - tortoiseSVN - Apply Path - All Item


A폴더는 변경된 파일을 모두 가지고 있는 폴더

B폴더도 마찬가지지. 다만 패치 생성 때는 폴더 안에 들어 Apply Path를 해야함.

Post

const : 변수가 상수가 되도록 정의하는 키워드


* 함수의 인자에 const 키워드를 선언한다면 구현 부분에도 const를 추가한다

선언부

void PrintNumber(const int nValue);


구현부

void PrintNumber(const int nValue)

{

printf("%d \n", nValue);

}


* 멤버변수의 값이 멤버함수 내에서 변경되지 않도록 만들려면 함수 선언 맨 뒤에 const를 추가한다.

(지역변수는 const 함수 내에서도 값 변경이 된다)


선언부

class Level

{

int nLevel = 0;

public:

// 함수안에서 값을 변경할 수 없을 때

void SetLevel(int nValue) const;

};


구현부

void Level::SetLevel(int nValue) const

{

int temp = nValue;

//nLevel = nValue;


* const 키워드를 포인터 변수에 사용할 시 위치에 따라 의미가 달라진다.


const int* p = &a;

const가 맨 앞에 있는 경우 : 포인터가 가르키는 변수의 값을 변경시키지 못한다.

*p = 22;   (X)



int* const p = &a;

p = &b;   (X)
const가 포인터* 뒤에 있는 경우 : 포인터가 가르키는 주소를 변경하지 못한다. 

 

Post

애니메이션 끝나고 함수 호출하는 법

해당 애니메이션 더블클릭

=>Inspector-Animation-Event에서 

Function에 호출할 함수 이름을 적는다.

Post

BuildSetting=>PlayerSetting


Other Setting 

Identification 중요

앞에있는 버전 : 실행파일 버전(환경이 바뀌었을 때.. 구글스토어 업뎃)

뒤에(Bundle Version Code) : 데이터바뀔 때 (게임에서 업뎃)

Minimum API Level : 최소버전..어느버전서부터호환시킬것인지?

Target API Level : 권장사항


Resoultion and Presention

Defualt Orientation : AutoRotion (막돌아감)


Splash Image : 로고(공짜면 로그인 로그 안없어짐)


publishing Settings : 퍼블리싱 키 코드받아서 작업..(예를들어 n스토어면 n스토어 키)


Unity Proferense


유니티 5.6 버전 => jdk Java SE 8u171/ 8u172 다운

설치 위치를 넣어주면 된다


sdk위치 : C:/Users/Administrator.Sc-201712221631/AppData/Local/Android/Sdk

jdk위치 : C:/Program Files/Java/jdk1.8.0_161




====================================


3D애니메이션과 프리팹은 다름

=> 3D애니메이션은 transform이 아니라 모델 다체의 스케일 팩터를 수정해야한다!

▲ top