[초보]채팅 프로그램에서 클라이언트쪽 키보드 입력처리에 관한

iamsjkim의 이미지

프로그램 초보 질문입니다..
리눅스 환경이고 gcc 컴파일 입니다..

클라이언트가 유저로부터의 키보드 입력을 select문에서 받아 적절한 function수행 후에 서버에게로 보내주면 서버가 그 메세지에 적절한 작업수행후 클라이언트에게 보내고, 클라이언트는 서버로부터 오는 메세지는 받아다가 유저에게 출력해주는 채팅 서버-클라이언트 프로그램과 유사한 프로그램이 필요해서 일단 채팅프로그램 비스무리한 코드를 작성 해봤습니다.

그런데 서버쪽 실행코드를 실행시켜놓고 클라이언트도 실행시킨 후에 클라이언트쪽 터미널에서 몇글자 친 다음에 엔터를 치면 일단 메세지는 대충 서버에게 전달이 되는것 같은데 서버에서 작업을 수행한 후 해당 클라이언트에게 send를 하면 클라이언트쪽에서는 아무 반응이 없습니다. -.-
게다가 제가 몇글자(예를 들어 0000)을 치고 <Enter>를 치면 같은 메세지가 서버에게 두번 전송되기까지 하고 서버가 보내는 메세지는 받지도 않고 계속 유저의 키보드 입력만 기다립니다.

대체 어디에 문제가 있는건지 모르겠습니다.
처음에 read함수 말고 recv함수를 썼더니 키보드 입력도 전혀 안먹히기에 read함수로 바꿨더니 위와 같은 문제가 생깁니다.
제가 키보드에 입력하는 방법에 문제가 있는건지요?

답변 부탁드립니다.. T_T

일단 코드의 골격은 대충 다음과 같습니다.

Server쪽 코드 :

int main()
{
	fd_set allset; 
.......
.......

	FD_ZERO(&allset); 
	FD_SET(server_fd,&allset);
	.......
	.......

	while(1)
	{

		nready = select(max_fd+1,&allset,NULL,NULL,NULL);

		if(FD_ISSET(server_fd,&allset))
		{
			client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &clilen);

			.......


			FD_SET(client_fd,&allset);  /* add new descriptor to set */

			.......
		} 
		for(i=0;i<(max_i+1); i++) /* check all clients for data */
		{
			if((sock_fd = client_s[i]) < 0)
					continue;

			if(FD_ISSET(sock_fd,&allset))
			{
				size = recv(sock_fd, rbuf, MAXLINE, 0);
			.......
			.......
			}
		}
	} 

Client쪽 코드 :

int main()
{
	.......
	fd_set read_fds;
	.......

	FD_ZERO(&read_fds); 
	FD_SET(serverFd,&read_fds); /* server input */
	FD_SET(0,&read_fds); /* standard input */
	.......

	while(1)
	 {
		nready = select(serverFd+1,&read_fds,NULL,NULL,NULL);
			 
		if(FD_ISSET(0,&read_fds)) /* standard input : user input */
		{
			size = read(0,(char *)rbuf,8,0));
			.......
			.......

		}

		if(FD_ISSET(serverFd,&read_fds)) /* server input */
		{
			size = recv(serverFd, rbuf, MAXDATASIZE, 0);
			.......
			.......
		}
	 }



alsong의 이미지

iamsjkim wrote:

서버가 보내는 메세지는 받지도 않고 계속 유저의 키보드 입력만 기다립니다.

키보드 입력함수에서 블럭(다음을 실행하지 않고 기다리는)이 걸린것 같군요.
입력과 수신을 두개의 쓰레드로 만드세요.

그나저나 백수 언제 탈출하냐... ㅡㅡ; 배고파라.

운형의 이미지

   FD_ZERO(&read_fds);
   FD_SET(serverFd,&read_fds); /* server input */
   FD_SET(0,&read_fds); /* standard input */
   .......

   while(1)
  {
       nready = select(serverFd+1,&read_fds,NULL,NULL,NULL); 

여기서 FD_SET을 잘못 이해하셔서 그런겁니다.
FD_SET에 의해 마스킹된(디스크립터를 등록하는 거죠) 비트는
select 한번 거쳐서 읽고 나면 마스킹이 지워집니다.
그래서 select를 한번 거치고 난후 그 디스크립터를
다시 셀렉트가 관찰해야 한다면 다시 세팅을 해야하죠.
결국 FD_ZERO나 FD_SET을 while()내부로 옮겨야한다는 겁니다.

서버나 클라이언트 쪽 모두 잘못 세팅되있습니다.

도움이 되시길..

Do you think that's the air you are breathing now?

iamsjkim의 이미지

아, select가 그런줄 모르고.. ^^;;
원래 서버쪽 코드는 아래와 같았는데 여기에 올릴적에는 read_fds와 allset에 차이가 없는줄 알고 그냥 전부 allset으로 통일해서 올려버렸네요..-_-;;;;(서버쪽 코드짤적에 스티븐 아저씨의 네트워크 프로그래밍에 나온 예제를 따라서 짰었는데 fd_set타입 변수가 두개길래 이유도 모르고 그냥 두개로 만들어서 짰었는데 이제야 그 이유를 알겠습니다..감사합니다^ㅡ^)

클라이언트쪽 예제는 스티븐아저씨 책에선 찾을수가 없어서(있는데 제가 못찾은건지도..-_-; ) 제맘대루 짰더니 여지없이 문제가 생기는군요ㅠ_ㅠ(에효..)

쓰레드를 쓰지않은 문제도 생기네요..(쓰레드는 너무 어려워서 타이머 쓰레드만 쓸라고 남겨놨었는데..ㅠ.ㅠ)

다시 코딩해보고 질문 드리겠습니다..

서버쪽 코드 :

int main() 
{ 
   fd_set read_fds,allset; 
....... 
....... 

   FD_ZERO(&allset); 
   FD_SET(server_fd,&allset); 
   ....... 
   ....... 

   while(1) 
   { 
     read_fds = allset; 

      nready = select(max_fd+1,&read_fds,NULL,NULL,NULL); 

      if(FD_ISSET(server_fd,&read_fds)) 
      { 
         client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &clilen); 

         ....... 


         FD_SET(client_fd,&allset);  /* add new descriptor to set */ 

         ....... 
      } 
      for(i=0;i<(max_i+1); i++) /* check all clients for data */ 
      { 
         if((sock_fd = client_s[i]) < 0) 
               continue; 

         if(FD_ISSET(sock_fd,&read_fds)) 
         { 
            size = recv(sock_fd, rbuf, MAXLINE, 0); 
         ....... 
         ....... 
         } 
      } 
   } 

답변 달아주신 두분께 정말 감사드립니다. (_ _)~꾸벅

alsong의 이미지

참고로...
두개의 쓰레드로 하지않고
표준입력(stdin)을 select에 넣을 수 있는 걸로 알고 있습니다.
참고하시기 바랍니다.

그나저나 백수 언제 탈출하냐... ㅡㅡ; 배고파라.

iamsjkim의 이미지

클라이언트쪽 select세팅을 server처럼 바꿔줬더니 제대로 돌아갑니다.(뿌듯^^)

조언말씀 주신 두분께 정말 감사드립니다.

댓글 달기

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