Hej,
Na wstępie: C++ znam tylko trochę. Do tej pory bardziej PHP, więc muszę się przestawić - potrzebuję dorobić do biblioteki DLL kod, który pozwoli na wystawienie portu zewnętrznego (w sieci lokalnej) i komunikację po tym porcie.
Biblioteka wspomniana to plugin do X-Plane 11, jeśli kogoś interesuje.
Wyglądałoby to tak, że X-Plane ładuje bibliotekę, która wystawia na zewnątrz jakąś funkcjonalność, a ja ją później obrabiam w innym miejscu.
Czy to w ogóle możliwe? Jeśli tak, to czy możecie mi zaproponować jakieś tutoriale? Korzystam z Visual Studio 2017 Community i wolałbym nie zmieniać środowiska, bo musiałbym wszystko przekonfigurować i znowu dostosowywać...
Ale ty bardziej potrzebujesz chyba klienta tcp, żeby po wczytaniu ddl i pozyskaniu danych wysłał je do tej zew apki.
Jak duże paczki danych to będą? Czy oczekujesz jakiegoś wyniku zwrotnego który będzie wpływał na tą gre?
Hm, próbuję zrozumieć co chcesz zrobić, podzielmy to na punkty
- Bierzesz cudzą bibliotekę która jest pluginem do X-Plane 11
- Dodajesz do biblioteki swój kod zamieniający ją w serwer TCP (nie wiem czy to poprawne okreslenie), ale tak żeby dalej była pluginem do X-Plane 11
- Komunikujesz się swoim programem z tym serwerem po TCP
Czy dobrze Cię rozumiem?
To ma być plugin do X-Plane, a nie nakładka na inny plugin. Odczytywanie danych w teorii mam, ale nie mam jak tego podpiąć, bo rozwiązania, które teraz zastosowałem, blokują wykonanie aplikacji dopóki nie otrzyma ona pakietów z aplikacji testowej.
Paczki będą małe (około 1-2 KB). Nie chcę robić tego tak, że apka na androida, która docelowo ma odczytywać te dane, miałaby być serwerem - to nie przejdzie. Jedyny wpływ na grę ma być akceptowalnym opóźnieniem pobrania danych i wysłania paczki - nic innego ten plugin nie będzie robił.
Na chwilę obecną próbuję skleić coś z winsock2, ale brak umiejętności podpięcia pod to wielowątkowości mnie blokuje.
Kolega wyżej wrzucił link do TCPListenera, ale z C#. Poszukałem na necie czegoś podobnego i znalazłem coś: https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1TcpListener.php ale to nie rozwiązuje chyba problemu z tym, że połączenie będzie blokowało wykonanie instrukcji w bibliotece...
Albo tylko na początku nawiązujesz połączenie i je podtrzymujesz przez całe życie apki wysyłając przez nie dane. Albo przy każdym wysyłaniu bierzesz i odpalasz wątek w którym to już sobie nawiązuje połączenie i wysyła te dane nie blokując apki.
http://www.cplusplus.com/reference/thread/thread/detach/
Zrobiłem jak w powyższym przykładzie - walnąłem zammiast 'join' - 'detach' i zadziałało :D
Tylko pytanie: czy przy wychodzeniu z pluginu powinienem jakoś zwalniać zasoby z winsock2?
Oto kod wątku, który obsługuje wystawienie danych:
void InstrumentDataThread() {
useSocket = true;
WSADATA wsaData;
SOCKET mainSocket;
sockaddr_in service;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != NO_ERROR) {
useSocket = false;
}
else {
mainSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (mainSocket == INVALID_SOCKET)
{
useSocket = false;
WSACleanup();
}
else {
memset(&service, 0, sizeof(service));
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(30123);
if (bind(mainSocket, (SOCKADDR *)& service, sizeof(service)) == SOCKET_ERROR)
{
useSocket = false;
closesocket(mainSocket);
}
else {
if (listen(mainSocket, 1) == SOCKET_ERROR) {
useSocket = false;
}
else {
SOCKET acceptSocket = SOCKET_ERROR;
acceptSocket = accept(mainSocket, NULL, NULL);
if (acceptSocket != SOCKET_ERROR) {
mainSocket = acceptSocket;
char Buffer[256];
while (useSocket) {
sprintf(Buffer, "%f", psi);
int bytesSent;
int bytesRecv = SOCKET_ERROR;
char recvbuf[2] = "";
bytesSent = send(mainSocket, Buffer, strlen(Buffer), 0);
while (bytesRecv == SOCKET_ERROR)
{
bytesRecv = recv(mainSocket, recvbuf, 1, 0);
if (bytesRecv == 0 || bytesRecv == WSAECONNRESET)
{
useSocket = false;
break;
}
if (bytesRecv < 0) {
useSocket = false;
break;
}
}
}
closesocket(mainSocket);
}
}
}
}
}
WSACleanup();
InstrumentDataThread();
}
Na chwilę obecną nie podtrzymuje połączenia.
Na końcu jest powtórne wywołanie funkcji, ale problem jest taki, że po połączeniu klienta i zakończeniu połączenia, aplikacja się wywala :/
Jak mogę zrestartować wątek tak, aby mógł się do niego podłączyć inny klient?
W funkcji głównej modułu uruchamiam pierwszy wątek tak:
std::thread t(InstrumentDataThread);
t.detach();