문제

그것은 기본적으로 하나 앱에 설치되어 있는 여러 PC,각각의 설치를 유지하는 그것의 자신의 데이터베이스는 동기와 기타의로 있을 때까지(동일한 네트워크에 연결)동일한 시간에.

나는 테스트를 사용하여 이 간단 소켓 연결하고 사용자 지정 버퍼만을 만들고 싶어 통신 응용 프로그램 사이 받아들인 기준을 준수하며 또한 안전/견고하고 시도하지 않을 재조명하다.

는 무엇입 normal/표준 방식으로 이렇게 하는 응용 프로그램-투-응용 프로그램 comms&알아보려면 어떻게 해야 합 더 많은?

또한,무엇을 기술을 사용할 수 있습니다/을 발표하고 찾을 수 있는 다른 응용 프로그램에서 네트워크?


편집:(정제 내 문제)

The pub/하위 모델에 의해 지정된 지멜 아래 될 것으로 보인의 라인을 따라 내가 필요합니다.그러나 그것은 땅을 많이 다루지 정말로 알리&에서 사용하는 모든입니다.

그것은 또한처럼 나를 설정해야 합 P2P 연결이 한 번 두 개 이상의 응용 프로그램을 발견이 서로 어떻게 할까요?

이 있는 경우는 예제/tutorials 사용하십시오점습니다.작은 오픈 소스 프로젝트 모듈을 구현하는 뭔가 필요한 것 또한 제공합니다.

나의 플랫폼을 선택은 Linux,Windows 을 가지고 사례를 살펴보는 것도 매우 사용할 수 있습니다.


편집[09-01-06]:

나는 지금은 다음 옵션:

  1. 멀티 캐스팅 (TLDP-Howto)-이 보인다 실행할 수 있는지만,나는 공부를 할 필요 그것은 몇 가지 더 있습니다.
  2. 무료로 사용하는 동적인 DNS 서버,이 비트는 것은 위험한...
  3. 를 사용하여 일부 시설 무료 이메일,예를 들어,gmail/yahoo/...및 보내기/에서 메일을 읽을 찾아 거기에 다른 응용 프로그램의 IP(작동할 수 있지만,더러운 느낌)
  4. webservices 제안되었다,하지만 난 그들이 어떻게 작업&공부를 해야 그것을

을 알려 주시면 감사하겠 의견에는 이러한 옵션이있는 경우 그는 거기에있다.나는 불행하게도 없는 옵션을 중앙 서버를 사용하거나 웹 사이트(지 않으면 그것을 보장 할 수 없습니다 무료로 영원한).

[편집 2009-02-19]

(수을 받아 두/세 있습니다.하나 나는 받을 제공하기 때문에 라인의 생각과 가능성을하는 동안,다른 사람들과 함께 수정하지만,해당되는 솔루션입니다.덕분에 모두 응답하는,그것의 모든 도움이 됩니다.)

적으로 찾을 구현할/나의 솔루션,나를 업데이트합니다 질문해야 합 솔루션이 충분할 만들겠 sourceforge 프로젝트를 위한니다.(이것은 어떠한 경우에 작은 문제에 훨씬 더 큰 프로젝트입니다.)

도움이 되었습니까?

해결책

흠,

이것은 수학 문제와 비슷합니다. 두 컴퓨터의 방법에 대한 질문 연결을 설정하십시오 그들이 서로를 찾으면 상당히 간단합니다. P2P 또는 클라이언트 서버 프로토콜을 사용할 수 있습니다. SSL 거의 보편적으로 제공되지만 봉사 할 수도 있습니다 SSH, 운영 Freenet 또는 무엇이든. 이러한 프로토콜 중 하나를 통해 연결을 설정하면 게시/구독 모델 데이터 교환 잘 작동 할 수 있습니다. 하지만 거기에는

컴퓨터가 서로를 어떻게 찾는 지에 대한 질문은 상황이 까다로워지는 곳입니다. 본질적으로 세 가지 경우가 있습니다.

  1. 모든 컴퓨터는 자신의 지역 네트워크에 있습니다. 이 경우 온라인으로 가하는 모든 컴퓨터는 온라인 상태라는 메시지를 방송하고 다른 기계가 온라인에 관한 메시지를 되 찾을 수 있습니다. 방송/멀티 캐스트 메시지는 네트워크의 모든 컴퓨터에 대한 대상이 무엇인지 알지 못하므로 네트워크의 모든 컴퓨터에 있습니다. 인터넷에있는 모든 컴퓨터에 메시지를 보낼 수 없으므로 인터넷 에서이 작업을 수행 할 수 없습니다.

  2. 컴퓨터는 인터넷에서 임의의 노드에 있습니다 ** 그리고 ** 웹에 연결된 컴퓨터 중 하나 이상이 항상있을 것입니다 ** 그리고 ** 모든 기계는 정기적으로 온라인으로 이동합니다.이 경우 각 컴퓨터는 IP 주소의 실행 목록을 유지할 수 있습니다. 오프라인 상태가 된 컴퓨터가 온라인으로 돌아 오면 알려진 주소를 확인하여 첫 번째 유효한 주소에 연결합니다. Emule 프로토콜이 서버를 찾는 방식입니다.

  3. 컴퓨터는 인터넷에서 임의의 노드에 있습니다 ** 그리고 ** 모든 기계가 함께 오프라인으로 ** 또는 ** Otheers는 IP 주소를 전환하는 동안 오프라인으로 오프라인으로 이동합니다. 여기서 나는 당신이 새로운 기계가 서로를 찾을 방법이 없다는 것을 증명할 수 없다고 생각합니다. 없이 공통 IP에 대한 링크에 대한 일부 중앙 서버. 인터넷 전반에 걸쳐 큰 메시지가 크기 때문에 방송 할 방법이 없습니다. 이러한 상황에서 컴퓨터에는 온라인으로 오는 다른 기계에 대한 식별 정보가 없으므로 중앙의 공통 리소스를 사용해야합니다. 언급 한 이메일 주소, 웹 사이트, FTP 서버, IRC 채널. 동적 DNS는 중앙 집중식 정보 저장소의 한 가지 인스턴스 일뿐입니다. 이러한 매장을 사용해야하는 경우 자연스럽게 신뢰성, 속도 및 영구성을 위해 사용 가능한 모든 상점을 평가해야합니다. 이와 관련하여 응용 프로그램이 필요한 것에 대한 자세한 정보를 제공하지 않는 한, 다른 사람이 필요한 영구 저장소를 결정할 수는 없다고 생각합니다.

