komunikacja UDP, problem z odczytem

0

Mam program:

#include <winsock.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>

//using namespace std;

int main(int argc, int **argv)
{
   WSADATA wsda;		      // informacja zwrocona przez WSAStartup
   struct hostent *host;	// informacja o serwerze
   char szMessage[80];      // komunikat
   int iMessageLen;
   int ret;
   char szAddress[64];
   int iPort;

   SOCKET s;					// uzywane gniazdo
   SOCKADDR_IN addr,remote_addr;// adres hosta
   int iRemoteAddrLen;				// objetosc struktury adresu

   int dudp;
   struct timeval tv;
   fd_set zb;

   tv.tv_sec = 2;
   tv.tv_usec = 500000;

   // pobranie adresu IP
   std::cout << "Podaj adres IP do ktorego wyslac (np. 127.0.0.1) ...";
   std::cin.getline(szAddress,64,'\n');   

   // port na ktorym sie komunikujemy (0...65535)
   iPort = 1000;

   // pobranie komunikatu do bufora
   std::cout << "Wpisz komunikat ...";
   std::cin.getline(szMessage,80,'\n');

   // dlugosc komunikatu
   iMessageLen = strlen(szMessage);

   // zaladuj WinSock w wersji 2.2
   WSAStartup(MAKEWORD(2,2), &wsda);

   // stworzenie gniazda UDP
   s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
   dudp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

   // sprawdzenie bledu
   if(s == SOCKET_ERROR)
   {
      std::cout << "Blad przy inicjalizacji gniazda : " << WSAGetLastError() << std::endl;
      system("PAUSE");      
      exit(1);
   }

   // informacja o hoscie
   addr.sin_family = AF_INET;
   addr.sin_port = htons(iPort);
   addr.sin_addr.s_addr = inet_addr(szAddress);

   if(addr.sin_addr.s_addr == INADDR_NONE) 	// jesli nie podano numeru IP, to odnajdz go z DNS
   {
      std::cout << "Nie podado numeru IP" << std::endl;
      system("PAUSE");      
      exit(1);
   }

   iRemoteAddrLen = sizeof(remote_addr);

   while(1) {
	   // wyslanie komunikatu
	   std::cout << "Wysylanie komunikatu..." << std::endl;
	   ret = sendto(s, szMessage, iMessageLen, 0, (struct sockaddr *) &addr, sizeof(addr));

	   // sprawdzenie poprawnosci wyslania
	   if(ret == SOCKET_ERROR)
	   {
		  std::cout << "Blad przy wysylaniu komunikatu." << std::endl;
		  system("PAUSE");      
		  exit(1);
	   }

	   std::cout << "Komunikat o tresci \"" << szMessage << "\" zostal wyslany do " << szAddress << std::endl;

	   std::cout << "Oczekiwanie na pakiet z zewnatrz " << std::endl;

	   FD_ZERO(&zb);
	   FD_SET(dudp, &zb);

       // nie martw się o writefds i exceptfds:
       ret = select(dudp, &zb, NULL, NULL, &tv );

		if (ret > 0) {
			if (FD_ISSET(dudp, &zb)) {
				ret = recvfrom(s, szMessage, iMessageLen, 0, (struct sockaddr *) &remote_addr, &iRemoteAddrLen);
				std::cout << "Otrzymano: " << szMessage << std::endl;
			}
		}

   }

   // zamkniecie gniazda
   closesocket(s);

   // zamkniecie WinSock
   WSACleanup();
   
   system("PAUSE");

   return 0;
}

I jak uruchamiam go na dwóch komputerach w sieci, wpisując przy tym takie numery IP, aby programy wysylaly do siebie nawzajem komunikaty, bo programy nie pokazuja że odebrały coś od komputera z drugiej strony.

Co mam źle w programie ?

0

Źle masz to, że zawartość remote_addr jest przypadkowa. Musisz tam wstawić numer portu i IP nadawcy, albo lepiej usuń remote_addr, a do recvfrom podaj &addr - to samo co w sendto.
IP nadawcy możesz ustawić na zero jeżeli chcesz odbierać pakiety od dowolnego komputera z sieci (ale na konkretnym porcie), ale wtedy musisz odblokować "broadcast" w sockecie (funkcja setsockopt wywołana zaraz po socket, flagi SOL_SOCKET, SO_BROADCAST, TRUE).

I jeszcze trzy:

  1. po co tam są dwa sockety? Jeden wystarczy, ten sam w sendto i w recvfrom.
  2. wyzeruj sin_zero w SOCKADDR_IN dla świętego spokoju.
  3. nie < code > tylko < cpp >
