먼저 클라이언트와 서버 사이에 약속할 부분을 정의합시다. TCP 에코 서버는 10200 포트를 bind 하여 사용하고 listen 함수에 설정할 백 로그 큐의 크기는 5로 설정할게요. 그리고 송수신 메시지 크기를 256으로 약속합시다.
#define PORT_NUM 10200 #define BLOG_SIZE 5 #define MAX_MSG_LEN 256
진입점에서는 윈속을 초기화한 후에 대기 소켓을 설정합니다. 대기 소켓 설정이 성공하면 클라이언트 연결 요청을 수락하는 작업을 반복하는 Accept Loop을 수행합니다. 그리고 모든 작업이 끝나면 윈속을 해제화합니다.
int main() { WSADATA wsadata; WSAStartup(MAKEWORD(2,2),&wsadata);//윈속 초기화 SOCKET sock = SetTCPServer(PORT_NUM,BLOG_SIZE);//대기 소켓 설정 if(sock == -1) { perror("대기 소켓 오류"); WSACleanup(); return 0; } AcceptLoop(sock);//Accept Loop WSACleanup();//윈속 해제화 return 0; }
대기 소켓을 설정하는 함수를 작성합시다. 먼저 소켓을 생성합니다.
SOCKET SetTCPServer(short pnum,int blog) { SOCKET sock; sock = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);//소켓 생성 if(sock == -1) { return -1; }
그리고 소켓 주소를 설정하여 네트워크 인터페이스와 결합합니다.
SOCKADDR_IN servaddr={0};//소켓 주소 servaddr.sin_family = AF_INET; servaddr.sin_addr = GetDefaultMyIP(); servaddr.sin_port = htons(PORT_NUM); int re = 0; //소켓 주소와 네트워크 인터페이스 결합 re = bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr)); if(re == -1) { return -1; }
TCP 서버에서는 연결 요청 과정동안 클라이언트 정보를 기억하기 위한 백 로그 큐를 설정합니다.
re = listen(sock,blog);//백 로그 큐 설정 if(re == -1) { return -1; } return sock; }
이처럼 대기 소켓을 설정하였으면 서버에서는 클라이언트의 연결 요청을 수락하는 작업을 반복합니다.
void AcceptLoop(SOCKET sock) { SOCKET dosock; SOCKADDR_IN cliaddr={0}; int len = sizeof(cliaddr); while(true) {
accept함수를 호출하면 클라이언트의 소켓 주소를 알 수 있고 송수신에 사용할 소켓을 반환합니다.
dosock = accept(sock,(SOCKADDR *)&cliaddr,&len);//연결 수락 if(dosock == -1) { perror("Accept 실패"); break; } printf("%s:%d의 연결 요청 수락\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
연결 수락하는 accept 함수가 반환한 소켓을 이용하여 송수신합니다.
DoIt(dosock); }
모든 작업을 완료하면 소켓을 닫습니다.
closesocket(sock);//소켓 닫기 }
이제 메시지 송수신 함수를 작성합시다.
void DoIt(SOCKET dosock) { char msg[MAX_MSG_LEN]="";
에코 서버에서는 메시지를 수신하는 것을 반복합니다. 만약 상대가 소켓을 닫으면 recv 함수는 0을 반환합니다. 그리고 수신이 실패하면 -1을 반환합니다. 따라서 recv 함수가 양수를 반환하면 계속 수행합니다.
while(recv(dosock,msg,sizeof(msg),0)>0)//수신 { printf("recv:%s\n",msg);
에코 서버이므로 받은 메시지를 상대에게 전송합니다.
send(dosock,msg,sizeof(msg),0);//송신 } closesocket(dosock);//소켓 닫기 }
다음은 이번 실습에서 작성한 소스 코드입니다.
#include "common.h" #define PORT_NUM 10200 #define BLOG_SIZE 5 #define MAX_MSG_LEN 256 SOCKET SetTCPServer(short pnum,int blog);//대기 소켓 설정 void AcceptLoop(SOCKET sock);//Accept Loop void DoIt(SOCKET dosock); //송수신 int main() { WSADATA wsadata; WSAStartup(MAKEWORD(2,2),&wsadata);//윈속 초기화 SOCKET sock = SetTCPServer(PORT_NUM,BLOG_SIZE);//대기 소켓 설정 if(sock == -1) { perror("대기 소켓 오류"); WSACleanup(); return 0; } AcceptLoop(sock);//Accept Loop WSACleanup();//윈속 해제화 return 0; } SOCKET SetTCPServer(short pnum,int blog) { SOCKET sock; sock = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);//소켓 생성 if(sock == -1){ return -1; } SOCKADDR_IN servaddr={0};//소켓 주소 servaddr.sin_family = AF_INET; servaddr.sin_addr = GetDefaultMyIP(); servaddr.sin_port = htons(PORT_NUM); int re = 0; //소켓 주소와 네트워크 인터페이스 결합 re = bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr)); if(re == -1) { return -1; } re = listen(sock,blog);//백 로그 큐 설정 if(re == -1) { return -1; } return sock; } void AcceptLoop(SOCKET sock) { SOCKET dosock; SOCKADDR_IN cliaddr={0}; int len = sizeof(cliaddr); while(true) { dosock = accept(sock,(SOCKADDR *)&cliaddr,&len);//연결 수락 if(dosock == -1) { perror("Accept 실패"); break; } printf("%s:%d의 연결 요청 수락\n",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); DoIt(dosock); } closesocket(sock);//소켓 닫기 } void DoIt(SOCKET dosock) { char msg[MAX_MSG_LEN]=""; while(recv(dosock,msg,sizeof(msg),0)>0)//수신 { printf("recv:%s\n",msg); send(dosock,msg,sizeof(msg),0);//송신 } closesocket(dosock);//소켓 닫기 }