// 블로킹 소켓 생성
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) err_quit("socket()");
// 넌블로킹 소켓으로 전환
u_long on = 1;
retval = ioctlsocket(sock, FIONBIO, &on);
if (retval == SOCKET_ERROR) err_quit("ioctlsocket()");
int ioctlsocket(
__in SOCKETs, // 소켓
__in longcmd, // 소켓에 수행할 명령
__inout U_long *argp // 명령 cmd에 적용할 값
);
#include <winsock2.h>
int select(
int nfds,
fd_set *readfds, // 읽기, 쓰기, 예외 셋
fd_set *writefds,
fd_set *exceptfds,
const struct timeval *timeout // 타임아웃 시간 지정, 해당 시간이 지나지 않으면 리턴
)
// timeval 구조체
struct timeval {
long tv_sec; // seconds
long tv_usec; // microseconds
};
11-2. Select 모델 TCP 서버 작성
struct SOCKETINFO
{
SOCKET sock;
char buf[BUFSIZE + 1];
int recvbytes;
int sendbytes;
}
int nTotalSockets = 0;
SOCKETINFO *SocketInfoArray[FD_SETSIZE];
// 소켓 정보 관리 함수
bool AddSocketInfo(SOCKET sock);
void RemoveSocketInfo(int nIndex);
// 넌블로킹 소켓으로 전환
u_long on = 1;
retval = ioctlsocket(listen_sock, FIONBIO, &on);
if (retval == SOCKET_ERROR) err_display("ioctlsocket()");
// 데이터 통신에 사용할 변수
fd_set rset, wset;
int nready
// 소켓 셋 초기화
while(1) {
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(listen_sock, &rset);
for (int i = 0; i < nTotalSockets; i++) {
if (SocketInfoArray[i]->recvbytes > SocketInfoArray[i]-> sendbytes)
FD_SET(SocketInfoArray[i]->sock.&wset); // 받은 데이터 > 보낸 데이터: 쓰기 셋
else
FD_SET(SocketInfoArray[i]->sock.&rset); // 아니면 읽기 셋에 추가
}
// select()
nready = select(0, &rset, &wset, NULL, NULL);
if (nready == SOCKET_ERROR) err_quit("select()");
// 소켓 셋 검사 (1): 클라이언트 접속 수용
if (FD_ISSET(listen_sock, &rset)) { // 읽기 셋에 연결 대기 소켓 O -> 접속한 클라이언트
addrlen = sizeof(clientaddr);
client_sock = accept(listen_sock, // 클라이언트 소켓도 넌블로킹 소켓으로
(struct sockaddr *)&clientaddr, &addrlen);
if (client_sock == INVALID_SOCKET) {
err_display("accept()");
break;
}
else {
// 클라이언트 정보 출력
char addr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clientaddr.sin_addr, addr, sizeof(addr));
printf("\n[TCP 서버] 클라이언트 접속: IP 주소=%s, 포트 번호 =%d\n"
addr, ntohs(clientaddr.sin_port));
if (!AddSocketInfo(client_sock)) // 추후 소켓에 대해 읽고 쓰기 위해 저장
closesocket(client_sock);
}
if (--nready <= 0) // 읽기나 쓰기 작업이 필요한 소켓이 없음
continue;
// 소켓 셋 검사(2): 데이터 통신
for (int i = 0; i < nTotalSockets; i++) {
SOCKETINFO *ptr = SocketinfoArray[i];
if (FD_ISSET(ptr -> sock, &rset)) {...} // 읽기 셋에 있는 소켓 처리
else if (FD_ISSET(ptr -> sock, &wset)) {...} // 쓰기 셋
if (FD_ISSET(ptr->sock, &rset)) {
// 데이터 받기
retval = recv(ptr->sock, ptr->buf, BUFSIZE, 0);
if (retval == SOCKET_ERROR) P
err_display("recv()");
RemoveSocketInfo(i); // 에러 발생한 소켓에 대해서 제가
}
else if (retval == 0) {
RemoveSocketInfo(i); // 연결이 종료된소켓에 대해 제거
}
else {
ptr->recvbytes = retval // 받은 데이터 초기화
// 클라이언트 정보 얻기
addrlen = sizeof(clientaddr);
getpeername(ptr->sock, (struct sockaddr *)&clientaddr, &addrlen);
// 받은 데이터 출력
ptr->buf[ptr->recvbytes] = '\0'; // 받은 데이터 끝에 null 추가
char addr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clientaddr.sin_addr, addr, sizeof(addr));
printf("[TCP/%s:%d\ %s\n", addr,
ntohs(clientaddr.sin_port, ptr->buf);
}