태그 보관물: c

[C] Echo server/client

대학교 이후로 거의 접해지 못했던 C 를 다시 시작하고 있습니다.

다시 시작하는 이유는 회사에서 C 개발자가 필요하기 때문입니다. 처음이라 진입이 쉽지는 않겠지만 포기하지 않고 계속 하다 보면 언젠가는 되리라 생각합니다.

예제는 http://www.joinc.co.kr/w/Site/win_network_prog/doc/winsock_basic 의 코드를 참조하며 모르는 부분에 주석을 추가하였습니다.

지속적으로 이전에 java로 만들었던 채팅 서버, 메신저 서버 등을 만들어볼 생각입니다.

개발환경

  • IDE: Visual Studio 2012
  • OS: Windows 10

[echo_server.c]

/*
File: echo_server.c
Desc: 에코 서버.
        서버는 클라이언트의 접속 요청을 수락한 후 송신된 메시지를 출력 및 동일 메시지 송신 후 클라이언트의 연결을 종료하는 프로그램
Date: 2016. 04. 27
Author: coozplz@gmail.com
*/
#include <winsock.h>
#include <stdio.h>

// Winsock 라이브러리 로딩을 위한 코드
#pragma comment(lib, "Ws2_32.lib");


#define MAX_PACKET_LEN 512
#define PORT 5552
#define BACK_LOG_SIZE 5

int main()
{
    WSADATA wsaData;
    int status;
    int socketLength;
    int readSize, writeSize;
    SOCKET endPointSocket, clientSocket;

    struct sockaddr_in SocketInfo, ClientSocketInfo;

    char buffer[MAX_PACKET_LEN];

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup error \n");
        return 0;
    }

    endPointSocket = socket(AF_INET, SOCK_STREAM, 0);

    if (endPointSocket == INVALID_SOCKET) {
        printf("endPointSocket is invalid \n");
        return 0;
    }

    printf("Server is running on port %d\n", PORT);

    // 구조체를 초기화한다.
    ZeroMemory(&SocketInfo, sizeof(struct sockaddr_in));
    SocketInfo.sin_family = AF_INET;
    // htons(): Host 시스템에서 Network 로 short 형 데이터를 보낼 때 바이트 오더를 바꿔주는 함수
    SocketInfo.sin_port = htons(PORT); 

    // htonl(): long형 데이터의 바이트 오더를 바꿔주는 함수.
    // INADDR_ANY 는 OS에 설정되어 있는 아이피 어느것이나 바인딩 한다는 의미
    SocketInfo.sin_addr.S_un.S_addr = htonl(INADDR_ANY); 

    status = bind(endPointSocket, (struct sockaddr*)&SocketInfo, sizeof(struct sockaddr_in));

    if (status == SOCKET_ERROR) {
        printf("failed to bind socket \n");
        return 0;
    }

    //
    // backlog 인자는 아직 미결인 연결들에 대한 큐의 늘어날 수 있는 최대 길이를 정의한다.
    // 큐에 도착한 연결 요청들이 꽉 찬다면 클라이언트는 ECONNREFUSED를 가리키는 에러를 받거나 만일 하위 프로토콜이 재전송을 지원한다면, 요청은 재시도가 성공되도록 하기 위해 무시된다.
    //
    status = listen(endPointSocket, BACK_LOG_SIZE);

    if (status == SOCKET_ERROR) {
        printf("failed to listen socket \n");
        return 0;
    }

    while (TRUE) {
        // 클라이언트 구조체 초기화
        ZeroMemory(&ClientSocketInfo, sizeof(struct sockaddr_in));
        socketLength = sizeof(struct sockaddr_in);

        clientSocket = accept(endPointSocket, (struct sockaddr *) &ClientSocketInfo, &socketLength);
        if (clientSocket == INVALID_SOCKET) {
            printf("client socket is invalid\n");
            closesocket(clientSocket);
            continue;
        }

        // inet_ntoa() : 네트워크 바이트 순서의 32비트 값을 Dotted-Decimal notation 의 주소값으로 변환
        // 예 0x6601a8c0: 102.1.168.192
        printf("[Accepted] %s:%d\n", inet_ntoa(ClientSocketInfo.sin_addr), (int)ntohs(ClientSocketInfo.sin_port));
        ZeroMemory(&buffer, sizeof(buffer));
        readSize = recv(clientSocket, (void *)buffer, MAX_PACKET_LEN, 0);

        if (readSize > 0) {
            printf("Message from client > %s\n", buffer);
            writeSize = send(clientSocket, buffer, readSize, 0);
        }
        else {
            printf("Error on reading\n");
        }
        closesocket(clientSocket);
        printf("[Closed] %s:%d\n", inet_ntoa(ClientSocketInfo.sin_addr), (int)ntohs(ClientSocketInfo.sin_port));
    }
    closesocket(endPointSocket);
    WSACleanup();
    return 0;
}

[echo_client.c]

