// SocketClient.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//
#include "stdafx.h"
#include "winsock.h"
#include <process.h>
#define MAXBUF 60
#define MAXLINE 15
#define PORT 9000
#define IPADDR "127.0.0.1" // server IP
CRITICAL_SECTION hCriticalSection; // 동기화 도구 - 크리티컬 섹션 변수
int curLine = 0; // dialog 배열의 현재 위치
char dialog[MAXLINE][MAXBUF+1+12] = {0, }; // 대화내용 저장
bool isDisconnected = false; // 연결이 끊어졌는지 체크
char bufBlank[MAXBUF+1+12]; // 콘솔창에서 1라인을 지우기 위한 공백텍스트임
void show_dialog(); // 대화내용을 보여주는 변수
unsigned int WINAPI RecvThreadProc(LPVOID lpParam);
unsigned int WINAPI SendThreadProc(LPVOID lpParam);
int main()
{
SOCKET c_socket;
struct sockaddr_in client_addr;
WORD wVersionRequested;
WSADATA wsaData;
int err;
char addr[] = "000.000.000.000";
DWORD dwRecvThreadId, dwSendThreadId;
HANDLE hRecvThread, hSendThread;
InitializeCriticalSection(&hCriticalSection); // 동기화 도구 초기화 함.
memset(dialog, 0, sizeof(dialog));
memset(bufBlank, (int)' ', sizeof(bufBlank)); // space로 채운다. 화면을 지우기 위한것.
bufBlank[ sizeof(bufBlank)-1 ] = '\0';
// before use socket, must do startup! 소켓 초기화
wVersionRequested=MAKEWORD(2,2);
err = WSAStartup(wVersionRequested, &wsaData);
system("cls"); // 전체 화면 지우기
// 연결할 서버 주소를 입력 받는다.
printf("\n\n * Input IP address : ");
fflush(stdin);
scanf_s("%s", addr, sizeof(addr));
c_socket = socket(PF_INET, SOCK_STREAM,0); // 소켓 생성
memset(&client_addr, 0, sizeof(client_addr)); // 주소 구조체 셋팅
client_addr.sin_addr.s_addr = inet_addr(addr);
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(PORT);
// connect, 연결함
if(connect(c_socket, (struct sockaddr *) &client_addr, sizeof(client_addr)) == -1)
{
printf("Can not connect\n");
closesocket(c_socket);
return -1;
}
printf("\n * Connected ...\n");
Sleep(1000);
system("cls");
isDisconnected = false;
// 서버로 부터 데이터를 기다리는 쓰레드 생성
hRecvThread = (HANDLE)_beginthreadex(NULL, 0, RecvThreadProc, (void*)c_socket, CREATE_SUSPENDED, (unsigned*)&dwRecvThreadId);
// 서버로 데이터를 보내는 쓰레드 생성
hSendThread = (HANDLE)_beginthreadex(NULL, 0, SendThreadProc, (void*)c_socket, CREATE_SUSPENDED, (unsigned*)&dwSendThreadId);
ResumeThread(hRecvThread); // 쓰레드 시작
ResumeThread(hSendThread); // 쓰레드 시작
WaitForSingleObject(hRecvThread, INFINITE); // 쓰레드가 종료될때까지 대기
WaitForSingleObject(hSendThread, INFINITE); // 쓰레드가 종료될때까지 대기
// disconnect, 두 쓰레드가 모두 종료되면, 소켓을 닫음.
closesocket(c_socket);
// 동기화 도구 해제
DeleteCriticalSection(&hCriticalSection);
return 0;
}
// 콘솔에서 대화 내용등을 보여주는 일을 함
void show_dialog()
{
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 윈도우 핸들 얻기
DWORD dw;
COORD Coor;
int i=0, line = 0;
Coor.X=1;
for(i=0; i<MAXLINE; i++) // 대화내용을 콘솔의 아래쪽부터 한줄씩 위로 표시함
{
// set position. 위치 설정
Coor.Y = MAXLINE - i;
line = (curLine + MAXLINE - i) % MAXLINE;
// 먼저 기존의 대화 내용을 지우고,
WriteConsoleOutputCharacterA(hOut,bufBlank,strlen(bufBlank),Coor,&dw);
// 대화내용을 표시해줌
WriteConsoleOutputCharacterA(hOut,dialog[line],strlen(dialog[line]),Coor,&dw);
}
if(isDisconnected) // 연결이 끊어졌을때,
{
char disc[] = "* Disconnected.. Press Enter key.. ";
Coor.X = 1;
Coor.Y = MAXLINE + 1;
WriteConsoleOutputCharacterA(hOut,disc,strlen(disc),Coor,&dw);
}
}
// 수신 쓰레드
unsigned int WINAPI RecvThreadProc(LPVOID lpParam)
{
SOCKET c_socket = (SOCKET)lpParam;
int n;
char rcvBuffer[MAXBUF+1] = {0, };
// receive data from server
while(1)
{
// 먼저 연결이 끊어 졌는지 체크, isDisconnected 가 TRUE 이면 내가 접속을 끊은것이고,
// recv 리턴값이 0 이면 상대방이 접속을 끊은것이다. 0보다 작으면 다른 에러 발생으로 종료된것
if(isDisconnected || (n = recv(c_socket, rcvBuffer,sizeof(rcvBuffer), 0)) <= 0)
{
closesocket(c_socket); // 소켓을 닫고
if(isDisconnected == true)
{
return 0;
}
isDisconnected = true;
EnterCriticalSection(&hCriticalSection);
show_dialog();
LeaveCriticalSection(&hCriticalSection);
return (-1);
}
rcvBuffer[n] = '\0';
// 수신된 내용을 대화내용 저장 배열에 카피하고 표시함.
EnterCriticalSection(&hCriticalSection);
memset(dialog[curLine], 0, sizeof(dialog[curLine]));
strcpy(dialog[curLine], "Remote : ");
strcat(dialog[curLine], rcvBuffer);
show_dialog();
curLine = (curLine + 1) % MAXLINE;
LeaveCriticalSection(&hCriticalSection);
};
return 0;
}
// 발신 쓰레드
unsigned int WINAPI SendThreadProc(LPVOID lpParam)
{
SOCKET c_socket = (SOCKET)lpParam;
int n;
char sendBuffer[MAXBUF+1] = {0, };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dw;
COORD cdPos = { 0, MAXLINE + 3 };
COORD cdPos2 = { 0, MAXLINE + 4};
// send chat text
while(1)
{
memset(sendBuffer, 0, sizeof(sendBuffer));
// 기존의 사용자 입력 내용을 삭제하고,
WriteConsoleOutputCharacterA(hOut,bufBlank,strlen(bufBlank),cdPos2,&dw);
// scanf 입력 받은 후 콘솔이 아래로 내려 가지 않도록 위치를 고정시킴
SetConsoleCursorPosition(hOut, cdPos);
if(isDisconnected)
{
closesocket(c_socket);
return 0;
}
// 슬래쉬 exit는 연결 종료 명령어임
printf("/exit : disconnect\n");
printf("Send : ");
// 대화내용을 입력받음. fflush(stdin)를 입력 버퍼를 비움
fflush(stdin);
scanf_s("%[^\n]", sendBuffer, MAXBUF);
n = strlen(sendBuffer);
// 입력하는동안 접속이 끊겻다면 소켓을 닫고, 리턴
if(isDisconnected)
{
closesocket(c_socket);
return 0;
}
// 그냥 엔터를 쳣거나 하면 무시
if(n <= 0)
continue;
// 종료 명령어를 입력 했는지 체크
if(strcmp(sendBuffer, "/exit") == 0)
{
closesocket(c_socket);
isDisconnected = true;
return 0;
}
// 상대편으로데 메시지를 전송
send(c_socket, sendBuffer,n, 0);
// 메시지를 대화내용에 저장하고 표시
EnterCriticalSection(&hCriticalSection);
memset(dialog[curLine], 0, sizeof(dialog[curLine]));
strcpy(dialog[curLine], "Local : ");
strcat(dialog[curLine], sendBuffer);
show_dialog();
curLine = (curLine + 1) % MAXLINE;
LeaveCriticalSection(&hCriticalSection);
};
return 0;
}
댓글 0