2012-06-12 2 views
6

직장에서 TCP 서버를 Modbus 슬레이브 장치의 일부로 구현해야했습니다. 스택 교환과 인터넷 (일반적으로 우수 http://beej.us/guide/bgnet/ 포함)에서이 글을 많이 읽었지만 디자인 문제로 어려움을 겪고 있습니다. 요약하면, 내 장치는 단지 2 개의 연결 만 허용 할 수 있으며 각 연결에서 메인 컨트롤러 루프에서 처리해야하는 수신 Modbus 요청이되며 성공 또는 실패 상태로 회신합니다. 나는 이것을 구현하는 방법에 대해 다음과 같은 생각을 가지고있다. Linux에 멀티 스레드 TCP 서버 작성

  1. 는 들어오는 데이터 및 유휴 제한 시간 후 가까운 연결을위한 연결에서 수신 할 수있는 새로운 pthread에 급부상, 생성 결합, 수신 및 연결을 허용 리스너 스레드를 가지고. 현재 활성 스레드 수가 2 인 경우 새 연결이 즉시 닫혀 2 개만 허용됩니다.

  2. 리스너 스레드에서 새 스레드를 생성하지 않고 select()를 사용하여 들어오는 연결 요청뿐만 아니라 활성 연결에서 들어오는 Modbus 연결을 감지합니다 (Beejs 가이드의 접근 방식과 유사).

  3. accept() 호출에서 차단할 수있는 소켓 (동일한 IP 및 포트 번호)을 만드는 2 개의 리스너 스레드를 작성한 다음 소켓 fd를 닫고 연결을 처리하십시오. 여기에서는 (아마도 순진하게) 이것이 블로킹 읽기를 사용하여 처리 할 수있는 최대 2 개의 연결 만 허용한다고 가정합니다.

저는 오랫동안 C++을 사용해 왔지만 저는 리눅스 개발에 상당히 익숙합니다. 위의 접근법 중 어느 것이 가장 좋을지, 그리고 Linux에 대한 나의 미숙함이 그 중 하나가 실제로 정말 나쁜 아이디어라는 것을 의미하는 것이라면 어떤 제안도 환영 할 것입니다. 들어오는 modbus 요청이 대기열에 올라서 주기적으로 주 컨트롤러 루프를 읽으므로 fork()를 피하고 pthreads를 고수하고 싶습니다. 어떤 조언을 주셔서 미리 감사드립니다.

답변

3

세 번째 방법은 작동하지 않으며 로컬 주소에만 단 한 번만 바인딩 할 수 있습니다.

첫 번째 대안을 유용하게 사용할 수있는 많은 처리가 필요한 경우가 아니면 두 번째 대안을 사용하게 될 것입니다.

내가 생각하고있는 두 가지 첫 번째 대안의 조합은 두 개의 작업자 스레드를 만든 다음 주 차단 (항상 프로그램을 시작할 때 가지고있는 주 스레드)을 가지고 다음 블록으로 이동합니다. accept 새 연결을 기다리는 호출 . 새 연결이 도착하면 스레드 중 하나에 새 연결에 대한 작업을 시작하고 accept에 대한 차단으로 돌아가십시오. 두 번째 연결이 수락되면 다른 스레드가 해당 연결에서 작동하도록 알립니다. 두 연결이 이미 열려 있으면 하나의 연결이 닫힐 때까지 허용하지 않거나 새 연결을 기다리지 만 즉시 닫습니다.

+0

나는이 소리를 좋아합니다. 유일한 문제는 내 메인 루프가 엄격히 차단해서는 안된다는 것입니다. 처리를 수행하고 리스너 스레드의 요청을 주기적으로 처리해야합니다. 이 점을 염두에두면 옵션 2가 가장 좋을까요? – mathematician1975

+0

@ mathematician1975 내 메소드를 사용할 수는 있지만'accept'를 블로킹하는 대신'select' 타임 스탬프를 사용하거나'listen' 소켓을 비 블로킹으로 만들고'accept'를 사용하고'EAGAIN' /'EWOULDBLOCK ') 연결을 수락 할 수있는시기를 알 수 있습니다. –

+0

시간 제약이 주어진다면 이것이 단기적으로 추구 할 수있는 최선의 해결책이라고 생각합니다. 조언 해 주셔서 감사합니다. – mathematician1975

2

2 개의 연결 만 처리하기 때문에 연결 당 스레드가 이러한 종류의 응용 프로그램에 적합합니다. 비 차단 또는 비동기 I/O를 사용하는 객체 지향 접근법은 수천 개의 연결로 확장해야하는 경우 더 좋습니다. 2 리스너 쓰레드가 합리적인 경우, accept fd를 닫을 필요가 없습니다. 연결이 완료되면 다시 받아 들여야합니다. 사실, 하나의 변형은 3 개의 스레드가 받아들이는 것을 막는 것입니다. 두 개의 스레드가 연결을 적극적으로 처리하는 경우 세 번째 스레드는 새로 생성 된 연결을 다시 설정합니다 (또는 사용중인 장치에 적합한 모든 응답을 반환 함).

accept에서 세 개의 스레드를 모두 차단하려면 세 스레드가 accept/handle 처리를 시작하기 전에 주 스레드가 소켓을 만들고 바인딩해야합니다.

man page for pthreads on Linux은 accept가 스레드로부터 안전함을 나타냅니다. (thread-safe 함수 아래 섹션은 스레드로부터 안전하지 않은 함수를 나열합니다.)

+0

내 질문에 옵션 3을 의미합니까? – mathematician1975

+0

@ mathematician1975 : 그렇습니다. 옵션 3의 변형을 생각하고 있었지만 세 개의 스레드가 수락되었습니다. – jxh

+0

하지만 다른 대답은 내가 단 한 번만 바인딩 할 수 있다고 말합니다. 옵션 3은 제외됩니다. – mathematician1975

2

사용자가 제안하는 모든 디자인 옵션은 객체 지향적이지 않으며 모두 C++보다 C에 더 적합합니다. . Boost.Asio 라이브러리는 간단한 (복잡한) 소켓 서버를 만드는 데 환상적입니다. 거의 모든 예제를 가져 와서 확장 한 후 2 개의 활성 연결 만 허용하도록 확장하면 다른 모든 연결이 열리는 즉시 닫힙니다.

내 머리 꼭대기에서 연결 클래스 (생성자의 inc, 소멸자의 dec)에 정적 카운터를 유지하여 간단한 HTTP 서버를 수정할 수 있으며 새 서버가 만들어지면 check 카운트하고 연결을 닫을 지 여부를 결정합니다. 연결 클래스는 또한 boost :: asio :: deadline_timer를 사용하여 타임 아웃을 추적 할 수 있습니다.

이것은 첫 번째 디자인 선택과 가장 비슷합니다. 부스트는 1 스레드에서 수행 할 수 있으며 배경에서는 (일반적으로 epoll())과 유사합니다. 그러나 이것은 "C++ 방식"이며, 내 의견으로는 을 사용하고 pthread은 C 방식입니다.

+0

이 제안에 감사드립니다. 나는 C++과 OO 측면에 관해서 당신과 완전히 동의합니다. 그러나 현재의 제약 조건을 감안할 때 필자는 익숙해 진 시간이 걸리고 프로토 타입을 빨리 가져올 필요가 있기 때문에 원시 리눅스 API 접근 방식을 따라야한다고 생각합니다. 그러나 일단 프로젝트가 승인되면 나는이 접근 방식을 확실히 선호 할 것이라고 생각한다. – mathematician1975