나는 이것이 모든 가능성을 다루고 있다고 생각합니다. 당신이해야 할 일은 응용 프로그램이 어떤 일반적인 경우에 있는지 결정한 다음이 경우에 맞는 프로토콜 중 하나를 결정하는 것입니다.

편집하다:

피드백에서 사례 3이 적용되는 것 같습니다. 위에서 볼 수있는 추론에서, 어떤 형태의 외부 서버를 피할 수있는 방법이 없다는 것이 분명해야합니다. 건초 더미에서 바늘을 찾는 유일한 방법은 그것이 어디에 있는지 추적하는 것입니다. 그러한 공급자에게 원하는 자질은 다음과 같습니다.

  • 신뢰성 : 주어진 날의 일이 얼마나 자주 발생합니까?
  • 속도 : 사물이 얼마나 빨리 반응합니까?
  • 영구 : 얼마나 오래 지속될 것으로 기대하십니까? 인터넷이 발전함에 따라 접근에 대한 액세스를 잃을 것입니까?

언급 했듯이이 법안에 적합한 쉽게 사용할 수있는 자원이 많이 있습니다. 이메일 서버 (현재 사용중인대로), 웹 서버, FTP 서버, DNS 서버, IRC 채널, 트위터 계정, 웹 포럼 ....

잠시 후에 생생하고 중앙 서버없이 업데이트 해야하는 응용 프로그램의 문제는 일반적인 문제이지만 바이러스 작가 사이에서 일반적으로 일반적으로 일반적으로 일반적으로 제공됩니다. 섬기는 사람. 즉, 수년간의 표준 솔루션에는 이메일, HTTP 서버, FTP 서버, IRC 채널 및 동적 DNS 서버가 포함되었습니다. 이러한 각 범주의 다른 서버는 속도, 신뢰성 및 영구성이 다양하므로 선택하는 작업은 판단으로 되돌아갑니다. IRC 채널은 빠르고 설정하기 쉽기 때문에 언급 할 가치가 있지만 인터넷이 진화함에 따라 실제로 사라질 수 있습니다.

다양한 "클라이언트 찾기"기술을 사용하는 배포 애플리케이션의 예로서 다운로드 할 수 있습니다. BO2K 소스, a, uh "원격 관리 유틸리티". 이는 원격 업데이트 클라이언트의 모든 기능에 대한 통찰력을 제공 할 수 있습니다.

반복하기 위해. 문제에 세 부분이 있다고 생각합니다.

  1. 서로를 찾는 기계 (위 참조)
  2. 연결을 설정하는 기계 (다시, SSL, SSH 등은 쉽게 사용할 수 있습니다)
  3. 기계가 데이터를 교환합니다. "게시/구독"모델을 사용하거나 자신의 간단한 것을 굴릴 수 있습니다. 규약. 나는 자동 업데이트 클라이언트가있는 회사에서 일했고 그것이 우리가 한 일입니다. 자체 프로토콜을 만드는 이유는 1) 가장 간단한 상황에서도 속도와 신뢰성에 대한 요구 사항은 데이터 교환 된 것과 마찬가지로 다릅니다. 정말 간단한 데이터 교환을위한 프로토콜, 3. 다른 응용 프로그램이 다른 방법과 언어를 사용하기 때문에 간단한 데이터 교환을위한 프로토콜이 지배적이지 않습니다. 보다 복잡한 상황에서는 실제로 전체 프로토콜이 있지만 복잡성이 다양하면 간단한 데이터 교환에 사용하기가 어색해질 것입니다. 그 방식 git scm 데이터를 보내는 것은 업데이트 프로토콜의 한 예입니다. 데이터베이스가 GIT가 보내는 소스 코드와 비슷한 경우 GIT를 사용하여 데이터베이스를 유지할 수 있습니다. 그러나 당신의 업데이트 접근 방식은 Git이 그 일을 밀접하게하는 것과 비슷하지 않을 것입니다. 또 다른 예제 프로토콜은 하나 또는 다른 버전입니다 웹 서비스 비누와 같은. 이 프로토콜은 단지 포장하다 XML 및 HTTP를 사용하여 한 컴퓨터에서 함수를 호출하는 프로세스. 응용 프로그램간에 이미 소켓 통신을 설정할 수 있다면이를 수행 할 이유가 없습니다. 웹 서비스를 구현하려면 HTTP 서버를 실행하고 HTTP 클라이언트를 원시 데이터로 구문 분석해야합니다. 소켓을 통해 데이터를 직접 보낼 수 있다는 점을 고려할 때이를 수행 할 이유가 없습니다. 그래서 당신은 당신 자신을 굴리기로 돌아 왔습니다.