0
sapero napisał(a)

Źle masz to, że zawartość remote_addr jest przypadkowa. Musisz tam wstawić numer portu i IP nadawcy, albo lepiej usuń remote_addr, a do recvfrom podaj &addr - to samo co w sendto.
IP nadawcy możesz ustawić na zero jeżeli chcesz odbierać pakiety od dowolnego komputera z sieci (ale na konkretnym porcie), ale wtedy musisz odblokować "broadcast" w sockecie (funkcja setsockopt wywołana zaraz po socket, flagi SOL_SOCKET, SO_BROADCAST, TRUE).

I jeszcze trzy:

  1. po co tam są dwa sockety? Jeden wystarczy, ten sam w sendto i w recvfrom.
  2. wyzeruj sin_zero w SOCKADDR_IN dla świętego spokoju.
  3. nie < code > tylko < cpp >

Zmodyfikowałem kod wg. twoich instrukcji, jednak nadal program ten nie odbiera z drugiego komputera danych. Oto kod:

#include <winsock.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>

//using namespace std;

int main(int argc, int **argv)
{
   WSADATA wsda;		      // informacja zwrocona przez WSAStartup
   struct hostent *host;	// informacja o serwerze
   char szMessage[80];      // komunikat
   int iMessageLen;
   int ret;
   char szAddress[64];
   int iPort;

   SOCKET s;					// uzywane gniazdo
   SOCKADDR_IN addr;			// adres hosta
   int iAddr = sizeof(addr);

   int dudp;
   struct timeval tv;
   fd_set zb;

   tv.tv_sec = 2;
   tv.tv_usec = 500000;

   // pobranie adresu IP
   std::cout << "Podaj adres IP do ktorego wyslac (np. 127.0.0.1) ...";
   std::cin.getline(szAddress,64,'\n');   

   // port na ktorym sie komunikujemy (0...65535)
   iPort = 1000;

   // pobranie komunikatu do bufora
   std::cout << "Wpisz komunikat ...";
   std::cin.getline(szMessage,80,'\n');

   // dlugosc komunikatu
   iMessageLen = strlen(szMessage);

   // zaladuj WinSock w wersji 2.2
   WSAStartup(MAKEWORD(2,2), &wsda);

   // stworzenie gniazda UDP
   s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
   dudp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

   // sprawdzenie bledu
   if(s == SOCKET_ERROR)
   {
      std::cout << "Blad przy inicjalizacji gniazda : " << WSAGetLastError() << std::endl;
      system("PAUSE");      
      exit(1);
   }

   // informacja o hoscie
   addr.sin_family = AF_INET;
   addr.sin_port = htons(iPort);
   addr.sin_addr.s_addr = inet_addr(szAddress);
   memset(addr.sin_zero, '\0', sizeof addr.sin_zero);

   if(addr.sin_addr.s_addr == INADDR_NONE) 	// jesli nie podano numeru IP, to odnajdz go z DNS
   {
      std::cout << "Nie podado numeru IP" << std::endl;
      system("PAUSE");      
      exit(1);
   }

   while(1) {
	   // wyslanie komunikatu
	   std::cout << "Wysylanie komunikatu..." << std::endl;
	   ret = sendto(s, szMessage, iMessageLen, 0, (struct sockaddr *) &addr, sizeof(addr));

	   // sprawdzenie poprawnosci wyslania
	   if(ret == SOCKET_ERROR)
	   {
		  std::cout << "Blad przy wysylaniu komunikatu." << std::endl;
		  system("PAUSE");      
		  exit(1);
	   }

	   std::cout << "Komunikat o tresci \"" << szMessage << "\" zostal wyslany do " << szAddress << std::endl;

	   std::cout << "Oczekiwanie na pakiet z zewnatrz " << std::endl;

	   FD_ZERO(&zb);
	   FD_SET(dudp, &zb);

       // nie martw się o writefds i exceptfds:
       ret = select(dudp, &zb, NULL, NULL, &tv );

		if (ret > 0) {
			if (FD_ISSET(dudp, &zb)) {
				ret = recvfrom(s, szMessage, iMessageLen, 0, (struct sockaddr *) &addr, &iAddr);
				std::cout << "Otrzymano: " << szMessage << std::endl;
			}
		}

   }

   // zamkniecie gniazda
   closesocket(s);

   // zamkniecie WinSock
   WSACleanup();
   
   system("PAUSE");

   return 0;
}
0

Nadal używasz dwóch socketów, ten drugi jest w stanie surowym a oczekujesz że coś odbierze.
Masz tu przykład nie wymagający wpisywania IP.

