2010-01-14 5 views
11

우리는 클라이언트와 서버를 작성하고 있습니다. (내가 생각한) 꽤 간단한 네트워크 통신입니다. 여러 클라이언트가 서버에 연결 한 다음 다른 모든 클라이언트에 데이터를 다시 보내야합니다.선택과 같은 소켓 (TCP)을 사용하여 읽기 및 쓰기

서버는 막히는 트래픽을 기다리는 루프 select에 앉아서 다른 클라이언트로 데이터를 보냅니다. 이것은 잘 작동하는 것 같습니다.

문제는 클라이언트입니다. 읽기에 대한 응답으로, 때때로 쓰기를 원할 것입니다. 읽을 수있는 새로운 데이터가있을 때까지

rv = select(fdmax + 1, &master_list, NULL, NULL, NULL); 

내 코드 차단 :

그러나, 나는 내가 사용하는 경우 것으로 나타났습니다. 그러나 때로는 (다른 스레드와 비동기 적으로) 네트워크 통신 스레드에 쓸 새로운 데이터가 있습니다.

if (select(....) != -1) 
{ 
    if (FD_SET(sockfd, &master_list)) 
    // handle data or disconnect 
    else 
    // look for data to write and write()/send() those. 
} 
내가 가진 모드 (또는 터무니없이 짧은 시간 제한) 폴링 선택을 설정하려고

:

// master list contains the sockfd from the getaddrinfo/socket/connect seq 
struct timeval t; 
memset(&t, 0, sizeof t); 
rv = select(fdmax + 1, &master_list, NULL, NULL, &t); 
을 그래서, 난 내 선택이 주기적으로 일어나처럼 쓸 수있는 데이터가있는 경우 나 확인할 수 있도록하려면

그러나 클라이언트는 들어오는 데이터를 전혀 얻지 못한다는 사실을 발견했습니다.

는 또한 같은 비 차단 될 소켓 FD를 설정하려 :

fcntl(sockfd, F_SETFL, O_NONBLOCK); 

하지만 문제가 해결되지 않습니다 내 고객 select()struct timeval, 독서가있는 경우

  1. 을 데이터는 작동하지만 쓸 수있는 데이터를 찾도록 차단을 해제하지는 않습니다.
  2. 내 클라이언트 select()timeval이 폴링되면 읽는 수신 데이터가 있음을 알리지 않으며 내 앱이 다른 모든 기능 호출이 성공 했음에도 불구하고 네트워크 연결이 없다고 생각하여 정지합니다.)

나는 무엇을 잘못하고있을 수 있는지에 대한 모든 지침을 제공합니까? 동일한 소켓에서 읽기 - 쓰기를 수행 할 수 없습니까 (사실이라고 믿을 수는 없습니다).

(편집 : 정답, 나는 서버가 아닌 클라이언트에서 기억 것은()를 선택하는 두 번째 FD_SET이 있고, 각 호출하기 전에 master_list을 복사하는 것입니다 :

// declare and FD_ZERO read_fds: 
// put sockfd in master_list 

while (1) 
{ 
    read_fds = master_list; 
    select(...); 

    if (FD_ISSET(read_fds)) 
    .... 
    else 
    // sleep or otherwise don't hog cpu resources 
} 

)

+0

