C&C++/소켓 프로그래밍

[C/C++] VSCode 윈도우 기반 서버, 클라이언트 예제

DeveloperJW 2022. 7. 22. 15:39

우선 서버, 클라이언트 코드부터 보겠습니다.

 

- server.c

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

void ErrorHandling(char *message);

int main(int argc, char *argv[])
{
       WSADATA wsaData;
       SOCKET hServSock, hClntSock;
       SOCKADDR_IN servAddr, clntAddr;
 
       int szClntAddr;
       char message[] = "Hello World!";
       if(argc != 2)
       {
              printf("Usage:%s <port>\n", argv[0]);
              exit(1);
       }

       if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) //소켓 라이브러리 초기화
              ErrorHandling("WSAStartup() error!");

       hServSock = socket(PF_INET, SOCK_STREAM, 0); //소켓생성
       if(hServSock == INVALID_SOCKET)
              ErrorHandling("socket() error");

       memset(&servAddr, 0, sizeof(servAddr));
       servAddr.sin_family = AF_INET;
       servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
       servAddr.sin_port = htons(atoi(argv[1]));

       if(bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) //소켓에 IP주소와 PORT 번호 할당
              ErrorHandling("bind() error");

       if(listen(hServSock, 5) == SOCKET_ERROR) //listen 함수호출을 통해서 생성한 소켓을 서버 소켓으로 완성
              ErrorHandling("listen() error");


       szClntAddr = sizeof(clntAddr);
       hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr); //클라이언트 연결요청 수락하기 위해 accept함수 호출

       if(hClntSock == INVALID_SOCKET)
              ErrorHandling("accept() error");

       send(hClntSock, message, sizeof(message), 0); //send함수 호출을 통해서 연결된 클라이언트에 데이터를 전송
       closesocket(hClntSock);
       closesocket(hServSock);
       WSACleanup(); //프로그램 종료 전에 초기화한 소켓 라이브러리 해제

       return 0;
}

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

 

 

- client.c

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

void ErrorHandling(char *message); 

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    SOCKET hSocket;
    SOCKADDR_IN servAddr;

    char message[30];
    int strLen;
    if(argc != 3)
    {
        printf("Usage:%s <IP> <port>\n", argv[0]);
        exit(1);
    }

    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(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = inet_addr(argv[1]);
    servAddr.sin_port = htons(atoi(argv[2]));

    if(connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) //생성된 소켓을 바탕으로 서버에 연결요청을 하고 있다
        ErrorHandling("connect() error!");

    strLen = recv(hSocket, message, sizeof(message) -1, 0); //recv 함수 호출을 통해서 서버로부터 전송되는 데이터를 수신하고 있다.

    if(strLen == -1)
        ErrorHandling("read() error");

    printf("Message from server:%s\n", message);
    closesocket(hSocket); //소켓 라이브러리 해제
    WSACleanup();

    return 0;
}

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

프로그램을 작성하고 실행하시면 되는데 우선 서버 프로그램 예제부터 실행하시고 클라이언트 예제를 실행하셔야 정상적으로 작동합니다.

 

VSCode로 작성하신분은 라이브러리가 링킹이 안되어있어 undefined reference to `WSAStartup@8' 같은 오류가 뜨는데 gcc 또는 g++ 을 이용한 컴파일 시 아래 명령처럼 ws2_32 라이브러리를 직접 링킹 하면 됩니다.

 

gcc -o C:\Users\server.exe C:\Users\server.c -lws2_32 -Wall

gcc -o C:\Users\client.exe C:\Users\client.c -lws2_32 -Wall

 

-l 옵션은 링크할 라이브러리 이름을 입력하는 옵션입니다.

-Wall 옵션은 컴파일 시 존재하는 모든 경고 메시지를 출력하라는 기능을 합니다.

-o 옵션은 소스파일(HelloWorld.c)를 빌드하고 그 결과물(HelloWorld.exe)를 만드는 기능을 합니다.

 

 

위와같은 과정을 마치신 후,

명령프롬프트 두개를 켜서 server부터 실행파일을 아래와 같이 실행시켜 줍니다.

server.exe 포트번호

정상적으로 실행이 된다면 위와 같이 cmd창이 멈추고 커서가 깜빡이기만 할 것입니다. 

 

그 후 client 실행파일을 아래와 같이 실행시켜 줍니다.

client.exe 127.0.0.1 포트번호

정상적으로 작동이 된다면 이렇게 서버로부터 Hello World!라는 메세지를 받으실 수 있을 것입니다.

(127.0.0.1은 자신의 PC를 가리키는 주소입니다)

 

[참고]:윤성우 저 TCP/IP 소켓 프로그래밍