select와 signal을 이용했을때 문제점.

cjy1126의 이미지

select와 signal을 이용해서 서버를 작성하였습니다.

select만 있을때는 잘되는데, signal을 넣으니까 가끔 들어온 패킷을 무시하고, 링크드 리스트로 저장한 데이터가 꼬입니다.

flags = fcntl(listen_sock, F_GETFD, 0);
fcntl(listen_sock, F_SETFD, flags | 
O_NONBLOCK);
[/code]

위와같이 하여서 Recv나 accept에서 block가 일어나지 않아야하는데, 가끔 block이 일어나는것 같기도 합니다

그런 상태에서 시그널에 의해 block이 풀리는것 같습니다.

쉽게 쓸수있는 윈도우의 SetTimer같은 함수가 없을까요?

아래는 소스입니다.(주석부분이 이상하게 제 에디트와 줄이 안맞네요. 에디트에서는 깔끔하게 정리했는데... 지저분해져서 죄송합니다.)

변수

extern SOCKETINFO *si_head, *si_tail;		/*소켓구조체 링크드 리스트 */
struct itimerval timer;		/* setitime용 시간 */

SOCKETINFO *GetNextSocketInfo(SOCKETINFO *prev);		/* 리스트의 다음부분을 구하는 함수 */

sig_alarm

void sig_alarm(int signo)		/* 알람 발생시...  */
{
	SOCKETINFO *si_temp;
	PACKET packet;

	printf("######################## sig alarm ##########################\n");

	packet.type = PACKET_ISLIVE;

	si_temp = si_head;

	/* 리스트에 있는 socket들에 heartbeat 질의 */
	while((si_temp=GetNextSocketInfo(si_temp)) != si_tail)	 
	{
		send(si_temp->socket, (char *)&packet, sizeof(packet), 0);
		printf("send: si_temp->socket = %d\n", si_temp->socket);
	}

	setitimer(ITIMER_REAL, &timer, NULL);		/* 타이머를 다시 셋팅 */
}

init: 데이터 초기화

void Init()		/* 데이터 초기화 */
{
	si_head = (SOCKETINFO *)malloc(sizeof(SOCKETINFO));
	si_tail = (SOCKETINFO *)malloc(sizeof(SOCKETINFO));

	si_head->prev = si_head;
	si_head->next = si_tail;

	si_tail->prev = si_head;
	si_tail->next = si_tail;

	timer.it_value.tv_sec = 10;
	timer.it_value.tv_usec = 0;
	timer.it_interval.tv_usec = 0;
	timer.it_interval.tv_usec = 0;
}

main

