[리눅스] TCP / IP통신에서 write() / read()에 관한 질문입니다.

nbsaver의 이미지

서버: 우분투(c언어로 작성), 클라이언트: 안드로이드

책에 보니까 write()가 반환되는 시점은 상대 호스트로 데이터의 전송이 완료되는 시점이 나닌, 전송할 데이터가 출력버퍼로 이동이 완료되는 시점이라고 나와있는데요...

이것때문에 제가 구현한 서버측 소스내에서 write()가 제대로 동작(?) 하지 않아 클라이언트에서 read()하면 타임아웃이 발생하는 것 같습니다.

일단 소스를 보시면,

//이부분은 스레드를 생성하고 그 안에서 실행되는 부분입니다.
while(1) {
   str_len=read(clnt_sock, protocol, sizeof(protocol));
   if(str_len > 0) { //데이터를 읽음
      printf("str_len : %d\n", str_len);
   }
   else {
      printf("str_len : %d\n", str_len); //데이터가 없으므로 루프 반복
      continue;
   }
 
   if(packet_valid_check(protocol, str_len) == 1) {
      printf("패킷 정상 수신.\n"); 
   }
   else {
      printf("정상적인 패킷이 아님.\n");
   }
 
   //패킷 분석(배열 인덱스3에 위치한 값이 프로토콜) ex) @.a.중간내용.#
   switch(protocol[2]) {
 
   // a라면...
   case 'a':
      result[0]='1';
      write(clnt_sock, result, strlen(result)); 
      break;
   }
}

a라는 프로토콜을 전송하면 서버측에서는 읽고 패킷분석까지 하고 switch()내의 case문 까지 잘 진입합니다.

write함수 반환값을 받아보면 바이트 수까지 정확하게 찍습니다.

그리고 루프를 돌아 read()에서 블록 상태까지 대기까지 하는 것 같습니다.

근데 클라이언트쪽에서는 read()하면 타임아웃이 발생합니다....

제가 해본 방법으론는 서버측 write() 실행 후 버퍼에만 데이터가 남아있고 전송이 안된 것 같아 fflush(stdout); 넣어봤는데 안됐고요..

close(clnt_sock); 하니까 전송이 되네요... (이 방법은 한번 연결하고 끊어 버리기 때문에 다른 방법이 필요 합니다.)

한 일주일 이것때문에 여기저기 다 찾아보고 했는데 해답을 잘 못찾겠네요.

리눅스 DDD 디버거를 써볼려고 해도 사용법이 잘 안나와있어서 ...

조언 부탁드립니다.

랜덤여신의 이미지

fflush는 소켓 기술자인 clnt_sock에는 적용할 수 없습니다. 그렇다고 말씀하신 것처럼 fflush(stdout); 를 하는 것은 의미가 없습니다. stdout은 소켓과 아무 상관 없는, 화면 출력 스트림이니까요. 또한 fflush는 사용자 수준의 버퍼 처리 함수입니다. 사용자 버퍼의 내용을 커널로 보내는 역할을 합니다. 여기서 사용자 버퍼란 fwrite 등의 작업을 하면 채워집니다. 반면에 write는 커널에 직접 데이터를 보내는 시스템 호출(system call)이므로, 커널은 이미 데이터를 받은 것입니다.

소켓 전송이 지연되는 것은 네이글 알고리즘에 따라 커널이 충분한 데이터가 모일 때까지 패킷 전송을 미루고 있기 때문으로, 커널에 데이터를 확실히 전달하는(fflush) 것만으로는 해결이 안 됩니다. 커널에서 보낼지 말지 결정하는 것은 그 이후 문제니까요. 따라서 두 가지 해결책을 생각해 볼 수 있습니다.

1. fsync나 fsyncdata를 사용합니다. 커널 버퍼를 즉시 비우는 시스템 호출입니다. 디스크에 파일을 쓰는 데는 이 시스템 호출이 유용하게 쓰입니다. 그런데, http://kldp.org/node/1284 에 따르면 소켓 기술자에는 이것들을 못 쓴다고 합니다.
2. 네이글 알고리즘을 끕니다. 소켓 옵션으로 TCP_NODELAY를 주면 꺼집니다. 이때 처음부터 끌 수도 있고, 중요한 데이터를 보낼 때만 일시적으로 끌 수도 있습니다. http://stackoverflow.com/questions/855544/is-there-a-way-to-flush-a-posix-socket 에 따르면, 다음과 같이 쓸 수 있군요.

send(sock, "notimportant", ...);
send(sock, "notimportant", ...);
send(sock, "notimportant", ...);
int flag = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
send(sock, "important data or end of the current message", ...);
flag = 0;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
모지리의 이미지

그냥 참고만 해보세요.

1. 넌블락 소켓으로 작성
2. 리턴값+에러코드값 모두 확인
3. READ(), WRITE() 모두 리턴 바이트 확인.
4. 특히 WRITE()은 전송해야될 데이터가 모두 전송해야할때 까지 전송

즉, 4번의 경우 5바이트를 전송해야 되면 5바이트가 전송되었을때까지 루프를 돌면서
확인 하는 구조로 바꾸시면 될거 같습니다. 클라이언트측에서 타임아웃이 나왔다면
아마 읽기 루틴이 조금 잘못되었을수도 있을겁니다.

TCP의 특성

1. 반드시 정확히 전송된다.
2. 언제 전송될지 모른다+MTU등 각종 상황에 따라 패킷 프로그먼트가 생긴다.

이정도만 참고 하시면 될것 같습니다.

nbsaver의 이미지

문제점이...

제가 원하는 부분에서 read() 하기 전에

c언어에서 전송한 구조체배열(크기 10)을 for()으로 10회 read()하였는데

거기서 읽히지 않고 남아 있던 데이터가 있어서

위에 소스에서 read할때 남아있는 구조체 크기만큼 read()를 해서 에러가 나는 것이 었습니다.

그래서 for문으로 구조체를 읽지 않고 while()문으로 length가 0이 아닐때까지 read() 하니까

그부분에선 timeout이 나더라도

제가 원하는 부분에선 원하는 데이터를 읽더라구요.

답변 감사합니다.

댓글 달기

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