/*
File: echo_client.c
Desc: 에코 클라이언트.
      서버에 접속 요청을 한 후 사용자가 입력한 메시지를 서버에 송신 후 응답 메시지를 출력한 후 소켓 연결이 종료되었는지 판단하고 소켓 연결을 종료한다.
Date: 2016. 04. 27
Author: coozplz@gmail.com
*/

#include <stdio.h>
#include <WinSock2.h>

// Winsock 라이브러리 로딩을 위한 코드
#pragma comment(lib, "Ws2_32.lib");


#define PORT_NUM 5552
#define MAX_LEN 1024

int main(int argc, char **argv)
{
    SOCKET socketFd;
    WSADATA wsaData;
    struct sockaddr_in addr;

    char buffer[MAX_LEN];
    char receiveBuffer[MAX_LEN];

    int status = 0;
    int readSize = 0;
    status = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (status != NO_ERROR) {
        printf("WSAStart has error\n");
        return 1;
    }

    socketFd = socket(AF_INET, SOCK_STREAM, 0);

    if (socketFd == INVALID_SOCKET) {
        printf("socketFd is invalid\n");
        return 1;
    }
    ZeroMemory(&addr, sizeof(struct sockaddr_in));

    addr.sin_family = AF_INET;
    //
    // inet_addr() : Dotted-decimal 타입을 네트워크 32바이트 순서로 변경
    //
    addr.sin_addr.S_un.S_addr = inet_addr("192.168.10.52");
    addr.sin_port = htons(PORT_NUM);

    status = connect(socketFd, (struct sockaddr_in *)&addr, sizeof(addr));

    if (status == SOCKET_ERROR) {
        printf("connection failed...\n");
        return 1;
    }

    while (TRUE) {
        printf("Connection is established...\n");
        printf("> ");
        fgets(buffer, MAX_LEN - 1, stdin);
        if (strncmp(buffer, "quit\n", 5) == 0) {
            break;
        }

        printf("SEND: %s", buffer);
        send(socketFd, (void *)buffer, strlen(buffer), 0);
        ZeroMemory(&receiveBuffer, sizeof(receiveBuffer));
        readSize = recv(socketFd, (void *)receiveBuffer, MAX_LEN, 0);
        printf("READ = %s\n", receiveBuffer);
        //
        // 소켓연결이 종료되었는지 판단하기 위해 다시 recv() 함수를 호출한다.
        //
        ZeroMemory(&receiveBuffer, sizeof(receiveBuffer));
        readSize = recv(socketFd, (void *)receiveBuffer, MAX_LEN, 0);
        if (readSize <= 0) {
            printf("socket is disconnected by server!!\n");
            break;
        }
    }
    closesocket(socketFd);
    WSACleanup();

    printf("Press any key to exit program.");
    getchar();
    return 0;
}
이 글은 C 카테고리에 분류되었고 , 태그가 있으며 님에 의해 에 작성되었습니다.

야구 게임

[게임 방법]
컴퓨터가 1-9까지 서로 다른 숫자 3개를 저장한다.
사용자가 1-9까지의 숫자를 3개 입력 한다.
컴퓨터가 가지고 있는 자리와 숫자가 일치 하면 STRIKE, 숫자만 일치 하면 BALL 이고
그렇지 않은 경우 아무것도 아니다.

[소스 코드]

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>



void main(void)
{
    int computer[3] = { 0, };
    volatile int gamer[3] = { 0, };
    int guess[9] = { 0, };
    int count = 0, i = 0;
    int strike, ball;
    int inputCount = 0;
    char yesno;

    srand(time(NULL));
    puts("야구 게임을 시작합니다.");

    while (1)
    {
        computer[0] = (rand() % 9) + 1;
        computer[1] = (rand() % 9) + 1;
        computer[2] = (rand() % 9) + 1;
        count = 1;
        inputCount = 0;

        if (computer[0] == computer[1]
            || computer[0] == computer[2]
            || computer[1] == computer[2])
            continue;

        // puts("숫자 1-9를 공백으로 분리하여 3개를 입력  ");

        memset(guess, 0, sizeof(guess));

        // printf("%d %d %d \n", computer[0], computer[1], computer[2]);


        while (1)
        {
            strike = ball = 0;

            printf("\n 3개의 숫자[0-9]를 입력하세요: ");
            scanf("%d %d %d", &gamer[0], &gamer[1], &gamer[2]);



            if (computer[0] == gamer[0]) {
                strike++;
            }
            else if (computer[0] == gamer[1] || computer[0] == gamer[2]) {
                ball++;
            }

            if (computer[1] == gamer[1]) {
                strike++;
            }
            else if (computer[1] == gamer[0] || computer[1] == gamer[2]) {
                ball++;
            }

            if (computer[2] == gamer[2])  {
                strike++;
            }
            else if (computer[2] == gamer[1] || computer[2] == gamer[0]) {
                ball++;
            }

            guess[gamer[0] - 1] = 1;
            guess[gamer[1] - 1] = 1;
            guess[gamer[2] - 1] = 1;


            printf("\n[%2d회]  %d %d %d  S: %d\tB: %d \n\n", count, gamer[0], gamer[1], gamer[2], strike, ball);
            if (strike == 3)  {
                printf("삼진 아웃 입니다.\n\n");
                break;
            }

        }
        printf("게임을 계속하겠습니까(y/n)? ");
        scanf("%c", &yesno);
        if (yesno == 'N' || yesno == 'n')
            break;

    }

}

