Socket Linux zwielokrotnione we/wy

Socket Linux zwielokrotnione we/wy
antoniaklja
  • Rejestracja:ponad 14 lat
  • Ostatnio:około 9 lat
  • Postów:88
0

Witam.
Pisze sobie najprostszy serwer pod linuxem obsługujący kilku klientów naraz. Zastosowałem funkcję

Kopiuj
select

Niestety pomimo wszystkiego nie działa mi, chociaż wygląda poprawnie.

Kod serwera:

Kopiuj
 #include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

#define BACKLOG 5
#define MY_PORT 5000
#define IP "192.168.8.21"


int main() {
	int sockfd, new_sockfd;
	struct sockaddr_in my_addr;
	struct sockaddr_in their_addr;
	struct timeval tv;
	fd_set read_fds;
	fd_set write_fds;
	fd_set master_fds;
	int bytes;
	char buffor[100];
	int yes = 1;
	socklen_t sin_size;
	int fdmax;
	int i;
	int status;

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");	
		exit(1);
	}

	if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) == -1) {
		perror("setsockopt");
		exit(1);
	}

	FD_ZERO(&read_fds);
	FD_ZERO(&write_fds);
	FD_ZERO(&master_fds);
	//FD_ZERO(&status);

	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(MY_PORT);		
	my_addr.sin_addr.s_addr = inet_addr(IP);
	memset(&(my_addr.sin_zero), '\0', 8);

	if((bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))) == -1) {
		perror("bind");
		exit(1);
	}

	if((listen(sockfd, BACKLOG)) == -1) {
		perror("listen");
		exit(1);
	}		

	FD_SET(sockfd, &read_fds);	
	fdmax = sockfd;

	while(1) {
		/*
		   sin_size = sizeof(struct sockaddr);
		   if((new_sockfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
		   perror("accept");
		   exit(1);
		   }
		   if((bytes = recv(new_sockfd, &buffor, sizeof(buffor), 0)) == -1) {
		   perror("recv");
		   exit(1);
		   }
		   printf("connection from: %s, recived: %s\n", inet_ntoa(their_addr.sin_addr), buffor);
		 */
		

		read_fds = master_fds;
		if((select(fdmax+1, &read_fds, &write_fds, NULL, NULL)) == -1) {
			perror("select");
			exit(1);
		}

		for(i=0; i<fdmax; i++) {
			if(FD_ISSET(i, &read_fds)) {
				if(i==sockfd) {
					sin_size = sizeof(struct sockaddr);
					if((new_sockfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
						perror("accept");
						exit(1);
					}
						printf("server got connection from: %s, socket: %d\n", inet_ntoa(their_addr.sin_addr), new_sockfd);
						FD_SET(status, &master_fds);
				} else {
					if((bytes = recv(new_sockfd, &buffor, sizeof(buffor), 0)) == -1) {
						perror("recv");
						exit(1);
					}
				printf("connection from: %s, recived: %s\n", inet_ntoa(their_addr.sin_addr), buffor);

				}	
			}
		}
	}

	system("pause");
	close(sockfd);
	close(new_sockfd);
	return 0;
}

aby sprawdzić czy działa wystarczy połączyć się za pomocą telnetu na port 5000.
Wedle moich poszukiwań staje w miejscu

Kopiuj
if(i==sockfd) { ...

Nie mam pojęcia jak rozwiązać ten problem, spędziłem nad nim prawie cały dzisiejszy dzień.
Nie do końca rozumiem koncepcje funkcji select.

edytowany 1x, ostatnio: antoniaklja
06
  • Rejestracja:prawie 20 lat
  • Ostatnio:ponad rok
  • Postów:2440
0
Kopiuj
fdmax = sockfd;

while(1) 
{
	read_fds = master_fds;

	if((select(fdmax + 1, &read_fds, &write_fds, NULL, NULL)) == -1) { ... }

	for(i = 0; i <= fdmax; i++) //<--- warunek miałeś zły
	{
		if(FD_ISSET(i, &read_fds)) 
		{
			if(i == sockfd) 
			{
				sin_size = sizeof(struct sockaddr);
				
				if((new_sockfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) { ... }
				
				FD_SET(new_sockfd, &master_fds);
				fdmax = max(new_sockfd, fdmax); //<---  przy każdym nowym sockecie musisz zwiększać fdmax
			} 
			else 
			{
				if((bytes = recv(i, //<--- tu, nie wiedzieć czemu, miałeś 'new_sockfd'
						buffor, 
						sizeof(buffor), 
						0)) == -1) 
				{ ... }
				
			}        
		}
	}
}

--- dodane ---

Swoją drogą jestem ciekaw, na ile taka konstrukcja pętli jest przenośna:

Kopiuj
fd_set read_fds;

...
	
for(size_t i = 0; i < read_fds.fd_count; ++i)
{
	if(read_fds.fd_array[i] == sockfd)
	{
		...
	}
	else
	{
		...
	}
}

bo to kręcenie pętlą na podstawie wartości deskryptorów niezbyt dobrze mi wygląda...

edytowany 2x, ostatnio: _0x666_
antoniaklja
  • Rejestracja:ponad 14 lat
  • Ostatnio:około 9 lat
  • Postów:88
0

widzę, że chyba zastosowałeś tutaj c++ ja piszę to w czystym c.
Po Twoich poprawkach nie działa nadal.
Ps.sprawdzanie po deskryptorach plików w pętli stosują wszyscy z tego co wyczytałem w internecie oraz ksiązkach.

06
  • Rejestracja:prawie 20 lat
  • Ostatnio:ponad rok
  • Postów:2440
0

Po Twoich poprawkach nie działa nadal.

W sumie to nie wiadomo, co oznacza to "nie działa". Poprawiłem błędy, które rzuciły mi się w oczy.

Ps.sprawdzanie po deskryptorach plików w pętli stosują wszyscy (...)

No właśnie, i tu jest problem. W windowsie na przykład deskryptory mogą przybierać duże wartości, zatem takie kręcenie pętlą od zera do np. 4000 tylko po to, żeby sprawdzić na przykład jeden socket jest nieefektywne i nieracjonalne, zważywszy, że struktura read_fds zawiera gotową listę socketów, które spełniają warunek (tak na dobrą sprawę użycie FD_ISSET w kodzie, który podałem też jest bez sensu - poprawiłem). A to, że ta metoda występuje w wielu internetowych publikacjach... no cóż, pokaż mi publikację, która wytłumaczyłaby, dlaczego mam robić to tak a nie inaczej.

antoniaklja
  • Rejestracja:ponad 14 lat
  • Ostatnio:około 9 lat
  • Postów:88
0

"nie działa" - chodzi mi o to że serwer nie wykrywa nowego połączenia oraz nie wyświetla bufora.
napisałem prostego klienta wysyłającego wiadomość do sprawdzenia czy wszystko jest ok, który wysyła(send) wiadomość do serwera, lecz serwer tego nie odczytuje.
Przy zwykłym strumieniowym, jedno-połączeniowym działa bez problemu.

edytowany 1x, ostatnio: antoniaklja
06
  • Rejestracja:prawie 20 lat
  • Ostatnio:ponad rok
  • Postów:2440
0
Kopiuj
if((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { ... } //<--- protokół zapomniałeś ustawić

Pokaż jak wygląda kod po poprawkach.

antoniaklja
  • Rejestracja:ponad 14 lat
  • Ostatnio:około 9 lat
  • Postów:88
0

kod źródłowy serwera:

Kopiuj
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

#define BACKLOG 5
#define MY_PORT 5000
#define IP "192.168.8.21"


int main() {
	int sockfd, new_sockfd;
	struct sockaddr_in my_addr;
	struct sockaddr_in their_addr;
	struct timeval tv;
	fd_set read_fds;
	fd_set write_fds;
	fd_set master_fds;
	int bytes;
	char buffor[100];
	int yes = 1;
	socklen_t sin_size;
	int fdmax;
	int i;	
	int status;

	if((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
		perror("socket");	
		exit(1);
	}

	if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) == -1) {
		perror("setsockopt");
		exit(1);
	}

	FD_ZERO(&read_fds);
	FD_ZERO(&write_fds);
	FD_ZERO(&master_fds);
	//FD_ZERO(&status);

	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(MY_PORT);		
	my_addr.sin_addr.s_addr = inet_addr(IP);
	memset(&(my_addr.sin_zero), '\0', 8);

	if((bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))) == -1) {
		perror("bind");
		exit(1);
	}

	if((listen(sockfd, BACKLOG)) == -1) {
		perror("listen");
		exit(1);
	}		

	FD_SET(sockfd, &read_fds);	
	fdmax = sockfd;

	while(1) {
		/*
		   sin_size = sizeof(struct sockaddr);
		   if((new_sockfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
		   perror("accept");
		   exit(1);
		   }
		   if((bytes = recv(new_sockfd, &buffor, sizeof(buffor), 0)) == -1) {
		   perror("recv");
		   exit(1);
		   }
		   printf("connection from: %s, recived: %s\n", inet_ntoa(their_addr.sin_addr), buffor);
		 */
		

		read_fds = master_fds;
		if((select(fdmax+1, &read_fds, &write_fds, NULL, NULL)) == -1) {
			perror("select");
			exit(1);
		}

		for(i = 0; i <= fdmax; ++i) {
			if(FD_ISSET(i, &read_fds)) {
				if(i == sockfd) {
				puts("2");
					sin_size = sizeof(struct sockaddr);
					if((new_sockfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
						perror("accept");
						exit(1);
					}
						printf("server got connection from: %s, socket: %d\n", inet_ntoa(their_addr.sin_addr), new_sockfd);
						FD_SET(new_sockfd, &master_fds);
						//fdmax = max(new_sockfd, fdmax);
				} else {
					if((bytes = recv(i, &buffor, sizeof(buffor), 0)) == -1) {
						perror("recv");
						exit(1);
					}
				printf("connection from: %s, recived: %s\n", inet_ntoa(their_addr.sin_addr), buffor);

				}	
			}
		}
	}

	system("pause");
	close(sockfd);
	close(new_sockfd);
	return 0;
} 

kod źródłowy klienta:

Kopiuj
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define MY_PORT 5000
#define MAX_DATA_SIZE 100

int main() {
	int sockfd, bytes;
	struct sockaddr_in dest_addr;	
	char buffor[100] = "MESSAGE\n";

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		perror("socket");

	dest_addr.sin_family = AF_INET;
	dest_addr.sin_port = htons(MY_PORT);	
	dest_addr.sin_addr.s_addr = inet_addr("192.168.8.21");
	memset(&(dest_addr.sin_zero), '\0', 8);

	if((connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr))) == -1)
		perror("connect");


	if((bytes = send(sockfd, &buffor, MAX_DATA_SIZE-1, 0)) == -1)
		perror("send"); 

	close(sockfd);
	return 0;
}

 

nie wykonuje się kod:

Kopiuj
  if(i == sockfd) {
                                puts("2");
                                        sin_size = sizeof(struct sockaddr);
                                        if((new_sockfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
                                                perror("accept");
                                                exit(1);
                                        }
                                                printf("server got connection from: %s, socket: %d\n", inet_ntoa(their_addr.sin_addr), new_sockfd);
                                                FD_SET(new_sockfd, &master_fds);
                                                //fdmax = max(new_sockfd, fdmax);

tak jakby sprawdzenie

Kopiuj
  if(i == sockfd) {

nie zwróciło nigdy true.

06
  • Rejestracja:prawie 20 lat
  • Ostatnio:ponad rok
  • Postów:2440
1
Kopiuj
FD_SET(sockfd, &read_fds);  // <---- !!!

fdmax = sockfd;

while(1) {

	read_fds = master_fds; // <--- !!!
	
	...
}

W sumie przeoczyłem to, a to główny błąd :/ Zamiast ustawiać read_fds, ustaw master_fds.

Kopiuj
  //fdmax = max(new_sockfd, fdmax);

To musi być odkomentowane.

W kliencie też ustaw flagę IPPROTO_TCP.

antoniaklja
  • Rejestracja:ponad 14 lat
  • Ostatnio:około 9 lat
  • Postów:88
0

jeszcze jedno pytanie jaka biblioteka do

Kopiuj
  //fdmax = max(new_sockfd, fdmax); 

ponieważ dostaję undefined reference to `max'

Kopiuj
06
  • Rejestracja:prawie 20 lat
  • Ostatnio:ponad rok
  • Postów:2440
1
Kopiuj
#define max(x, y) (((x) > (y)) ? (x) : (y))
antoniaklja
  • Rejestracja:ponad 14 lat
  • Ostatnio:około 9 lat
  • Postów:88
0

dziękuje bardzo za pomoc :) wszystko działa jak należy, teraz mogę już go trochę rozbudować.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.