죄송합니다. if (FD_ISSET (.... – MarcWan

답변

12

if (FD_SET(sockfd, &master_list)) 행을 제외하고 모두 괜찮아 보입니다. 나는 매우 비슷한 코드 구조를 가지고 있으며 FD_ISSET을 사용했다. 목록을 설정했는지 테스트하고 다시 설정하지 않아야합니다. 그 외에는 아무것도 볼 수 없습니다.

편집. 또한, 나는 시간 제한에 대해 다음 한 : 당신이 일을 할 것 같은 당신이 (0으로 설정하면

timeval listening_timeout; 
listening_timeout.tv_sec = timeout_in_seconds; 
listening_timeout.tv_usec = 0; 

아마도 문제가있어?)

편집 2. 선택을 종료 한 후 다시 입력하기 전에 읽은 세트를 지우지 않을 때 이상한 문제가 있음을 기억했습니다. 나는 다음과 같이해야만했습니다 :

FD_ZERO(&sockfd); 
FD_SET(sockfd, &rd); 

전에 select을 입력해야했습니다. 나는 왜 그런지 기억할 수 없다.

+0

) 타임 아웃을 '0'으로 설정하는 데 문제가 없어야합니다 :이 경우'select'는 어떤 설명자도 현재 준비가되어 즉시 반환됩니다. – JaakkoK

+1

Edit2 :'select'가 세트를 수정하므로 다시 호출하기 전에 재설정해야합니다. – JaakkoK

+0

@jk 감사합니다.이 내용을 내 소스 파일에 주석으로 추가하겠습니다. , 나중에 유용 할 것입니다 – laura

7

select 호출의 설명자에 추가 된 주 스레드와 네트워크 스레드간에 읽기/쓰기 filedescriptor를 작성하고 공유하는 트릭을 생각해보십시오. 이 fd는 보낼 스레드가있을 때 주 스레드에 의해 1 바이트 씩 쓰여집니다. 쓰기는 select 호출에서 네트워크 스레드를 깨우고 네트워크 스레드는 공유 버퍼의 데이터를 가져 와서 네트워크에 쓴 다음 select에서 다시 sleep로 돌아갑니다.

죄송합니다. 약간 막연하고 코드가 부족한 경우 ... 내 기억이 맞지 않을 수 있습니다. 다른 사람들이 귀하를 더욱 안내해야 할 수도 있습니다.

+0

에 정말 도움이됩니다. 이 아이디어는 많이 있습니다. 비록 그것이 내 문제를 해결하지 못한다해도, 나는 파이프를 사용하여 네트워크 스레드에게 종료를 알리고, 도약을해서 쓸 수있는 데이터가있는 thread 나는 그것을 좋아한다!! – MarcWan

1

코드에 아무런 이상이 보이지 않아 작동합니다. 작업을 수행 할 수 없다면 작업을 처리하는 한 가지 방법은 읽기 스레드에서 사용할 파이프를 작성하고 쓰기를 준비하는 스레드를 작성하고 파이프의 읽기 끝을 select 세트에 추가하는 것입니다. 그런 다음 다른 스레드가 쓸 데이터를 준비하면 파이프에 무엇인가를 보냅니다. 읽기 스레드는 select에서 깨어나서 쓰기가 가능합니다. 읽고 쓰는 데이터의 빈도에 따라 더 효율적일 수 있습니다.

+0

글쎄, 위로 코드는 실제로 기본적으로 정확했다 (FD_SET/FD_ISSET 것을 제외하고). 옳지 않은 것은 무엇이 빠졌는지와 위에서 지적한 @jk가 무엇인지 선택하는 것입니다. 각 호출을 선택하기 전에 fd_set을 재설정해야합니다. read_fds라는 새 파일을 만들고 "read_fds = master_list"를 선택하기 전에 각 호출을하기 전에 모든 후속 FD_ISSET 호출은 master_list가 아니라 read_fds를 조사합니다. 고마워. 다시 읽기/쓰기 소켓, wooO! – MarcWan

+0

실제로 그것을 지적한 라우라였습니다. 방금 이유를 설명했습니다. 나는 그녀의 대답을 받아 들였을 것이다. 실제로 문제를 해결 한 대답이기 때문이다. – JaakkoK

+0

좋아, 끝났어. 모두가 정직한 포스터를 좋아합니다 :) – MarcWan

0

2 스레드는 동일한 소켓에서 동시에 작동 할 수 있어야하므로 메인 스레드는 클라이언트에 쓸 수 있어야하고 다른 스레드는 들어오는 데이터를 기다리는 선택 영역에서 대기해야합니다. 이것은 물론 두 스레드가 클라이언트 목록에 액세스 할 수 있다고 가정합니다.