어쨌든 간단한 프로토콜의 예는 다음과 같습니다.

  • 한 앱은 먼저 데이터의 색인을 색인 배열로 보냅니다.
  • 다른 앱은 새로운 항목 목록을 보냅니다.
  • 그런 다음 첫 번째 앱은 해당 실제 항목을 보냅니다.

그런 다음 앱은 역할을 변경하고 다른 방식으로 데이터를 교환합니다. 프로토콜에 주어진 "핸드 셰이크"는 의사 코드에서 다음과 같습니다.

void update_database(in_stream, out_stream) {
  Get_Index_Of_Other_Machines_Items(in_stream);
  Send_Index_Of_Items_You_Need(out_stream);
  Get_Items_You_Need(in_stream);
}

이러한 데이터 교환의 반대쪽을 구현하는 다른 기능이 있습니다.

추가 고려 사항은 데이터베이스의 각 항목이 독립적으로 생성되는 경우 분산 데이터베이스의 모든 항목을 참조 할 수 없으면 고유 한 ID를 생성해야한다는 것입니다. 당신은 a를 생성 할 수 있습니다 안내 또는이 목적을 위해 큰 임의의 숫자.

당신이 그것을 사용하려고한다면 당신은 의심 할 여지없이 이것을 조정하고 더 발전시켜야 할 것입니다.

그러나 그것을 명심하십시오 응용 프로그램이 모두 가끔 업데이트되는 경우, 주어진 인스턴스에 데이터 항목이있을 것이라고 확신 할 수있는 방법은 없습니다. 예를 들어, 하루 만에 기계의 절반 만 온라인으로 이동한다고 가정합니다. ~ 후에 오후 5시이고 나머지 절반은 온라인으로 만갑니다 ~ 전에 오후 5시. 이 경우 두 그룹 그룹은 서로 직접 업데이트 할 경우 데이터를 공유하지 않습니다. 반면에, 기계가 실제로 분산 된 시간 (내가 설명한 것처럼 패턴에 따라)에서 실제로 수행한다면, 각 기계는 결국 모든 업데이트를 얻을 가능성이 높습니다 (적어도 모든 기존 업데이트). .

이 모든 것을 감안할 때, 나는 당신이 정확히 고려해야한다고 생각합니다 얼마나 드물게 응용 프로그램이 연결되고 모든 데이터가 완전히 동기화되는 것이 얼마나 중요한지. 극심한 적은 상황에서 외부 서버를 사용하여 데이터를 이동하고 클라이언트를 찾는 것이 필요할 수 있습니다. 정기 이메일 이 문제에 대한 자연스러운 해결책입니다. 다른 사람이 온라인 상태 일 때 컴퓨터가 온라인 상태가 없다면 사용하는 것이 한 가지 일 것입니다. 특정 이메일 계정에 대한 액세스가 걱정 된 경우 여러 개의 이메일 주소 목록으로 각 앱을 시작할 수 있으며 모든 주소를 확인하고 주어진 주소가 더 많은 주소로 응용 프로그램을 업데이트 할 수있는 옵션이 있습니다. 모든 것을 위해 이메일을 사용하면 단순성의 미덕이 있습니다. 당신은 사용할 수 있습니다

더 가벼운 참고로 숫자 스테이션 정보 업데이트 문제를 해결하기위한 인터넷 이전의 노력이지만 귀하의 사례에도 맞지 않을 것입니다 (그러나 세계의 많은 부분에 방송됩니다).

다른 팁

보다 게시 / 구독 비동기 메시징 패러다임.

구현 예는 예입니다 Apache ActiveMQ:

Apache ActiveMQ는 빠르며, 많은 교차 언어 클라이언트 및 프로토콜을 지원하며, 사용하기 쉬운 엔터프라이즈 통합 패턴과 많은 고급 기능과 함께 JMS 1.1 및 J2EE 1.4를 완전히 지원합니다.

나는 몇 년 전에 당신이 묘사 한 것과 비슷한 앱을 디자인했습니다. 각 데스크탑에서 실행되는 "광고 서버"를 설계했으며 UDP를 사용하여 네트워크에서 실행되는 다른 프로그램에 대한 상태를 방송합니다. 이제이 응용 프로그램을 실행하는 방법에 따라 자체 문제 세트가 있습니다.

포트에서 '리스너'를 설정하고 데이터베이스 서버를 해싱하여 선택한 것과 응용 프로그램이 연결된 데이터베이스를 선택했습니다. 이를 통해 방송을받은 사람이라면 누구나 나와 동일한 데이터베이스를 사용하고 있으며 앱의 여러 인스턴스가 데스크탑 (설계 요구 사항)에서 실행할 수있었습니다.

