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;
}