int main()
{
	int listen_sock, client_sock;
	struct sockaddr_in listen_addr, client_addr;
	int flags, result, addrlen;
	PACKET packet;
	SOCKETINFO *si_temp;
	fd_set rset, wset;
	int maxfd;

	Init();

	Ora_Connect("****", "****");		/* 오라클 연결 */

	listen_sock = socket(AF_INET, SOCK_STREAM, 0);
	if(listen_sock<0)
		ExitError("Listen Socket Create Failed");

#ifdef DEBUG
	printf("Listen Socket Create Ok!\n");
#endif

	listen_addr.sin_family = AF_INET;
	listen_addr.sin_addr.s_addr = INADDR_ANY;
	listen_addr.sin_port = htons(PORT);

	result = bind(listen_sock, (struct sockaddr *)&listen_addr, sizeof(listen_addr));
	if(result<0)
		ExitError("Bind Failed\n");

#ifdef DEBUG
	printf("Bind Ok!\n");
#endif

	result = listen(listen_sock, BACKLOG);
	if(result<0)
		ExitError("Listen Failed");

#ifdef DEBUG
	printf("Listen Ok!\n");
#endif

	flags = fcntl(listen_sock, F_GETFD, 0);		/*  listen을 비동기 소켓으로 전환 */
	fcntl(listen_sock, F_SETFD, flags | O_NONBLOCK);

	maxfd = listen_sock;
	
	signal(SIGALRM, sig_alarm);		/* 알람 시그널 등록 */
	setitimer(ITIMER_REAL, &timer, NULL); 

	while(1)
	{
		FD_ZERO(&rset);

		FD_SET(listen_sock, &rset);

		si_temp = si_head;

		/* 리스트에 데이터가 있다면 그 소켓을 FD_SET으로 등록 */
		while((si_temp=GetNextSocketInfo(si_temp)) != si_tail)		
		{
			printf("GetNextSocketInfo: si_temp->socket = %d\n", si_temp->socket);
			FD_SET(si_temp->socket, &rset);
		}

		printf("befor select\n");

		result = select(maxfd+1, &rset, 0, 0, 0);		/* 셀렉트... 대기 */
		if(result<0)
		{
			if(errno==EINTR)
			{
				printf("select: erron==EINTR\n");
				continue;
			}
			ExitError("Select Failed");
		}

		printf("after select\n");

		if(FD_ISSET(listen_sock, &rset))		/* listen 소켓에 접속요청이라면 */
		{
			client_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &addrlen);
			if(client_sock<0)
			{
				ViewError("Accept Failed");
				continue;
			}

			if(client_sock>maxfd)
				maxfd = client_sock;

			AddSocketInfo(client_sock);		/* 소켓정보 리스트에 등록 */

			flags = fcntl(client_sock, F_GETFD, 0);		/* 접속받은 소켓을 비동기로 전환 */
			fcntl(client_sock, F_SETFD, flags | O_NONBLOCK);

#ifdef DEBUG
	printf("Accept Ok!\n");
#endif
		}

		si_temp = si_head;

		/* 리스트에 있는 소켓 FD_ISSET으로 검사 */
		while((si_temp=GetNextSocketInfo(si_temp)) != si_tail)		
		{
			if(FD_ISSET(si_temp->socket, &rset))
			{
				memset(&packet, 0, sizeof(packet));

				printf("before Recv\n");
				result = Recv(client_sock, (char *)&packet, sizeof(packet), 0);	/* Recv... */
				if(result<0)
				{
					if(errno==EINTR)		/* 시그널이라면 계속 */
						continue;

					else
					{
						SOCKETINFO *remove;		/* 에러면 소켓리스트 정리 */

						if(client_sock==maxfd)
							--maxfd;

						remove = si_temp;
						si_temp = GetNextSocketInfo(si_temp);
						
						RemoveSocketInfo(remove);
						continue;
					}
				}

				printf("after Recv\n");

				if(packet.type==PACKET_START)		/* 출근 */
				{
					printf("PACKET_START: si_temp->socket = %d\n", si_temp->socket);
					OnStart(packet.sabun);		/* 오라클에 출근시간 저장 */
				}
				
				else if(packet.type==PACKET_END)		/* 퇴근 */
				{
					SOCKETINFO *remove;

					printf("PACKET_END: si_temp->socket\n", si_temp->socket);
					OnEnd(packet.sabun);		/* 오라클에 퇴근시간 저장 */

					remove = si_temp;
					si_temp = GetNextSocketInfo(si_temp);
					
					RemoveSocketInfo(remove);		/* 소켓리스트에서 제거 */

					continue;
				}
				
				else if(packet.type==PACKET_CHULJANG)		/* 오라클에 출장 저장 */
					OnChulJang(packet.sabun); 
				
				/* sig_alarm에서 보낸 PACKET_ISLIVE에 대한 응답 */
				else if(packet.type==PACKET_LIVE)								
				{
					printf("PACKET_LIVE: si_temp->socket = %d\n", si_temp->socket);
				}
				else
					printf("잘못된값: si_temp->socket = %d\n", si_temp->socket);
			}
		}
	}

	return 0;
}
최종호의 이미지

cjy1126 wrote:

...
flags = fcntl(listen_sock, F_GETFD, 0);
fcntl(listen_sock, F_SETFD, flags | O_NONBLOCK);

...

F_GETFL 과 F_SETFL 을 F_GETFD 와 F_SETFD 로 잘못 쓰신 듯 보입니다.
일단 고쳐보시고 다시 테스트해 보세요...

익명 사용자의 이미지

리스트에 있는 데이터를 읽는 부분에서

result = Recv(client_sock, (char *)&packet, sizeof(packet), 0);

울고싶네요.

client_sock 은 1개 짜리로 db랑 테스트 하던건데... 이걸 si_temp->socket로 안바꿔서 그랬네요. ㅠ.ㅠ

괜히 엄한 sig_alarm이나 탓하고... 제가 제대로 모르는건가하고... 모든걸 의심했네요.

그래도 덕분에 코드가 좀 더 튼튼해졌지만요.(모때문에 에러인지 몰라서 낌새 보이는건 모두 수정 ㅡㅡ;)

F_GETFD와 F_SETFD도 F_GETFL 와 F_SETFL로 고쳤습니다.

답변 감사드립니다. ^^

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.