그런 다음 특정 이벤트를 방송하는 다양한 "BroadcastMessage ()"기능을 설정합니다. API를 사용하여 사용자 지정 페이로드 데이터를 사용하여 사용자 정의 이벤트를 만들 수있는 기능을 사용한 다음 해당 이벤트에 대해 리시너를 등록하여 해당 이벤트가 들어 왔을 때 레지스트라에 통보 할 수있는 기능을 사용하여 개발자가 허용했습니다. 함께 제공되는 데이터.

예를 들어, 앱이 시작되면 "I 'm Here"메시지를 방송하고 듣는 사람은 메시지를 먹거나 무시하거나 답장 할 수 있습니다. "I 'm here"에는 실행중인 응용 프로그램의 IP 주소가 포함되어있어 모든 클라이언트가 추가 데이터 업데이트를 위해 TCP 연결을 통해 연결할 수 있도록 전달해야했습니다.

나는 UDP를 선택했다. 왜냐하면 다른 모든 실행중인 인스턴스에서 이러한 방송을 볼 필요가 없기 때문이다. 같은 화면에있는 동안 누군가가 DB에 레코드를 추가 한 경우, 새로운 레코드가 데스크탑에서 '나타나는'것입니다.

또한 관리자가 앱을 실행하는 동안 사용자의 권한을 변경하면 사용자가 앱을 종료하고 다시 입력 할 필요가없고 업데이트가 수신되어 바로 처리되었으며 사용자가 수행 할 수 있다는 것이 유용했습니다. 그들이 필요로하는 것.

이러한 유형의 메시지에 대해 듣는 스레드에서 리스너를 설정하는 것은 정말 쉽습니다 ... 예제 코드가 필요한 경우, 나도 제공 할 수 있지만 C ++에 있으며 Windows 용으로 설계되었지만 RAW WSOCK32를 사용합니다. .lib, 따라서 모든 UNIX 플랫폼으로 전송해야합니다. (내가 많이 사용했던 것처럼 dword를 입력하면됩니다 ..).

네트워크 관리 에서이 문제를 몇 번 해결했습니다. 기본적인 관심사는 "발견"인 것 같습니다. 앱이 어떻게 서로를 발견합니까?

솔직히 가장 쉬운 방법은 IP 주소와 마스크 (대부분 클래스 C)를 알고 해당 클래스의 각 컴퓨터에 연결하려고 시도하는 것입니다.

클래스 C로 기본값을 사용하는 경우 대부분의 네트워크에서 항상 작동합니다. 그런 다음 특정 IP 주소를 추가하여 연결하여 추가 서브넷을 추가 할 수 있습니다.

클래스 C를 발견하려면 IP 주소 (192.168.2.77이라고합시다)를 파악한 다음 192.168.2. (1-254)의 모든 것을 반복하여 각각의 연결을 열려고합니다.

여러 스레드로 수행했습니다 (한 번에 모든 장치를 한 번에 핑하고 3 초 안에 좋은 결과를 얻을 수 있습니다. 수백 스레드로 5 분 안에 클래스 B 네트워크를 발견했습니다!) 단일 스레드에서 하나에서 다음으로 다음으로-하지만 시간 초과가 실제로 낮은 지 확인하면 (1/2 초 정도) 그렇지 않으면 1/2 초에도 영원히 걸릴 것입니다. 1 분이 걸립니다. 라운드를 만들기 위해.

당신은 아마도 디스커버리 스레드를 배경으로 더 낮은 속도로 실행하고 항상 새로운 동료를 검색하고 싶을 것입니다.

더 빠른 시작을 위해 "알려진 좋은"IP 주소를 캐시하십시오.

어려운 문제는 아니지만 사소한 것은 아닙니다. 약간의 레그 워크를 기대합니다.

또한 외부 서브넷을 스캔하기 위해 새 IP/마스크를 추가 할 수 있기를 원할 것입니다. 인터넷에서 장치에 연락하려는 경우이 주위에 아무런 방법이 없습니다 (한 PC가 인터넷을 발견하면 원하는 경우 다른 모든 사람에게 주소를 보낼 수 있으며 실제로는 정말 빠르게 성장할 수 있습니다!)

당신은 이것을 완전히 P2P가되기를 원하십니까?

커뮤니케이션 보안의 경우 SSL이 괜찮을 것입니다. Java는 당신이 사용하는 것이라면 이들을 매우 간단한 방식으로 지원합니다. 여기에 있습니다 참조 Java 6의 SSL 용

확인.약속대로 여기에 몇 가지의 샘플 코드에서 찢어진 내 응용 프로그램이 있습니다.이지 않을 컴파일하고,이 예는 방법 았습니다.당신이해야 할 수도 있습니다 당신은 완전히 다릅니다.또한,이를 위해 기록된 윈도우,그리고 당신이 볼 수 코드에서,윈도우를 사용하는 메시지 데이터를 전송 서버 사이드의 주요 응용 프로그램이지만,그 모든 방법에 따라 당신은 그것을 사용할 계획입니다.내가 남의 일부는 더 많은 흥미로운 부분에 대한 당신을 참조합니다.

으로 보안에 대한 부분,저는 그림을 처리할 수 있는 부분입니다.는 단순한 물질의 데이터를 암호화하기 전에 그것을 넘어 와이어를 사용하여 몇 가지 잘 알고 암호,그래서 나는 생각하지 않았을 포함합니다.예를 들어,당신은 당신이 볼 수 있는 방법을 내가 건설된 패킷 헤더,다음 있는 일반적으로 페이로드의 또 다른 구조입니다.그래서,암호화하는 구조물,보낼 데이터,그리고 다음 해 다른 쪽 끝에서,그리고 복사하여 적절한 구조입니다.

