Socket Linux zwielokrotnione we/wy

0

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

select

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

Kod serwera:

 #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

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.

0
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:

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...

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.

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.

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.

0
if((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { ... } //<--- protokół zapomniałeś ustawić

Pokaż jak wygląda kod po poprawkach.

0

kod źródłowy serwera:

#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:

#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:

  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

  if(i == sockfd) {

nie zwróciło nigdy true.

1
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.

  //fdmax = max(new_sockfd, fdmax);

To musi być odkomentowane.

W kliencie też ustaw flagę IPPROTO_TCP.

0

jeszcze jedno pytanie jaka biblioteka do

  //fdmax = max(new_sockfd, fdmax); 

ponieważ dostaję undefined reference to `max'

1
#define max(x, y) (((x) > (y)) ? (x) : (y))
0

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

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