소켓프로그래밍 select() 함수 client 연결수락 문제
안녕하세요
현재 c++ select()를 이용한 http 서버를 만드는 과제를 하고있습니다.
소켓프로그래밍이 완전 처음이라 처음부터 공부하고 있는데요,
몇가지 막히는게 있어서 질문드립니다.
질문드리기에 앞서 제가 코드를 잘 짠건지 한번 봐주세요 ㅠㅠ
맨 처음 서버소켓을 만드는 과정에서 fcntl로 논블러킹으로 만들었습니다.
이유는 과제에서 모든 fd를 non-blocking fd 를 사용하라고 해서요.
int server_socket; int new_socket; // 테스트용 응답코드 const char *hello = "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 12\n\nHello world!"; // non blocking fd fcntl(server_socket, F_SETFL, O_NONBLOCK);
그 후에 bind(), listen()를 해주고, select를 사용하기전
다음과 같이 read_set, write_set, temp_set 의 fd_set 변수를 만들고 초기화도 시켜주었습니다.
여기서 set 컨테이너로 curfds, tmpfds 도 만들어서 서버소켓과 클라이언트가 접속하면 해당 소켓을 저장할
변수를 만들었습니다.
fd_set read_set, write_set; fd_set temp_set; FD_ZERO(&read_set); FD_ZERO(&write_set); FD_ZERO(&temp_set); // 서버소켓을 temp_set에 저장 FD_SET(server_socket, &temp_set); int fd_max = server_socket; // 서버, 클라이언트 fd가 저장될 리스트 // select 에서 사용할 fd_max를 쉽게 알아내기 위해 자동 정렬이 되는 set을 사용했습니다. std::set<int> curfds; std::set<int> tmpfds; tmpfds.insert(server_socket);
이제 본격적으로 서버가 돌아가는 코드입니다.
while(1) { curfds = tmpfds; fd_max = *curfds.rbegin(); // 가장 큰 숫자의 fd를 가져옴 read_set = temp_set; write_set = temp_set; // timeout을 0 으로 줬는데 괜찮은지 모르겠습니다... struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; select(fd_max + 1, &read_set, &write_set, NULL, &timeout) < 0) // readset 중에 활성화된것을 확인 // 그동안 가지고 있던 소켓들을 한바퀴 돌면서 for (std::set<int>::iterator it = curfds.begin(); it != curfds.end(); ++it) { // 어떤 소켓이 변했는지 체크 if (FD_ISSET(*it, &read_set) > 0) { // 만약 해당 소켓이 서버소켓이면 if (*it == server_socket) { // 클라이언트 연결수락 new_socket = accept(*it, (sockaddr *)&new_address, &new_len); // 클라이언트소켓도 논블락으로 만들어줌 opts = fcntl(new_socket, F_SETFL, O_NONBLOCK); // temp_set에 넣고, tmpfds에도 넣어줌 FD_SET(new_socket, &temp_set); tmpfds.insert(new_socket); printf("client connected\n"); } else { // 만약 읽을 소캣이 클라이언트라면 printf("ready to read from client\n"); char buff[1001]; int res = read(*it, buff, 1000); buff[res] = 0; // 더이상 읽을게 없을때 if (res == 0) { // 응답 보내기 if (FD_ISSET(*it, &write_set) > 0) { write(*it, hello, strlen(hello)); } // 연결 해제 FD_CLR(*it, &temp_set); close(*it); tmpfds.erase(*it); } } } } // readset 확인 반복문 sleep(1); // 반복문이 몇바퀴도는지 확인하기 위한 코드 printf("finished one rotation\n"); // 반복문이 몇바퀴도는지 확인하기 위한 코드 } // while
코드에서 에러체크하는 부분은 읽기 쉬우시라고 다 뺐습니다.
모든 소켓은 non-block이고 웹브라우저로 접속 테스트를 한 결과 다음과 같이 나왔어요.
finished one rotation
finished one rotation
finished one rotation
finished one rotation
client connected // 클라이언트 접속
finished one rotation
client connected // 클라이언트를 temp_set에 추가했는데도 select는 한번더 클라이언트를 받음
ready to read from client
finished one rotation
문제점1.
클라이언트가 접속시도할 때 select 를 이용해 서버소켓의 읽기변화를 감지하고 accept로 클라이언트를
추가해줬는데도 불구하고 다음반복문에서 또 변화가 있다고 감지해서 클라이언트를 받습니다.
문제점2.
서버의 응답이 이루어지질 않습니다. 브라우저에서 무한대기만하고 응답을 받지를 못하고 있어요...
어떤게 문제가 되는 걸까요? 알려주시면 감사하겠습니다.
FD_ZERO(&write_set);
이 부분은 select 에 들어가기전에 항상 이루어져야합니다.
select는 read/write 가능한 디스크립터를 받아 오는 함수이므로 항상 초기화 되어야하지요.
이 부분은 while이 시작하는 첫 부분에서 항상 이루어져야합니다. 그래야 비교되
---
http://coolengineer.com
답변 감사드립니다.
그런데 select가 temp_set 을 건드리질 않으므로 while 문 안에서 write_set = temp_set, read_set = temp_set 하면 따로 FD_ZERO를 써주지 않아도 괜찮다고 생각했는데 아닌가요?
웹브라우저에서 연결을 두번 시도하는 이유가
파비콘 때문이었네요.... 이런....이제 응답문제를 찾으러 떠날 시간입니다.
댓글 달기