// Some defines that you may see in the code, all of which are user defined...
#define ADVERTISE_SERVER           0x12345678 // Some unique ID for your advertisement server
#define ACTIVITY_NONE              0x00000000
#define ACTIVITY_LOGON             0x00000001
#define ACTIVITY_LOGOFF            0x00000002
#define ACTIVITY_RUNNING           0x00000004
#define ACTIVITY_IDLE              0x00000005
#define ACTIVITY_SPECIFIC          0x00000006


enum Advertisements {
   ADVERTISE_SHUTDOWN,
   ADVERTISE_MESSAGE,
   ADVERTISE_DEBUG,
   ADVERTISE_OVERLAPPED,
   ADVERTISE_BROADCAST_IDENTITY,
   ADVERTISE_IDENTITY,
   ADVERTISE_PARAMETER_CHANGE
};

struct TAdvertiseServerPacket {
   UINT     uiAdvertisePacketType;
   DWORD    dwPacketLength;
   bool     bRequestReply;
   UINT     uiReplyType;
   bool     bOverlappedResult;
   int      iPacketId;
   bool     bBroadcast;
   char     GuidHash[35];
   BYTE     PacketData[1024];
};

struct TAdvertiseIdentity {
   TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
   char  szUserName[LEN_APPL_USERNAME + 1];
   char  szDatabase[MAX_PATH];
   char  szConfiguration[MAX_PATH];
   char  szVersion[16];
   long  nUserId;
   char  szApplication[MAX_PATH];
   char  szActivity[33];
   UINT  uiStartupIndc;
};

struct TAdvertiseMessage {
   char              MessageFrom[LEN_APPL_USERNAME + 1];
   char              MessageText[512];
};

struct TAdvertiseItemUpdate {
   NMHDR             pNMHDR;
   long              nItemId;
   long              nItemTypeId;
   char              szItemName[LEN_ITEM_NAME + 1];
   bool              bState;
};

struct TAdvertiseItemUpdateEx {
   NMHDR             pNMHDR;
   long              nItemId;
   bool              bState;
   bool              bBroadcast;
   DWORD             dwDataSize;
   void              *lpBuffer;
};

struct TOverlappedAdvertisement {
   int               iPacketId;
   BYTE              Data[1020];
};

