소켓프로그래밍 select() 함수 client 연결수락 문제

guidani의 이미지

안녕하세요
현재 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.
서버의 응답이 이루어지질 않습니다. 브라우저에서 무한대기만하고 응답을 받지를 못하고 있어요...

어떤게 문제가 되는 걸까요? 알려주시면 감사하겠습니다.

pynoos의 이미지

FD_ZERO(&write_set);
FD_ZERO(&temp_set);

이 부분은 select 에 들어가기전에 항상 이루어져야합니다.
select는 read/write 가능한 디스크립터를 받아 오는 함수이므로 항상 초기화 되어야하지요.
이 부분은 while이 시작하는 첫 부분에서 항상 이루어져야합니다. 그래야 비교되
guidani의 이미지

그런데 select가 temp_set 을 건드리질 않으므로 while 문 안에서 write_set = temp_set, read_set = temp_set 하면 따로 FD_ZERO를 써주지 않아도 괜찮다고 생각했는데 아닌가요?

guidani의 이미지

파비콘 때문이었네요.... 이런....이제 응답문제를 찾으러 떠날 시간입니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.