이 글은 C 카테고리에 분류되었고 태그가 있으며 님에 의해 에 작성되었습니다.

atoi 구현

[출처] http://bluemir7.tistory.com/91

C 라이브러리에 보면
문자열을 숫자로 변환해주는 atoi 이라는 함수가 있다.
하지만 특정 시스템에 따라서는 해당 함수를 지원하지 않는 경우가 있는데
이때, atoi 함수를 구현하여 사용하여야 한다.
구현 방법 또한 단순하다. 단 지금 정리하는 구현 방법이 최적의 방법이라고 볼 수는
없을 것이다. 또 다른 방법이 존재할 지도…;;
구현 내용은 다음과 같다.

[알고리즘]
1. 변환하고자 하는 문자열을 가져온다.
2. 문자열을 첫 지점부터 ”를 만날때 까지 각 자리 문자를 체크하며 이때 해당 문자는 ‘0’ 보다 크고 ‘9’ 작아야 한다. [조건] 문자 != ” && (문자 >= ‘0’ && 문자 <= '9') 조건을 만족할 경우 '3' 으로 만족하지 않을 경우 '4'
3. 한자리 문자를 가져오며 다음 식에 의해 숫자 값으로 변환한다.
[식] y = ((int)x – (int)'0') + (10 * y)
x : 대상 문자
y : 변환된 숫자
다음 문자 처리를 위해 '2'으로..
4. 최종 변환됫 값을 반환한다.

[소스 코드]

int atoi(char * cTarget)
{

    int iChangeValue = 0;
    while (*cTarget != '' && (*cTarget >= '0' && *cTarget <= '9'))
    {
        // 현재 문자 변환시켜 기존에 있는 숫자에 더해준다.
        iChangeValue = (iChangeValue * 10) + ((int)*cTarget - (int)'0');
        cTarget++;   // 다음 문자로 이동
    }
    return iChangeValue;
}

위에서 ((int)*cTarget – (int)'0') 는 문자의 아스키 값을 숫자로 변환 했을때의 값을 계산하여
숫자로 치환 한것이다. 즉, *cTarget 값이 '1'이라면 '1'은 아스키 값이 49 이고 '0'은 아스키값이 48 이므로
49 -48을 하게되면 1이 반환된다.
(10 * iChangeValue) 이 부분은 기존 값을 한자리씩 앞으로 옮기는 역활을 담당한다.
즉, 현재 값이 2 이고 기존에 1이 있다면 (1 * 10) + 2 가 되므로 12를 반환하게된다

이 글은 C 카테고리에 분류되었고 태그가 있으며 님에 의해 에 작성되었습니다.

빌드에서 제외하기

VisualStudio 에서는 환경설정 들어가면 빌드에서 제외하기가 바로 나와서 테스트 하기 좋았는데 이클립스는 조금 다른 방법입니다.
파일 우클릭 –> Resource Configurations –> Exclude from Build 체크 하면 해제 됩니다.

201202161224

이 글은 Tools 카테고리에 분류되었고 태그가 있으며 님에 의해 에 작성되었습니다.

Eclipse Scanf

[출처] http://blog.naver.com/PostView.nhn?blogId=bboy6604&logNo=20105208581

이클립스를 사용하여 C언어 공부를 하려고 하는데 초급 예제다 보니 Scanf 명령을 사용하여 데이터 입력값을 처리하는데 이상하게 VisualStudio에서는 잘되던데 Eclipse에서만 하면 입력전에 어떤 데이터를 입력하라고 하는 부분이 출력이 안되어 검색하니 printf 이후 버퍼를 비워야 한다고 합니다.

fflush(stdout);

int main()
{
    int kor[2], eng[2];
    int total[2];

    double avg[2];
    int i;

    for(i=0; i<2; i++)
    {
        printf(" 사용자 국어 영어 :\n");
//        fflush(stdout);
        scanf("%d %d", kor + i, eng + i);
    }

    for(i=0; i<2; i++)
    {
        total[i] = kor[i] + eng[i];
        avg[i] = total[i] / 2.;
    }
    for(i=0; i<2; i++)
        printf("%d %d %d %5.2lf\n", kor[i], eng[i], total[i], avg[i]);
    return 0;
}

12번 라인 주석 후 출력 결과
빨간 글씨는 사용자 입력 데이터입니다.

50 50
50 50

사용자 국어 영어 :
사용자 국어 영어 :
50 50 100 50.00
50 50 100 50.00

 

12번 라인 주석 해제 후 출력 결과

 

사용자 국어 영어 :
50 50
사용자 국어 영어 :
50 50
50 50 100 50.00
50 50 100 50.00

이 글은 C 카테고리에 분류되었고 , 태그가 있으며 님에 의해 에 작성되었습니다.