DWORD WINAPI CAdvertiseServer::Go(void* tptr)
{
   CAdvertiseServer *pThis = (CAdvertiseServer*)tptr;

   /* Used and reused for Overlapped results, */
   DWORD BufferSize       = 0;
   BYTE *OverlappedBuffer = NULL;
   bool bOverlapped       = false;
   int  iOverlappedId     = 0;
   DWORD BufferPosition   = 0;
   DWORD BytesRecieved    = 0;
   TAdvertiseItemUpdateEx *itemex = NULL;
   UINT uiPacketNumber    = 0;

   bool Debug = false;
#ifdef _DEBUG
   Debug = true;
#endif
   {
      DWORD dwDebug = 0;
      dwDebug = GetParameter(ADVERTISE_SERVER_DEBUG); // GetParameter is part of the main program used to store running config values.
      if(dwDebug > 0)
      {
         Debug = true;
      }
   }
   WSAData wsaData;
   WSAStartup(MAKEWORD(1,1), &wsaData);
   ServerSocket = socket(PF_INET, SOCK_DGRAM, 0);
   if(ServerSocket == INVALID_SOCKET)
   {
      CLogging Log("Client.log");
      ServerSocket = NULL;
      Log.Log("Could not create server advertisement socket: %d", GetLastError());
      return -1;
   }
   sockaddr_in sin;
   ZeroMemory(&sin, sizeof(sin));
   sin.sin_family = AF_INET;
   sin.sin_port = htons(Port);
   sin.sin_addr.s_addr = INADDR_ANY;
   if(bind(ServerSocket, (sockaddr *)&sin, sizeof(sin)) != 0)
   {
      CLogging Log("Client.log");
      Log.Log("Could not bind server advertisement socket on port: %d Error: %d", Port, GetLastError());
      DWORD dwPort = 0;
      dwPort = GetParameter(ADVERTISE_SERVER_PORT); // Again, used to set the port number, if one could not be figured out.
      if(dwPort > 0)
      {
         return -1;
      }
      Port = 36221;
      sin.sin_port = htons(Port);
      if(bind(ServerSocket, (sockaddr *)&sin, sizeof(sin)) != 0)
      {
         CLogging Log("Client.log");
         Log.Log("Could not bind server advertisement socket on port: %d Error: %d Could not start AdvertiseServer after two attempts.  Server failed.", Port, GetLastError());
         return -1;
      }
   }

   SECURITY_ATTRIBUTES sa;
   sa.bInheritHandle = TRUE;
   sa.lpSecurityDescriptor = NULL;
   sa.nLength = sizeof(SECURITY_ATTRIBUTES);
   HANDLE mutex = CreateMutex(NULL, FALSE, "Client.Mutex"); // Used to keep and eye on the main program, if it shuts down, or dies, we need to die.
   while (1)
   {
      TAdvertiseServerPacket ap;
      sockaddr_in sin;
      int fromlen = sizeof(sin);
      fd_set fds;
      FD_ZERO(&fds);
      FD_SET(ServerSocket, &fds);
      timeval tv;
      tv.tv_sec = 15;
      tv.tv_usec = 0;
      int err = select(0, &fds, NULL, NULL, &tv);
      if(err == SOCKET_ERROR)
      {
         CLogging Log("Client.log");
         Log.Log("Advertise: Winsock error: %d", WSAGetLastError());
         Beep(800, 100);
         break;
      }
      if(err == 0)
      {
         if(WaitForSingleObject(mutex, 0) != WAIT_OBJECT_0)
         {
            continue; // Main app is still running
         }
         else
         {
            Beep(800, 100); // Main app has died, so exit our listen thread.
            break;
         }
      }

      int r = recvfrom(ServerSocket, (char *)&ap, sizeof(ap), 0, (sockaddr *)&sin, &fromlen);

      if(r != sizeof(TAdvertiseServerPacket))
      {
         continue;
      }
      switch(ap.uiAdvertisePacketType)
      {
         // This is where you respond to all your various broadcasts, etc.
         case ADVERTISE_BROADCAST_IDENTITY:
         {
            // None of this code is important, however you do it, is up to you.
            CDataAccess db(CDataAccess::DA_NONE);
            TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
            ZeroMemory(ComputerName, sizeof(ComputerName));
            DWORD len = MAX_COMPUTERNAME_LENGTH;
            GetComputerName(ComputerName, &len);
            if(pThis->szActivity) {
               CAdvertiseServer::AdvertiseIdentity(ComputerName, CDataAccess::GetLoggedInUserName(), CDataAccess::DatabaseConfiguration(), CDataAccess::DatabaseConfiguration(), ACTIVITY_SPECIFIC, pThis->szActivity, false);
            } else {
               CAdvertiseServer::AdvertiseIdentity(ComputerName, CDataAccess::GetLoggedInUserName(), CDataAccess::DatabaseConfiguration(), CDataAccess::DatabaseConfiguration(), ACTIVITY_RUNNING, NULL, false);
            }
         }
         case ADVERTISE_IDENTITY:
         {
            TAdvertiseIdentity ident;
            memcpy((void*)&ident, (void*)ap.PacketData, ap.dwPacketLength);
            Listener::iterator theIterator;
            theIterator = pThis->m_Listeners.find(ap.uiAdvertisePacketType);
            if(theIterator == pThis->m_Listeners.end())
            {

               //We got an Identity Broadcast, but we're not listening for them.
               continue;
            }
            {
               itemex = new TAdvertiseItemUpdateEx;
               ZeroMemory(itemex, sizeof(TAdvertiseItemUpdateEx));
               memcpy((void*)&ident, ap.PacketData, ap.dwPacketLength);
               itemex->pNMHDR.code     = (*theIterator).first;
               itemex->pNMHDR.hwndFrom = (*theIterator).second;
               itemex->pNMHDR.idFrom   = ADVERTISE_SERVER;
               itemex->dwDataSize      = sizeof(TAdvertiseIdentity);
               itemex->lpBuffer        = (void*)&ident;
               SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)itemex);
               delete itemex;
            }
         }
         case ADVERTISE_SHUTDOWN:
         {
            TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
            ZeroMemory(ComputerName, sizeof(ComputerName));
            DWORD len = MAX_COMPUTERNAME_LENGTH;
            GetComputerName(ComputerName, &len);
            CString guid;
            guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);
            if(stricmp(ap.GuidHash, CDataAccess::HashPassword(guid)) == 0)
            {
               return 1;
            }
         }
         case ADVERTISE_MESSAGE:
         {
            TAdvertiseMessage msg;
            memcpy((void*)&msg, (void*)ap.PacketData, ap.dwPacketLength);
            CString msgtext;
            msgtext.Format("Message from: %s\r\n\r\n%s", msg.MessageFrom, msg.MessageText);
            ::MessageBox(NULL, msgtext, "Broadcast Message", MB_ICONINFORMATION | MB_SYSTEMMODAL);
            break;
         }
         case ADVERTISE_OVERLAPPED:
         {
            // I left this code in here, as it's a good example of how you can send large amounts of data over a UDP socket, should you need to do it.
            BufferPosition = (1020 * ((ap.uiReplyType - 1) - 1));
            if(BufferPosition > BufferSize) {
               BufferPosition -= 1020;
            }
            TOverlappedAdvertisement item;
            ZeroMemory(&item, sizeof(TOverlappedAdvertisement));
            memcpy((void*)&item, (void*)ap.PacketData, ap.dwPacketLength);
            if(item.iPacketId == iOverlappedId)
            {
               DWORD ToCopy = (sizeof(item.Data) > (BufferSize - BytesRecieved) ? BufferSize - BytesRecieved : sizeof(item.Data));
               memcpy((void*)&OverlappedBuffer[BufferPosition], (void*)item.Data, ToCopy);
               BytesRecieved += ToCopy;
               if(BytesRecieved < BufferSize)
               {
                  continue;
               }
            }
         }
         default:
         {
            // What do we do if we get an advertisement we don't know about?
            Listener::iterator theIterator;
            if(bOverlapped == false)
            {
               theIterator = pThis->m_Listeners.find(ap.uiAdvertisePacketType);
               if(theIterator == pThis->m_Listeners.end())
               {
                  continue;
               }
            }

            // Or it could be a data packet
            TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
            ZeroMemory(ComputerName, sizeof(ComputerName));
            DWORD len = MAX_COMPUTERNAME_LENGTH;
            GetComputerName(ComputerName, &len);
            CString guid;
            guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);
            bool FromUs = stricmp(ap.GuidHash, CDataAccess::HashPassword(guid)) == 0;
            if(((FromUs && Debug) || !FromUs) || ap.bBroadcast)
            {
               if(ap.bOverlappedResult)
               {
                  if(ap.uiReplyType == 1)
                  {
                     itemex = new TAdvertiseItemUpdateEx;
                     ZeroMemory(itemex, sizeof(TAdvertiseItemUpdateEx));
                     memcpy(itemex, ap.PacketData, ap.dwPacketLength);
                     OverlappedBuffer = (BYTE*)malloc(itemex->dwDataSize);
                     BufferSize = itemex->dwDataSize;
                     ZeroMemory(OverlappedBuffer, itemex->dwDataSize);
                     bOverlapped = true;
                     iOverlappedId = ap.iPacketId;
                     uiPacketNumber = ap.uiReplyType;
                  }
                  continue;
               }
               if(bOverlapped)
               {
                  itemex->pNMHDR.code     = (*theIterator).first;
                  itemex->pNMHDR.hwndFrom = (*theIterator).second;
                  itemex->pNMHDR.idFrom   = ADVERTISE_SERVER;
                  itemex->dwDataSize      = BufferSize;
                  itemex->lpBuffer        = (void*)OverlappedBuffer;
                  SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)itemex);
                  delete itemex;
                  free(OverlappedBuffer);
                  BufferSize       = 0;
                  OverlappedBuffer = NULL;
                  bOverlapped      = false;
                  iOverlappedId    = 0;
                  BufferPosition   = 0;
                  BytesRecieved    = 0;
                  itemex           = NULL;
                  uiPacketNumber   = 0;
                  break;
               }
               TAdvertiseItemUpdate *item = new TAdvertiseItemUpdate;
               ZeroMemory(item, sizeof(TAdvertiseItemUpdate));
               memcpy(item, ap.PacketData, ap.dwPacketLength);

               item->pNMHDR.code     = (*theIterator).first;
               item->pNMHDR.hwndFrom = (*theIterator).second;
               item->pNMHDR.idFrom   = ADVERTISE_SERVER;
               SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)item);
               delete item;
            }
            break;
         }
      }
   }
   try {
      ResetEvent(ServerMutex);
      CloseHandle(pThis->ServerMutex);
      closesocket(ServerSocket);
      return 0;
   }
   catch(...) {
      closesocket(ServerSocket);
      return -2;
   }
}

