TCP kliens és szerver egy oldalon.

TCP kliens és szerver egy oldalon.
2008-03-24T15:01:22+01:00
2008-03-25T08:45:01+01:00
2022-11-08T06:35:42+01:00
Spuriga
Sziasztok!

Van egy programom, amely indításkor csatlakozik egy TCP szerver programhoz. Ha a csatlakozás sikeres volt, akkor ugyanez a program indít egy szerver szálat, és vár, hogy csatlakozzanak hozzá. Miután csatlakozott egy kliens ehhez a szerver részhez, kommunikálnak, és a kliens lecsatlakozik.
A probléma az, hogy miután a kliens lecsatlakozott, a szerver nem képes újra megnyitni a portot (bind failed: 10048).
A baj az, hogy ennek a programnak (melynek egyik szála kliens, másik szerver) szerver oldalához akár percenként más más gép csatlakozhat. A kliens oldala viszont csak egyszer, program indulásakor csatlakozik föl a szerverre, és akár egész nap úgy van.

- A kérdésem az, hogy hogy tudok lezárni egy szerver socketet úgy, hogy közben egy másik definiált socket, másik porton aktív és kommunikál.

- A bind failed: 10048-as hibaüzenet szerint a socket használatban van, tehát nem sikerült lezárnom előtte, habár a closesocket 0-val tért vissza.

- Próbáltam A setsockopt-al a REUSEADDR tulajdonságot beállítani, egy fokkal jobb volt a helyzet, mert kétszer tudott szervert csinálni, de utána elszállt a szerverszálam hibaüzenet nélkül az accept()-nél.

- A WSACleanup()-ot nem tudom kiadni parancsban, mert a másik port aktív.

- Próbáltam a DONTLINGER és LINGER-rel játszani, hogy azonnal zárja le a socketet a Windows, de változatlan volt a helyzet!

Remélem érthetően írtam le a problémám...

Segítségeteket előre is köszönöm!

