네트워크 프로그래밍 11장 코드 정리

Hyun·2024년 12월 4일
0

1. 넌블로킹 소켓

// 블로킹 소켓 생성
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에 적용할 값
);

2. Select 모델

  1. select() 함수
#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);
 }

0개의 댓글

관련 채용 정보