이전 글에서 서버와 클라이언트, 두 프로그램을 만들어 연결하는 작업을 해보았다. 이제 소켓이 자주 쓰이는 채팅 프로그램에 대한 윤곽을 잡아보려 한다. 서버가 메시지를 받고 클라이언트에게 다시 전달하는 동작을 구현할 것이다.
서버 코드
#include <iostream>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
std::cout << "WSAStartup failed" << std::endl;
return 1;
}
SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket == INVALID_SOCKET)
{
std::cout << "socket failed: " << WSAGetLastError() << std::endl;
WSACleanup();
return 1;
}
SOCKADDR_IN serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(9000);
if (bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
std::cout << "bind failed: " << WSAGetLastError() << std::endl;
closesocket(listenSocket);
WSACleanup();
return 1;
}
if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR)
{
std::cout << "listen failed: " << WSAGetLastError() << std::endl;
closesocket(listenSocket);
WSACleanup();
return 1;
}
std::cout << "Server is listening on port 9000..." << std::endl;
SOCKADDR_IN clientAddr;
int clientAddrSize = sizeof(clientAddr);
SOCKET clientSocket = accept(listenSocket, (SOCKADDR*)&clientAddr, &clientAddrSize);
if (clientSocket == INVALID_SOCKET)
{
std::cout << "accept failed: " << WSAGetLastError() << std::endl;
closesocket(listenSocket);
WSACleanup();
return 1;
}
std::cout << "Client connected!" << std::endl;
// ===== 여기부터 새로 추가된 부분 =====
char buffer[512];
while (true)
{
// 1. 클라이언트가 보낸 메시지 받기 (듣기)
int bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (bytesReceived <= 0)
{
std::cout << "Client disconnected." << std::endl;
break;
}
buffer[bytesReceived] = '\0'; // 문자열 끝 표시
std::cout << "Received: " << buffer << std::endl;
// 2. 받은 걸 그대로 돌려보내기 (말하기)
send(clientSocket, buffer, bytesReceived, 0);
}
// ===== 여기까지 =====
closesocket(clientSocket);
closesocket(listenSocket);
WSACleanup();
return 0;
}
클라이언트와의 연결을 완료한 다음 받으면 그대로 되돌려주는 루프가 새로 들어갔다.
send()/recv() 함수는 인자로 (어떤 소켓으로, 데이터가 담긴 버퍼, 크기, 옵션)을 받는데 마지막 0은 '특별한 옵션 없음'이라 지금은 항상 0으로 두면 된다. recv()의 반환 값은 받은 바이트 수를 돌려주는데, 이게 0 이하이면 상대가 연결을 끊었다는 신호이다. 그래서 bytesReceived <= 0의 경우 break로 루프문을 빠져나가는 것이다.
buffer[bytesReceived] = '\0';는 데이터 끝에 문자열 종료 표시를 붙이는 것으로, 네트워크로 온 데이터엔 문자열의 끝을 알리는 '\0'이 없어서 직접 붙여준다.
클라이언트 코드
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <cstring>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
std::cout << "WSAStartup failed" << std::endl;
return 1;
}
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (clientSocket == INVALID_SOCKET)
{
std::cout << "socket failed: " << WSAGetLastError() << std::endl;
WSACleanup();
return 1;
}
SOCKADDR_IN serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
serverAddr.sin_port = htons(9000);
if (connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
std::cout << "connect failed: " << WSAGetLastError() << std::endl;
closesocket(clientSocket);
WSACleanup();
return 1;
}
std::cout << "Client: connected to server!" << std::endl;
// ===== 여기부터 새로 추가된 부분 =====
char sendBuffer[512];
char recvBuffer[512];
while (true)
{
// 1. 키보드로 메시지 입력받기
std::cout << "메시지 입력 (quit 입력 시 종료): ";
std::cin.getline(sendBuffer, sizeof(sendBuffer));
// 'quit'을 입력하면 반복 종료
if (strcmp(sendBuffer, "quit") == 0)
break;
// 2. 서버로 보내기 (말하기)
send(clientSocket, sendBuffer, (int)strlen(sendBuffer), 0);
// 3. 서버가 돌려준 메아리 받기 (듣기)
int bytesReceived = recv(clientSocket, recvBuffer, sizeof(recvBuffer) - 1, 0);
if (bytesReceived <= 0)
{
std::cout << "Server disconnected." << std::endl;
break;
}
recvBuffer[bytesReceived] = '\0';
std::cout << "서버 응답: " << recvBuffer << std::endl;
}
// ===== 여기까지 =====
closesocket(clientSocket);
WSACleanup();
return 0;
}
클라이언트의 경우 서버와의 연결 이후 ' 키보드로 입력 → 서버에 보내기 → 돌아온 메아리 받아서 출력' 순서를 반복하는 루프를 추가했다. 클라이언트도 서버와 마찬가지로 send()/recv() 함수를 호출하고, bytesReceived <= 0으로 반복을 종료한다.
여기까지 완료 했다면 서버 하나와 클라이언트 하나 사이에서의 한 줄 짜리 채팅이 완성된 것이다!

'프로그래밍 언어 > C++' 카테고리의 다른 글
| [소켓 통신] 다중 접속과 브로드캐스팅 (0) | 2026.07.14 |
|---|---|
| [소켓 통신] 멀티스레드 구현 (0) | 2026.07.14 |
| [소켓 통신] 서버에서 클라이언트 접속 받기 (0) | 2026.07.10 |
| [소켓 통신] Winsock 라이브러리 연결 (0) | 2026.07.09 |
| 불안정 정렬과 안정 정렬 (Stable Sort) (1) | 2024.08.30 |
