소켓의 본질에 대한 이해
- 소켓은 OS커널에 구현 되어 있는 프로토콜 요소에 대한 추상화된 인터페이스
- 장치 파일의 일종으로 이해할 수 있음
- 일반 파일에 대한 개념이 대부분 적용됨
TCP 상태 다이어그램과 적절한 상태 전이

- 파란색이 server이고 연결을 대기하고 있다가 끊길때는 client가 끊자고 해서 server는 passive close가 이뤄진다.
- server가 먼저 연결을 끊어버리는것은 적절한 전이 가 아니다.
TCP 에코 서비스 전체 흐름
- socket() -> bind() 가 끝나야 CLOSED -> LISTEN으로 변경된다.
- listen()을 통해 대기하다가 client 가 connect를 시도하면 server 가 accept하고 연결이 된다.
- socket은 server socket , accept에서 반환하는 socket이 존재한다.
TCP 에코 서버 제작: 서버 접속대기
#include "stdafx.h"
#include <winsock2.h>
#pragma comment(lib, "ws2_32")
int _tmain(int argc, _TCHAR* argv[])
{
//※윈속 초기화
WSADATA wsa = { 0 };
if (::WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
puts("ERROR: 윈속을 초기화 할 수 없습니다.");
return 0;
}
//1. 접속대기 소켓 생성
SOCKET hSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (hSocket == INVALID_SOCKET)
{
puts("ERROR: 접속 대기 소켓을 생성할 수 없습니다.");
return 0;
}
//2. 포트 바인딩
SOCKADDR_IN svraddr = { 0 };
svraddr.sin_family = AF_INET;
svraddr.sin_port = htons(25000);
svraddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
if (::bind(hSocket, (SOCKADDR*)&svraddr, sizeof(svraddr)) == SOCKET_ERROR)
{
puts("ERROR: 소켓에 IP주소와 포트를 바인드 할 수 없습니다.");
return 0;
}
//3. 접속대기 상태로 전환
if (::listen(hSocket, SOMAXCONN) == SOCKET_ERROR)
{
puts("ERROR: 리슨 상태로 전환할 수 없습니다.");
return 0;
}
//4. 클라이언트 접속 처리 및 대응
SOCKADDR_IN clientaddr = { 0 };
int nAddrLen = sizeof(clientaddr);
SOCKET hClient = 0;
char szBuffer[128] = { 0 };
int nReceive = 0;
//4.1 클라이언트 연결을 받아들이고 새로운 소켓 생성(개방)
while ((hClient = ::accept(hSocket,
(SOCKADDR*)&clientaddr,
&nAddrLen)) != INVALID_SOCKET)
{
puts("새 클라이언트가 연결되었습니다."); fflush(stdout);
//4.2 클라이언트로부터 문자열을 수신함.
while ((nReceive = ::recv(hClient, szBuffer, sizeof(szBuffer), 0)) > 0)
{
//4.3 수신한 문자열을 그대로 반향전송.
::send(hClient, szBuffer, sizeof(szBuffer), 0);
puts(szBuffer); fflush(stdout);
memset(szBuffer, 0, sizeof(szBuffer));
}
//4.3 클라이언트가 연결을 종료함.
::shutdown(hSocket, SD_BOTH);
::closesocket(hClient);
puts("클라이언트 연결이 끊겼습니다."); fflush(stdout);
}
//5. 리슨 소켓 닫기
::closesocket(hSocket);
//※윈속 해제
::WSACleanup();
return 0;
}
listen 의 2번째 인자에 원래 backlog , 즉 accpet처리중에 다른 client들이 연결을 시도 할 때 몇명까지 대기하게 할것이냐를 정하는 숫자를 썼었는데
요즘은 다 OS가 관리해준다.

tcp layer에서 segment의 조립이 일어나고 kernel buffer에서 application buffer로 copy가 일어나는 방식으로 recv()가 일어난다.
server에서 recv 에서 0을 반환했다는것은 client가 연결을 종료 했다는것을 의미한다.
tcp client
#include "stdafx.h"
#include <winsock2.h>
#pragma comment(lib, "ws2_32")
int _tmain(int argc, _TCHAR* argv[])
{
//※윈속 초기화
WSADATA wsa = { 0 };
if (::WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
puts("ERROR: 윈속을 초기화 할 수 없습니다.");
return 0;
}
//1. 접속대기 소켓 생성
SOCKET hSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (hSocket == INVALID_SOCKET)
{
puts("ERROR: 소켓을 생성할 수 없습니다.");
return 0;
}
//2. 포트 바인딩 및 연결
SOCKADDR_IN svraddr = { 0 };
svraddr.sin_family = AF_INET;
svraddr.sin_port = htons(25000);
svraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (::connect(hSocket,
(SOCKADDR*)&svraddr, sizeof(svraddr)) == SOCKET_ERROR)
{
puts("ERROR: 서버에 연결할 수 없습니다.");
return 0;
}
//3. 채팅 메시지 송/수신
char szBuffer[128] = { 0 };
while (1)
{
//사용자로부터 문자열을 입력 받는다.
gets_s(szBuffer);
if (strcmp(szBuffer, "EXIT") == 0) break;
//사용자가 입력한 문자열을 서버에 전송한다.
::send(hSocket, szBuffer, strlen(szBuffer) + 1, 0);
//서버로부터 방금 보낸 문자열에 대한 에코 메시지를 수신한다.
memset(szBuffer, 0, sizeof(szBuffer));
::recv(hSocket, szBuffer, sizeof(szBuffer), 0);
printf("From server: %s\n", szBuffer);
}
//4. 소켓을 닫고 종료.
//::shutdown(hSocket, SD_BOTH);
::closesocket(hSocket);
//※윈속 해제
::WSACleanup();
return 0;
}
'Network > Windows 소켓 프로그래밍 입문에서 고성능 서버까지!' 카테고리의 다른 글
파일 송/수신과 프로토콜 설계 (0) | 2023.11.27 |
---|---|
파일 송/수신 (0) | 2023.11.23 |
TCP 채팅 서버 - 성능 개선 (0) | 2023.11.22 |
TCP 채팅 서버 - 기본 이론 (0) | 2023.11.20 |
소켓 옵션과 필수 이론 (0) | 2023.11.19 |