#include "stdafx.h"
#undef WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <conio.h>
#pragma comment(lib,"ws2_32.lib")

BOOL IsNotMyIP(SOCKET s, u_long ip)
{
	char buff[256];
	DWORD bytes;
	WSAIoctl(s, SIO_ADDRESS_LIST_QUERY, 0, 0, (LPVOID)buff, sizeof(buff), &bytes, 0, 0);

	SOCKET_ADDRESS_LIST *list = (SOCKET_ADDRESS_LIST*)buff;
	for (int a=0; a<list->iAddressCount; a++)
	{
		sockaddr_in *psa = (sockaddr_in*)list->Address[a].lpSockaddr;
		if (ip == psa->sin_addr.S_un.S_addr) return FALSE;
	}
	return TRUE;
}

int main(int argc, char* argv[])
{
	WSADATA wsda;
	WSAStartup(MAKEWORD(2,2), &wsda);

	SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	// enable broadcast
	BOOL fEnabled = TRUE;
	setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&fEnabled, sizeof(fEnabled));

	// bind
	SOCKADDR_IN addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(1000); // port
	bind(s, (sockaddr*)&addr, sizeof(addr));

	addr.sin_addr.s_addr = 0xFFFFFFFF;
	timeval tv;

	// uformatuj CQ
	char tx[256];
	strcpy(tx, "CQ DX this is ");
	int txlen = strlen(tx);
	DWORD cch = sizeof(tx) - 1 - txlen;
	GetUserNameA(&tx[txlen], &cch);
	txlen = strlen(tx);

	while (!_kbhit())
	{
		Beep(600,20);
		int ret = sendto(s, tx, txlen, 0, (sockaddr*)&addr, sizeof(addr));

		//
		fd_set zb;
		zb.fd_count = 1;
		zb.fd_array[0] = s;
		tv.tv_sec = 2;
		tv.tv_usec = 0;
		while (select(0, &zb, NULL, NULL, &tv) == 1)
		{
			char rx[256];
			SOCKADDR_IN sender;
			int senderlen = sizeof(sender);

			int rxlen = recvfrom(s, rx, sizeof(rx)-1, 0, (sockaddr*)&sender, &senderlen);

			if (rxlen > 0)
			{
				if (IsNotMyIP(s, sender.sin_addr.s_addr))
				{
					rx[rxlen]=0;
					printf("%s: %s\n", inet_ntoa(sender.sin_addr), rx);
				}
			}
			else
			{
				break;
			}
		}
	}
	closesocket(s);
	WSACleanup();
	return 0;
}

W takiej konfiguracji z broadcastowym socketem, socket odbiera to co sam wyśle, dlatego potrzebna była dodatkowa funkcja IsNotMyIP. Pisałem to w c++, więc pewnie będziesz musiał sobie poprzestawiać wszystkie zmienne.
Program działa mniejwięcej tak: w pętli wysyła "ogólne gdzie mnie słychać" i wypluwa na stdout to co sam odbierze. Dowolny klawisz przerywa pętlę.

0

zaraz zanalizuje dokładnie, ale nie chodzi mi o broadcast. Mam z kuplem do napisania gre klient serwer. I zalozenie jest takie, że serwer ma odbierać informacje od klienta i przekazywać ją dalej, a klient ma odbierać i wysyłać informacje (z i do serwera). Powyższy kod to klient.

0

Przeanalizowałem twój kod sapero i troche go skróciłem przerobiłem aby był bardziej dla mnie przydatny (jednak mam nadzieje, nie usunołem zbędnego kodu, który uniemożliwia komunikację komputerów) jednak program dalej nie działa prawidłowo.

Kod programu:

#include <winsock.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>

//using namespace std;

int main(int argc, int **argv)
{
	WSADATA wsda;		      // informacja zwrocona przez WSAStartup

	//załadowanie WinSock w wersji 2.2
	WSAStartup(MAKEWORD(2,2), &wsda);

	//stworzenie gniazda UDP
	SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	//pobranie danych ze standardowego wejścia
	char hostAddress[64];
	int hostPort;
	std::cout << "Podaj adres IP: ";
	std::cin >> hostAddress;
	std::cout << "Podaj numer portu: ";
	std::cin >> hostPort;

	//informacje o hoscie
	SOCKADDR_IN addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(hostPort);
	addr.sin_addr.s_addr = inet_addr(hostAddress);

	//bind
	bind(s, (sockaddr*) &addr, sizeof(addr));

	//ustawienie czasu czekania na odbior wiadomosci
	timeval tv;
	tv.tv_sec=2;
	tv.tv_usec=0;

	while(1) {
		int ret = sendto(s, hostAddress, strlen(hostAddress), 0, (sockaddr*) &addr, sizeof(addr));

		fd_set zb;
		zb.fd_count = 1;
		zb.fd_array[0] = s;
		while(select(0, &zb, NULL, NULL, &tv) == 1) {
			char rx[64];
			SOCKADDR_IN sender;
			int senderLen = sizeof(sender);

			int rxlen = recvfrom(s, rx, sizeof(rx)-1, 0, (sockaddr*) &sender, &senderLen);

			if(rxlen > 0) {
				std::cout << "Otrzymano wiadomosc: " << rx << std::endl;
			} else {
				break;
			}
		}
	}

   // zamkniecie gniazda
   closesocket(s);

   // zamkniecie WinSock
   WSACleanup();

   return 0;
}

Na jednym komputerze wpisuje:
192.168.0.1
5000

na drugim:
192.168.0.3
5000

i po wpisaniu portu komunikacji program wchodzi w whila i nic nie pokazuje na ekranie.

PS. mój kod też napisany jest w C++ (ten wcześniejszy).

0

W sockaddr dla funkcji bind msi być podane IP jednej z Twoich kart sieciowych, localhost lub 0, inaczej bind nie zadziała i kto wie czy cokolwiek dalej zadziała (recvfrom na pewno nie).
W moim przykładzie zrobiłem memset dla całego sockaddr, wpisałem port, AF_INET i zaraz zrobiłem bind (s_addr wtedy było zerowe).

Co powinieneć zrobić:

  1. przesuń bind() przed linijkę gdzie ustawiasz s_addr.
  2. Zamknij odebrany ciąg znaków NULL'em by nie wypisywać krzaków ze stosu.
if(rxlen > 0) {
    rx[rxlen] = 0;
    cout
0

sapero, wielkie dzięki, gdyby nie ty nie poradziłbym sobie z tym problemem. Pojawiła się natomiast inna anomalia (przedstawie charakterystyke mojej sieci poniżej)

192.168.0.1:
komputer stacjonarny, który ma dodatkową kartę sieciową przez którą jest połączenie z internetem

192.168.0.3:
laptop, który ma ustawioną brame na 192.168.0.1.

I teraz jak uruchamiam programy, żeby nawzajem do siebie nadawały to pojawia się coś takiego, że komputer stacjonarny w chwili gdy odbierze (tzn wypisze na ekranie dwie wiadomosci) to laptop w tym czasie wypisze ich z około 2-3 razy więcej. Zupełnie nie wiem czemu tak się dzieje.

0

Nie mam jak tego sprawdzić, ale mogę poradzić co możesz zrobić.
Użyj tylko jednego komputera. Dodaj dowolne IP w opcjach sieciówki np 10.10.0.1/255.255.255.0.
Będziesz miał 2 adresy IP: 192.168.0.1 i 10.10.0.1.
W programie przed bind() wstaw

// zbinduj na pierwszym IP
addr.sin_addr.s_addr = inet_addr("192.168.0.1");

if (bind(s, (sockaddr*) &addr, sizeof(addr)))
{
	// ip:port zajęty, binduj na drugin IP
	addr.sin_addr.s_addr = inet_addr("10.10.0.1");

	bind(s, (sockaddr*) &addr, sizeof(addr));
	cout "moj ip: 10.10.0.1\n";
	// adres docelowy już znamy
	addr.sin_addr.s_addr = inet_addr("192.168.0.1");
}
else
{
	cout "moj ip: 192.168.0.1\n";
	// adres docelowy już znamy
	addr.sin_addr.s_addr = inet_addr("10.10.0.1");
}

Odpal dwie instancje programu, oba powinny dostać różne ip dla socketa-serwera.

0

po konsultacji z kolegami, doszliśmy do wniosku, że najlepiej zrobić komunikacje klient-serwer na kilku wątkach. No i napisałem klienta, który działa ale nie do końca. A oto jak działa

  • na komputerze 1. włączam serwer

  • wpisuje start (uruchamiane są 2 wątki, jeden do wysyłania, drugi do odczytywania)

  • włączam również klienta

  • wpisuje start, (enter), 127.0.0.1, (enter)

  • klient komunikuje się z serwerem bez problemu

  • na komputerze 2. włączam klienta

  • wpisuje start, (enter), 192.160.0.1, (enter)

  • klient nawiazuje łączność z serwerem (wysyła mu dane), ale nie może nawiązać połączenia odbierającego i wypisuje "Blad przy wiazaniu gniazda odbierajacego : 10049"

Poniżej zamieszczam kod klienta:

#include <winsock.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>

HANDLE threadHandle[2];

std::string klient_tab[4];	//tabela klientow
char host[20];

DWORD WINAPI ClientRecv( void *arg ) {
	WSADATA wsda;			// informacja zwrocona przez WSAStartup
	char szMessage[512];	// bufor na komunikat
	int iMessageLen;		// długość odebranej wiadomości
	int port=1000;
	std::string tmp1, tmp2;

	SOCKET s;
	SOCKADDR_IN addr;
	int iAddrLen;

	//załadowanie WinSock w wersji 2.2
	WSAStartup(MAKEWORD(2,2), &wsda);

	//stworzenie gniazda UDP
	s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	// sprawdzenie bledu
	if(s == SOCKET_ERROR) {
		std::cout << "Blad przy inicjalizacji gniazda odbierajacego : " << WSAGetLastError() <<"\n";
		return 1;
	}

	// informacja o interfejsie
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(host);

	if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
		std::cout << "Blad przy wiazaniu gniazda odbierajacego : " << WSAGetLastError() <<"\n";
		return 1;
	}

	iAddrLen = sizeof(addr);
	while(1) {
		iMessageLen = recvfrom(s, szMessage, sizeof(szMessage)-1, 0, (sockaddr*) &addr, &iAddrLen);

		if(iMessageLen > 0) {
			szMessage[iMessageLen] = 0;

			tmp1=inet_ntoa(addr.sin_addr);
			tmp2=szMessage;

			/*Start przetwarzania komunikatu*/

			std::cout << "Otrzymano wiadomosc: " << szMessage << std::endl;

			/*Stop przetwarzania komunikatu*/
		} else {
			//break;
		}
	}

	// zamkniecie gniazda
	closesocket(s);
	// zamkniecie WinSock
	WSACleanup();

	return 0;
}

