5.2 채팅 클라이언트 구현 [TCP/IP 소켓 프로그래밍 with 윈도우즈]

이번에는 채팅 클라이언트를 구현합시다. 먼저 서버에 약속한 포트 번호와 메시지 크기를 매크로 상수로 정의하고 서버의 IP 주소를 정의합시다. 여러분께서는 자신에 맞게 서버 IP 주소를 변경하세요.

#define PORT_NUM      10200
#define MAX_MSG_LEN   256
#define SERVER_IP     "192.168.34.50" //서버 주소

int main()
{

먼저 윈속을 초기화합니다.

    WSADATA wsadata;
    WSAStartup(MAKEWORD(2,2),&wsadata);//윈속 초기화

그리고 소켓을 생성합니다.

    SOCKET sock;
    sock = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);//소켓 생성
    if(sock == -1)
    {
        return -1;
    }

서버에 연결 요청합니다.

    SOCKADDR_IN servaddr={0};//소켓 주소
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(SERVER_IP);
    servaddr.sin_port = htons(PORT_NUM);
    int re = 0;
    re = connect(sock,(struct sockaddr *)&servaddr,sizeof(servaddr));//연결 요청
    if(re == -1)
    {
        return -1;
    }

서버와 연결한 후에는 사용자가 입력한 문자열을 서버에 전송하는 작업과 서버로부터 수신한 정보를 콘솔 화면에 출력하는 작업이 있습니다. 이 두 개의 작업은 서로 독립적으로 수행해야 합니다. 여기에서는 서버로부터 수신한 정보를 콘솔 화면에 출력하는 작업을 별도의 스레드 진입점을 작성하여 처리하기로 합시다.

    _beginthread(RecvThreadPoint,0,(void *)sock);

사용자로부터 문자열을 입력받아 전송합니다. 만약 입력한 문자열이 “exit”이면 종료하기로 합시다.

    char msg[MAX_MSG_LEN]="";
    while(true)
    {
        gets_s(msg,MAX_MSG_LEN);        
        send(sock,msg,sizeof(msg),0);//송신
        if(strcmp(msg,"exit")==0)
        {
            break;
        }
    }

소켓을 닫고 윈속을 해제화합니다.

    closesocket(sock);//소켓 닫기    
    WSACleanup();//윈속 해제화
    return 0;
}

서버로부터 메시지를 수신하여 콘솔 화면에 출력하는 스레드 진입점 함수를 작성합시다.

void RecvThreadPoint(void *param)
{

스레드 진입점 인자로 전달받은 소켓을 참조합니다.

    SOCKET sock = (SOCKET)param;
    char msg[MAX_MSG_LEN];

수신한 메시지 길이가 0보다 크면 수신한 정보를 출력하는 것을 반복합니다.

    while(recv(sock,msg,MAX_MSG_LEN,0)>0)
    {
        printf("%s\n",msg);
    }

수신한 메시지 길이가 0보다 작거나 같으면 연결이 끊겼거나 오류가 발생한 것이므로 소켓을 닫습니다.

    closesocket(sock);
}

다음은 이번 실습에서 작성한 채팅 클라이인트 소스 코드입니다.

#include "common.h"
#define PORT_NUM      10200
#define MAX_MSG_LEN   256
#define SERVER_IP     "192.168.34.50"//서버 IP 주소

void RecvThreadPoint(void *param);
int main()
{
    WSADATA wsadata;
    WSAStartup(MAKEWORD(2,2),&wsadata);//윈속 초기화	
    
    SOCKET sock;
    sock = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);//소켓 생성
    if(sock == -1)
    {
        return -1;
    }

    SOCKADDR_IN servaddr={0};//소켓 주소
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(SERVER_IP);
    servaddr.sin_port = htons(PORT_NUM);

    int re = 0;
    re = connect(sock,(struct sockaddr *)&servaddr,sizeof(servaddr));//연결 요청
    if(re == -1)
    {
        return -1;
    }
    _beginthread(RecvThreadPoint,0,(void *)sock);
    char msg[MAX_MSG_LEN]="";
    while(true)
    {
        gets_s(msg,MAX_MSG_LEN);        
        send(sock,msg,sizeof(msg),0);//송신
        if(strcmp(msg,"exit")==0)
        {
            break;
        }
    }    
    closesocket(sock);//소켓 닫기    

    WSACleanup();//윈속 해제화
    return 0;
}
void RecvThreadPoint(void *param)
{
    SOCKET sock = (SOCKET)param;
    char msg[MAX_MSG_LEN];
    
    SOCKADDR_IN cliaddr={0};
    int len = sizeof(cliaddr);

    while(recv(sock,msg,MAX_MSG_LEN,0)>0)
    {
        printf("%s\n",msg);
    }
    closesocket(sock);
}