A szervert így csinálom, ha esetleg kell: (MSDN-ről)
void SMMServer(void) { WSADATA SMMwsaData; sockaddr_in Clientservice; SOCKET AcceptSocket; while(Connection == true) { Log("Starting server...", LOG_SMMCOMM); int iResult = WSAStartup( MAKEWORD(2,2), &SMMwsaData ); if ( iResult != NO_ERROR ) Log("Error at WSAStartup()", LOG_SMMCOMM); // Create a socket. SMM_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( SMM_socket == INVALID_SOCKET ) { Log("Error at socket()", LOG_SMMCOMM); Sleep(1000); continue; } UINT port = GetPrivateProfileIntA(LOGSECTION, "SMMPort", DEFAULT_SMMPORT, TCPINI); Clientservice.sin_family = AF_INET; Clientservice.sin_addr.s_addr = INADDR_ANY; Clientservice.sin_port = htons((USHORT)port); //int on = 1; //setsockopt( SMM_socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof( on )); if ( bind( SMM_socket, (SOCKADDR*) &Clientservice, sizeof(Clientservice) ) == SOCKET_ERROR ) { sprintf_s(SmallLog, SMALL_LEN, "bind() failed: %d.", WSAGetLastError()); Log(SmallLog, LOG_SMMCOMM); Sleep(3000); continue; } // Listen on the socket. if ( listen( SMM_socket, 1 ) == SOCKET_ERROR ) Log("Error listening on socket.", LOG_SMMCOMM); sprintf_s(SmallLog, SMALL_LEN, "Waiting for a client to connect on port: %d ...", port); Log(SmallLog, LOG_SMMCOMM); while(1) { AcceptSocket = SOCKET_ERROR; while ( AcceptSocket == SOCKET_ERROR ) { Log("Accepting...", LOG_SMMCOMM); AcceptSocket = accept( SMM_socket, NULL, NULL); } sprintf_s(SmallLog, SMALL_LEN, "Client connected."); Log(SmallLog, LOG_SMMCOMM); SMM_socket = AcceptSocket; break; } SMMConnection = true; if (SMMServerTransThread != NULL) TerminateThread(SMMServerTransThread, 1); SMMServerTransThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SMMServerTrans, NULL, 0, NULL); int bytesRecv = 0; int bytesSent = 0; char *recvbuf = new char[DEFAULT_BUFLEN]; while(Connection == true) { bytesRecv = 0; bytesRecv = recv( SMM_socket, recvbuf, DEFAULT_BUFLEN, 0 ); if((bytesRecv == SOCKET_ERROR) || (bytesRecv == 0)) break; sprintf_s(Rlog, DEFAULT_BUFLEN + 100, "Message from SMM: %s", recvbuf); Log(Rlog, LOG_SMMCOMM); } delete [] Rlog; delete [] recvbuf; sprintf_s(SmallLog, SMALL_LEN, "Close server: %d", closesocket(SMM_socket)); Log(SmallLog, LOG_SMMCOMM); } return; }
Mutasd a teljes hozzászólást!
SMM_socket = AcceptSocket;

Ezt nem értem, de nagyon gyanús.

Az AcceptSocket lenne a kliensek felé a csatorna. Ezt kell lezárni, ha vége van egy kliens kiszolgálásnak.

Emiat taz értékadás miatt az SMM_socket-ben a ciklus újbóli végrehajtása esetén egy új socket handle-je lesz (új szerver socket!) és ugyanahhoz a címhez akarod, hogy kötve legyen az új socket. Így teljesen normális a hibajelenség.

Viszont a SMM_socket-et (server) sosem kellene lezárni, csak ha a szervert leállítod.

Másrészt a klienssel az SMMServerTrans függvényben kellene kommunikálni, nem? Én úgy látom, hogy ez az SMMServer-ben történik.

Lehet, hogy töredékes/összekeveredett a kód, amit beküldtél, de ha nem akkor át kellene gondolni, mi, melyik függvényben történik.
Mutasd a teljes hozzászólást!

  • [már látom, hogy próbáltad]
    Mutasd a teljes hozzászólást!


  • Akkor vagyok nagy bajban, ha nem lehet valamiért megcsinálni, remélem, hogy valakinek volt már ilyen tapasztalata esetleg!
    Mutasd a teljes hozzászólást!
  • Eleg rosszul olvasható a tagolás hiánya miatt, így nem próbáltam meg teljesen megérteni, hogyan működik. Viszont ami feltűnt, hogy ciklusban csinálszt listen/bind hívásokat, amit így ránézésre nem értek miért. Egy server socketet szerintem csak egyszer kell egy programban bind/listenelni, utána pedig accepttel csak fogadni a becsatlakozásokat. Nem ez okozza a problémát?
    Mutasd a teljes hozzászólást!
  • SMM_socket = AcceptSocket;

    Ezt nem értem, de nagyon gyanús.

    Az AcceptSocket lenne a kliensek felé a csatorna. Ezt kell lezárni, ha vége van egy kliens kiszolgálásnak.

    Emiat taz értékadás miatt az SMM_socket-ben a ciklus újbóli végrehajtása esetén egy új socket handle-je lesz (új szerver socket!) és ugyanahhoz a címhez akarod, hogy kötve legyen az új socket. Így teljesen normális a hibajelenség.

    Viszont a SMM_socket-et (server) sosem kellene lezárni, csak ha a szervert leállítod.

    Másrészt a klienssel az SMMServerTrans függvényben kellene kommunikálni, nem? Én úgy látom, hogy ez az SMMServer-ben történik.

    Lehet, hogy töredékes/összekeveredett a kód, amit beküldtél, de ha nem akkor át kellene gondolni, mi, melyik függvényben történik.
    Mutasd a teljes hozzászólást!
  • Köszönöm a gyors válaszokat!

    Ez az SMM_socket = AcceptSocket; sor az MSDN-ről van, volt ott régebben egy komplett server-cliens kód.
    A megoldás az lett, hogy az AcceptSocket = accept() után az SMM_socketet be !kell! zárni, és az AcceptSocketet használom.

    Bocsi, hogy a kód ilyen lett, csak beillesztettem, és valamiért a tabulátorokat nem veszi figyelembe.

    Valóban ciklusban van az egész, mert olyan a szerver, hogy egyszerre csak egy kliens csatlakozhat, és a lecsatlakozáskor a szerver leáll, majd újraindul.

    A klienssel valóban az SMMServerTrans függvényben kommunikálok, ez a függvény valójában csak loggol mident, amit kap, nem csinál mást.

    Még1x köszi mindenkinek!

    Gergő
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd