Я пытаюсь позволить нескольким клиентам соединяться с хостом с помощью выбора. Я должен буду соединить каждого, сказать им перемещаться в различный порт и затем снова соединяться на новом порте? Или выбор позволит мне подключать несколько клиентов к тому же порту?
Это - клиентский код:
int rv;
int sockfd, numbytes;
if ((rv = getaddrinfo(hostName, hostPort, &hints, &servinfo)) != 0) {
cout << "Could not get server address.\n";
exit(1);
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("Client: no socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("Client: connect");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "Unable to connect to server.\n");
exit(2);
}
FD_SET(sockfd, &masterSet);
Это - серверный код:
int rv = getaddrinfo(NULL, port, &hints, &res);
int yes = 1;//Not sure what this is for, found it in Beej's
if(rv != 0){
cout<< "Error, nothing matches criteria for file descriptor.\n";
exit(1);
}
int fdInit;
for(temp = res; temp != NULL; temp = temp->ai_next){
if((fdInit = socket(temp->ai_family, temp->ai_socktype, temp->ai_protocol)) == -1){
cout << "This is not the fd you're looking for. Move along.\n";
continue; //This is not the fd you're looking for, move along.
}
if(setsockopt(fdInit, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
cout << "Doom has fallen upon this set socket.\n";
perror("setsockopt");
exit(1); //Unable to set socket, exit program with code 1
}
if(bind(fdInit, temp->ai_addr, temp->ai_addrlen) == -1){
cout << "Could not bind fd\n";
close(fdInit);
continue; //Could not bind fd, continue looking for valid fd
}
break; //If a valid fd has been found, stop checking the list
}
if(temp==NULL){
cout<<"Server failed to bind a socket\n";
exit(2);
}
cout << fdInit << endl;
//Setup the file descriptor for initial connections on specified port
freeaddrinfo(res);
FD_SET(fdInit, &masterSet);
Любая справка была бы превосходна!Спасибо.
TCP-соединения идентифицируются по IP-адресу и номеру порта на обоих концах соединения. Поэтому нормально иметь множество клиентов (которым обычно будут назначены номера портов случайным образом) для подключения к одному порту сервера.
Вы создаете сокет и связываете ()
его с портом, на котором слушает ()
, а затем ждете, пока к нему постучат клиенты. Если вы не против блокировки, вы можете просто позвонить по нему напрямую accept ()
, но вы не сможете выполнять цикл тайм-аута или что-то еще.В противном случае вы можете select ()
на прослушивающем сокете, который станет доступным для чтения, когда клиент пытается подключиться, а затем вызвать accept ()
.
accept ()
вернет вновь созданный сокет, который является фактическим сокетом, по которому будет разговаривать с клиентом. Исходный прослушивающий сокет продолжает прослушивать, и на нем могут быть приняты другие соединения.
Обычно используется цикл select ()
для проверки читаемости прослушивающего сокета и любого из подключенных сокетов. Затем, когда select ()
возвращает, вы просто проверяете, доступен ли для чтения сокет прослушивания, и если да, то accept ()
; в противном случае найдите доступный для чтения подключенный сокет и обработайте его.
fd_set fds;
int max = 0, reuse = 1;
struct timeval tv;
int server;
std::vector<int> connected;
// create server listening socket
server = socket(PF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto);
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)); // optional, but recommended
if (bind(server, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
// error, could not bind server socket
}
if (listen(server, 8) < 0) {
// error, could not listen on server port
}
// loop looking for connections / data to handle
while (running) {
FD_ZERO(&fds);
FD_SET(server, &fds);
if (server >= max) max = server + 1;
for (std::vector<int>::iterator it = connected.begin(); it != connected.end(); ++it) {
FD_SET(*it, &fds);
if (*it >= max) max = *it + 1;
}
tv.tv_sec = 2; tv.tv_usec = 0;
if (select(max, &fds, NULL, NULL, &tv) > 0) {
// something is readable
if (FD_ISSET(server, &fds)) {
// it's the listener
connected.push_back(accept(server, (struct sockaddr *)&addr));
}
for (std::vector<int>::iterator it = connected.begin(); it != connected.end(); ++it) {
if (FD_ISSET(*it, &fds)) {
// handle data on this connection
}
}
}
}
Вы должны отметить серверный сокет как таковой ( listen (2)
), но позвонить только по accept (2)
при возврате из выберите (2)
, когда он станет доступным для чтения - это означает, что новый запрос на соединение еще не завершен.
Обратите внимание, что если вы не работаете с неблокирующими сокетами , существует вероятность гонки между select (2)
, возвращающимся и вызывающим ] accept (2)
- подключающийся клиент может отбросить попытку в течение этого времени - так что вы все равно можете заблокировать.
многие клиенты могут подключаться к одному и тому же порту
вы должны сначала прослушать, а затем выбрать
, когда select сообщает вам, что у вас новое соединение, затем примите. Он сообщает вам, что клиент подключился, отметив чтение на вашем сокете fd.