Asynchroniczny socket klienta nie łączy się z serwerem w pewnych przypadkach.

0

Dobry wieczór,

piszę sobie komunikator i mam pewien problem przy połączeniu socketa klienta z socketem serwera, mianowicie:

Klient łączy się z serwerem jeżeli:

  • uruchamiam klienty i serwer na jednej maszynie;
  • uruchamiam klienty na wirtualnej maszynie, a serwer na fizycznym systemie, który jest hostem dla tejże wirtualki;
  • uruchamiam klienty na wirtualnej maszynie i klienty na tej samej maszynie co serwer;

Klient nie łączy się jeżeli:

  • uruchamiam serwer na jednej fizycznej maszynie, a klienty na innych;

I tutaj właśni jest problem, bo przy próbie połączenia się dostaję taki oto wyjątek po stronie uruchomionego klienta:

0a3cbe6b67.png

Oba sockety są asynchroniczne, na kliencie wpisany jest z palucha adres IP i port serwera. Wygląda to jakby port serwera był blokowany ale tak nie jest i wyjątek jest wyrzucany nawet przy wyłączonych firewallach. Co dziwne telnet na port serwera działa prawidłowo.

Serwer i klienta wrzucam jako załącznik. Bardzo proszę pomoc, bo nie wiem w sumie gdzie jest problem. Przecież wirtualka to tak naprawdę inny komputer więc dlaczego na fizycznych maszynach klient nie widzi serwera? Jeżeli uruchomicie sobie oba te programy (na jednym kompie albo na kompie i wirtualce) i zmienicie adres IP, do którego ma się łączyć klient (Klasa KlientTCP) na właściwy dla swoich sieci to zobaczycie, że to coś działa :D

PS: Ten kod jest jeszcze mocno roboczy i wiele rzeczy będę zmieniał więc wygląda to jak wygląda :)

Pozdrawiam
Grzesiek

1

Ok, już wiem o co chodziło. Zupełnie pominąłem obiekty typu ManualResetEvent dla podłączenia klienta, wysłania i odebrania danych. Ale to była zmyła, ponieważ gdzieś czytałem, że jeżeli serwer/klient wykorzystuje GUI to nie można wykorzystywać tych obiektów, bo asynchroniczny socket spowoduje zawieszenie serwera/klienta.

Okazuje się, że to poniekąd prawda ale tylko w przypadku serwera stosującego GUI.
Okazuje się też, że jeżeli po drugiej stronie nie ma serwera to interfejs klienta również będzie "wisiał".

Wszystko przez wywołanie metody 'WaitOne()' zapewne, która uruchamiana jest zaraz po 'BeginConnect'.

Tutaj opis jakby co: https://msdn.microsoft.com/en-us/library/bew39x2a%28v=vs.110%29.aspx

0

Kolejna poprawka, tym razem działająca w stu procentach. Okazuje się, że asynchroniczny socket działający z GUI jednak nie potrzebuje obiektów typu ManualResetEvent.

Funkcje, które podłączają klienta do serwera napisane w MSDN tak:

private static void StartClient() {
	try {
		IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
		IPAddress ipAddress = ipHostInfo.AddressList[0];
		IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);

		Socket client = new Socket(AddressFamily.InterNetwork,
			SocketType.Stream, ProtocolType.Tcp);

		client.BeginConnect( remoteEP, 
			new AsyncCallback(ConnectCallback), client);
			connectDone.WaitOne();

		Send(client,"This is a test<EOF>");
		sendDone.WaitOne();

		Receive(client);
		receiveDone.WaitOne();

		Console.WriteLine("Response received : {0}", response);

		client.Shutdown(SocketShutdown.Both);
		client.Close();

	} catch (Exception e) {
		Console.WriteLine(e.ToString());
	}
}
	
private static void ConnectCallback(IAsyncResult ar) {
	try {
		Socket client = (Socket) ar.AsyncState;
		client.EndConnect(ar);

		Console.WriteLine("Socket connected to {0}",
			client.RemoteEndPoint.ToString());
		connectDone.Set();
	} catch (Exception e) {
		Console.WriteLine(e.ToString());
	}
}

Dla klienta wykorzystującego GUI będą wyglądały dużo prościej (pozwolicie, że zastosuję swoje nazwy wklejając tylko kod):

private void uruchom() {
	try {
		Socket gniazdo = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
		gniazdo.BeginConnect(new IPEndPoint(IPAddress.Parse("172.30.12.2"), 8080), new AsyncCallback(this.polaczCallback), gniazdo);
	}
	catch (Exception e) {
		MessageBox.Show(e.ToString(), "Wyjątek: KlientTCP 'void uruchom()'");
	}
}

private void polaczCallback(IAsyncResult rez) {
	try {
		Socket gniazdo = (Socket)rez.AsyncState;
		gniazdo.EndConnect(rez);
		
		//	Funkcje Recieve oraz Send lądują tutaj, kiedy połączenie jest już ustanowione, inaczej niż, w wersji konsolowej,
		//	kiedy system wymusza ResetEventami odczekanie aż asynchroniczne funkcje skończą swoją robotę.
		this.wyslij(gniazdo, "<Connecting/>");
		this.odbierz(gniazdo);
	}
	catch (SocketException e) {
		//	Rób coś...
	}
	catch (Exception e) { 
		//	Rób coś...
	}
}

Może się komuś przyda. Przetestowane i działa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1