Programming/Windows Socket Programming

Basic model(Chat Server) - Code

퀴노아 2020. 3. 1. 20:39

ConsoleChatServer.cpp

#include "stdafx.h"
#include <WinSock2.h>
#pragma comment(lib,"ws2_32")

#include <list>
#include <vector>
#include <iterator>
using namespace std;
std::list<SOCKET> socket_list; //동적배열
std::list<SOCKET>::iterator it;//동적배열 접근자

DWORD WINAPI CommunicationThread(LPVOID pParam); 
DWORD WINAPI ConnectionThread(LPVOID pParam);

DWORD WINAPI ConnectionThread(LPVOID pParam) //접속처리용 스레드
{
    SOCKET hSocket = (SOCKET)pParam;

    //5. 서버 소켓을 Accept로 전환
    SOCKADDR_IN    clientaddr = {0};
    int nAddrLen = sizeof(clientaddr);
    SOCKET hClient = 0;

    while((hClient = ::accept(hSocket, (SOCKADDR*)&clientaddr, &nAddrLen)) != INVALID_SOCKET) //연결되면 한바퀴 돈다. 그건while문 때문에 아니라 accept가 blocking함수이기 때문에 while문 안에서 계속 잡고있기 때문이다.
    {
        socket_list.push_back(hClient); //클라이언트가 하나 붙을 때 마다 즉시 핸들을 리스트에 넣어준다.        
        CreateThread(NULL,0,CommunicationThread,(LPVOID)hClient,0,NULL); //Communication스레드 생성
    }
    ::closesocket(hSocket);//서버소켓 종료
    ::WSACleanup();//윈속 정리
    return 0;
}

DWORD WINAPI CommunicationThread(LPVOID pParam) //연결된놈과 통신만하는 스레드
{
    SOCKET hClient = (SOCKET)pParam;

    char szBuffer[32] = {0};
    int nReceive = 0;
    puts("클라이언트 연결성공");
    while((nReceive = ::recv(hClient,szBuffer,sizeof(szBuffer),0)) >0 ) //클라이언트가 붙어있는동안은 send지속 //클라이언트소켓으로부터 받아오는거지..
    {
        //::send(hClient,szBuffer,sizeof(szBuffer),0);
        for(it = socket_list.begin(); it != socket_list.end(); ++it)
        {
            ::send(*it,szBuffer,sizeof(szBuffer),0); //list에 들어있는 전체 핸들에 send.. 즉 붙어있는 모든 클라이언트에 메시지 발송
        }
    }
    socket_list.remove(hClient);//클라이언트가 접속 종료할때는 리스트에서 클라이언트 핸들 삭제

    ::closesocket(hClient);//클라이언트소켓 종료
    puts("클라이언트연결종료");
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    //1. 윈속 초기화(여기는 자동으로 초기화가 안되므로 초기화를 해줘야지)
    WSADATA    wsa = {0};
    if(::WSAStartup(MAKEWORD(2,2), &wsa) != 0)
    {
        puts("ERROR: 윈속 초기화 오류");
        return 0;
    }

    //2. 서버 소켓 생성
    SOCKET hSocket = ::socket(AF_INET,SOCK_STREAM,0);
    if(hSocket == INVALID_SOCKET)
    {
        puts("ERROR: 소켓 생성 오류");
        return 0;
    }

    //3. 서버 소켓에 포트 바인딩
    SOCKADDR_IN svraddr = {0};
    svraddr.sin_family                = AF_INET;
    svraddr.sin_port                = htons(21000);
    svraddr.sin_addr.S_un.S_addr    = htonl(INADDR_ANY); //PC에 설치된 어떤 네트워크카드로 패킷이 들어와도 다 받아서 처리하겠다는 뜻.
    if(::bind(hSocket,(SOCKADDR*)&svraddr,sizeof(svraddr)) == SOCKET_ERROR)
    {
        puts("ERROR: 포트바인딩 오류");
        return 0;
    }

    //4. 서버 소켓 listen상태로 전환
    if(::listen(hSocket,SOMAXCONN) == SOCKET_ERROR)
    {
        puts("ERROR: Listen상태 전환 오류");
        return 0;            
    }

    CreateThread(NULL,0,ConnectionThread,(LPVOID)hSocket,0,NULL); //ConnectionThread생성
    while(1)
    {
        //무한루프돌기.. main이 죽으면 안되니까...
    }

    return 0;
}