// Here's a couple of the helper functions that do the sending...
bool CAdvertiseServer::SendAdvertisement(TAdvertiseServerPacket packet)
{
   TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
   ZeroMemory(ComputerName, sizeof(ComputerName));
   DWORD len = MAX_COMPUTERNAME_LENGTH;
   GetComputerName(ComputerName, &len);
   CString guid;
   guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);

   strcpy(packet.GuidHash, CDataAccess::HashPassword(guid));

   bool bRetval = false;
   SOCKET s = socket(PF_INET, SOCK_DGRAM, 0);
   if(s != INVALID_SOCKET)
   {
      BOOL tru = TRUE;
      setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&tru, sizeof(tru));
      sockaddr_in sin;
      ZeroMemory(&sin, sizeof(sin));
      sin.sin_family = PF_INET;
      sin.sin_port = htons(Port);
      sin.sin_addr.s_addr = INADDR_BROADCAST;
      if(sendto(s, (char *)&packet, sizeof(packet), 0, (sockaddr *)&sin, sizeof(sin)) > 0)
      {
         bRetval = true;
         if(packet.bRequestReply)
         {
           // Here is where your work comes in, in setting up a reply, or making a TCP connection back to the other client.
         }
      }
      closesocket(s);
   }
   return bRetval;
}

bool CAdvertiseServer::Advertise(UINT uiAdvertisement, long nItemId, bool bState, void *lpBuffer, DWORD dwDataSize, bool bBroadcast)
{
   TAdvertiseServerPacket packet;
   ZeroMemory(&packet, sizeof(packet));
   TAdvertiseItemUpdateEx   item;
   ZeroMemory(&item, sizeof(item));

   UINT packetnum = 1;
   packet.bOverlappedResult = true;
   packet.bRequestReply = false;
   packet.uiAdvertisePacketType = uiAdvertisement;
   packet.dwPacketLength = sizeof(item);
   packet.uiReplyType = packetnum;
   packet.bBroadcast = bBroadcast;
   item.nItemId = nItemId;
   item.bState  = bState;
   item.dwDataSize = dwDataSize;
   memcpy((void*)packet.PacketData, (void*)&item, sizeof(item));
   packet.iPacketId = GetTickCount();
   if(SendAdvertisement(packet))
   {
      BYTE *TempBuf = new BYTE[dwDataSize];
      memcpy(TempBuf, lpBuffer, dwDataSize);

      DWORD pos = 0;
      DWORD BytesLeft = dwDataSize;
      while(BytesLeft)
      {
         TOverlappedAdvertisement item;
         packet.uiAdvertisePacketType = ADVERTISE_OVERLAPPED;
         packet.bOverlappedResult = BytesLeft > 1020;
         item.iPacketId = packet.iPacketId;
         memcpy((void*)item.Data, (void*)&TempBuf[pos], (BytesLeft >= 1020 ? 1020 : BytesLeft));
         memcpy((void*)packet.PacketData, (void*)&item, sizeof(item));
         packet.dwPacketLength = sizeof(item);
         packet.uiReplyType++;
         if(SendAdvertisement(packet))
         {
            if(BytesLeft >= 1020)
            {
               BytesLeft -= 1020;
               pos += 1020;
            }
            else
            {
               BytesLeft = 0;
            }
         }
      }
      delete TempBuf;
   }
   return true;
}

void CAdvertiseServer::Shutdown()
{
   TAdvertiseServerPacket packet;
   packet.uiAdvertisePacketType = ADVERTISE_SHUTDOWN;
   SendAdvertisement(packet);
}

좋아 - 그래서 MQ와 그 유형의 물건은 지나치게 죽는 것처럼 들립니다.

당신의 앱에 대한 나의 이해 :

동일한 네트워크의 여러 머신에서 실행되는 데스크탑 앱 - 자체 데이터베이스가 있으며 서로를 발견해야합니다.

왜 안 돼:

1) UDP 브로드 캐스트 / 정기적으로 청취하여 "동일한 네트워크에서 다른 기계를 찾으십시오" - Java의 예 : Java : http://java.sun.com/docs/books/tutorial/networking/datagrams/index.html

2) 발견 후 실제 통신을 위해 SSL 소켓을 사용하십시오.
http://stilius.net/java/java_ssl.php....
http://www.exampledepot.com/egs/javax.net.ssl/client.html

BitTorrent Typed Setup 사용을 고려해 보셨습니까?

사용 된 의사 소통 원칙은 응용 프로그램 구축을위한 상당히 견고한 기반을 제공해야합니다. 두 노드가 서로에 대해 알게 된 다음 거기에서 구축됩니다. 나는 사용한다 단조로 렌트 비공개 (100- 노드) 데이터 네트워크를 실행하려면 RSS는 어떤 파일이 어디에 있어야하는지 (수정 된 버전의 WordPress)를 발표하고 SSH 터널에서 모든 작업을 수행해야합니다. 네트워크를 관리하는 중앙 서버가 있지만 100 개의 노드 중 하나에서 쉽게 살 수 있습니다. 동적 DNS 서비스를 사용하여 첫 번째 노드는 내 서버가 줄어든 경우 자체 트래커를 백업으로 설정합니다.

XML 파일을 메시징 체계로 사용하거나 BitTorrent 네트워크의 전송을 수정하여 데이터 패킷을 앱으로 직접 전송할 수 있습니다. 나는 당신이 찾고있는 것의 개념이 Bittorrent에 있다고 생각합니다. Fire Up의 첫 번째 노드는 동적 DNS 항목을 회수합니다 (Dyndns 사용하기 쉽습니다 API) 네트워크에 활성 호스트가 없으면. (단점이 있습니다 ... TTL 창 내에서 두 개의 트래커가 시작될 때 동기화 문제가 발생합니다)

참조가 꽤 있습니다 SSH 터널링 거기에서 나는 재미있는 다이어그램 때문에 이것을 사용합니다. SSH 터널링은 사용 가능한 가장 효율적인 방법이 아니지만 SSL 터널에서 통신을 프로그래밍 방식으로 래핑 해야하는 매우 좋은 대안입니다.

나는 생각이 뒤죽박죽이라는 것을 알고 있습니다. 나는 그것이 당신에게 올바른 방향으로 자신을 가리키는 데 약간의 도움을주기를 바랍니다. PS ... 완전 휴대용 솔루션의 경우 Java 또는 .NET (Mono에서 실행 중입니다. Mono를 실행하는 AppleTV가 있습니다). 그러면 OS는 운영의 유연한 부분 일 수도 있습니다.

분산 캐시 또는 오프라인 DB 기능이 필요한 것 같습니다 - 언어에 따라 (Java/C#/...) 다양한 옵션이 있습니다 ...

Amazon과 Microsoft는 모두 임의의 연결, 협력 애플리케이션 사이의 랑데부 지점으로 사용할 수있는 대기열을 호스팅했습니다. 아마존은 상업적이며 무료가 아닙니다. 마이크로 소프트 현재 무료이지만 영원히 보장되지는 않습니다. 그것은 당신이 직면 한 문제를 정확하게 해결합니다. 연결된 클라이언트를위한 펍/서브 모델을 제공합니다.

나는 여기서 무언가를 놓쳤을 수도 있지만, 당신의 프로그래밍 언어를 선택하지 못했습니다. .NET 프레임 워크를 사용하는 Windows 기반 환경에서 가장 좋은 선택은 WCF를 사용하여 간단한 구성으로 보안/견고성을 추가 할 수 있습니다. .NET 지향적이지 않은 Windows 기반 컴퓨터가있는 솔루션을 원한다면 MSMQ를 사용하여 이러한 표준에 맞게 커뮤니케이션 프레임 워크입니다.

WCF가있는 P2P에 대한 좋은 기사가 있습니다http://msdn.microsoft.com/en-us/magazine/cc188685.aspx. 코드를 제공하지만 .NET3, WCF, Vista 이상을 가정합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top