DWORD WINAPI ClientSend( void *arg ) {
	WSADATA wsda;			// informacja zwrocona przez WSAStartup
	char szMessage[512];	// bufor na komunikat
	int iMessageLen;		// długość odebranej wiadomości
	int ret;
	int port=1001;
	std::string tmp1, tmp2;

	SOCKET s;
	SOCKADDR_IN addr;
	//int iAddrLen;

	//załadowanie WinSock w wersji 2.2
	WSAStartup(MAKEWORD(2,2), &wsda);

	//stworzenie gniazda UDP
	s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	// sprawdzenie bledu
	if(s == SOCKET_ERROR) {
		std::cout << "Blad przy inicjalizacji gniazda nadajacego : " << WSAGetLastError() <<"\n";
		return 1;
	}
	// informacja o interfejsie
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(host);

	while(1) {
		strcpy_s(szMessage, "Rejestruj");
		iMessageLen = strlen(szMessage);

		ret = sendto(s, szMessage, iMessageLen, 0, (sockaddr*) &addr, sizeof(addr));

		Sleep(25);

		strcpy_s(szMessage, "Start");
		iMessageLen = strlen(szMessage);

		ret = sendto(s, szMessage, iMessageLen, 0, (sockaddr*) &addr, sizeof(addr));

		Sleep(25);
		break;
	}

	// zamkniecie gniazda
	closesocket(s);
	// zamkniecie WinSock
	WSACleanup();

	return 0;
}

int main(int argc, int **argv)
{
	char command[10];
	while(1) {
		std::cin >> command;

		if(strcmp(command, "start") == 0) {
			std::cin >> host;

			//rozpoczęcie dzialania serwera
			//watek wysylania
			threadHandle[0] = CreateThread(
								NULL,           // atrybuty bezpieczeństwa
								0,              // inicjalna wielkość stosu
								ClientSend,		// funkcja wątku
								NULL,			// dane dla funkcji wątku
								0,              // flagi utworzenia
								NULL );

			//watek odbierania
			threadHandle[1] = CreateThread(
								NULL,           // atrybuty bezpieczeństwa
								0,              // inicjalna wielkość stosu
								ClientRecv,		// funkcja wątku
								NULL,			// dane dla funkcji wątku
								0,              // flagi utworzenia
								NULL );

		} else if(strcmp(command, "stop") == 0) {
			//zakonczenie dzialania serwera
			CloseHandle(threadHandle[0]);
			CloseHandle(threadHandle[1]);

		} else if(strcmp(command, "exit") == 0) {
			break;
		}
	}

	//system("PAUSE");
	return 0;
}

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