[Sockets] Conexiones múltiples de clientes a servidor.

Iniciado por Zodiak98, 22 Enero 2016, 23:48 PM

0 Miembros y 1 Visitante están viendo este tema.

Zodiak98

Verán, estoy aprendiendo sobre sockets y hasta ahora todo bien. El problema es que sólo puedo manejar una conexión a la vez, ¿cómo podría manejar conexiones múltiples? Leí un tema que se logra a través de un Thread, pero estoy un poco confundido.

Estos son los códigos que llevo hasta ahora:

Servidor:
Código (cpp) [Seleccionar]

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <IPHlpApi.h>
#include <stdio.h>
#include <stdlib.h>

#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_PORT "27016"
#define DEFAULT_BUFLEN 516

int main() {
WSADATA wsaData;
int iResult;

char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
int recvBytes;

iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}

struct addrinfo *result = NULL,
hints;

ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;

iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo error: %d\n", iResult);
WSACleanup();
return 1;
}

SOCKET ListenSocket = INVALID_SOCKET;

ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}

iResult = bind(ListenSocket, result->ai_addr, result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("Error at bind: %d\n", iResult);
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}

freeaddrinfo(result);

if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
printf("Listen failed with: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}

SOCKET ClientSocket = INVALID_SOCKET;

ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}

printf("A CONNECTION HAS BEEN FOUND!!\n");

iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", iResult);
closesocket(ClientSocket);
WSACleanup();
return 1;
}

do {
recvBytes = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (recvBytes > 0) {
printf("Client >> ");
for (int i = 0; i < recvBytes; ++i) {
printf("%c", recvbuf[i]);
}
//printf("Bytes received: %d\n\n", recvBytes);
}
else if (recvBytes == 0) {
printf("Connection closing...\n");
}
else {
printf("recv failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (recvBytes > 0);

closesocket(ClientSocket);
WSACleanup();

system("pause>nul");

return 0;
}


Cliente:
Código (cpp) [Seleccionar]

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <IPHlpApi.h>
#include <stdio.h>
#include <stdlib.h>

#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_PORT "27016"
#define DEFAULT_BUFLEN 516

int main() {
WSADATA wsaData;
int iResult;

char sendbuf[DEFAULT_BUFLEN];
int iSendResult;

iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}

struct addrinfo *result = NULL,
*ptr = NULL,
hints;

ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed with error: %d/n", iResult);
WSACleanup();
return 1;
}

SOCKET ConnectSocket = INVALID_SOCKET;

ptr = result;

ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}

iResult = connect(ConnectSocket, ptr->ai_addr, ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}

freeaddrinfo(result);

if (ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}

do {
printf("Send a message to the server: ");
fgets(sendbuf, DEFAULT_BUFLEN, stdin);

iSendResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iSendResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
//printf("Bytes sent: %d\n\n", iSendResult);
} while (iSendResult > 0);

iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", iResult);
closesocket(ConnectSocket);
WSACleanup();
return 1;
}

closesocket(ConnectSocket);
WSACleanup();

system("pause>nul");

return 0;
}


Estoy trabajando en Visual Studio Community 2015.

sodark

No me he leido tu codigo pero te comento lo que se suele hacer en todos los lenguajes

- Inicias un servidor que escuche un puerto.
- Esperas a recibir un Accept (esto te devolvera un filedescriptor en C que sera el socket abierto, o un Socket en Java)
- Pasas ese filedescriptor / socket a un thread, donde sera atendido

Para que te hagas una idea en C tengo algo asi



while( 1 == 1 ) {
client_socket = accept(server_socket, (struct sockaddr *) &client, &client_length);
if (client_socket < 0) {
close(server_socket);
close(client_socket);
} else {
run_dozer_thread(client_socket);
}
}


Donde run dozer thread


/**********************************************
* @Nombre: run_dozer_thread
* @Def: Funcion encargada lanzar el Thread del Dozer
* @Arg: int socket_cliente
* @Ret:   void
**********************************************/
void run_dozer_thread(int client_socket) {
pthread_t thread_dozer_id;
pthread_create(&thread_dozer_id, NULL, thread_manage_dozer, (void *)client_socket);
}

mester

Crear un thread para cada cliente es una faena y si tienes un ordenador poco potente es ineficiente. Yo te recomendaría usar la función select(); https://msdn.microsoft.com/en-us/library/windows/desktop/ms740141%28v=vs.85%29.aspx
Con esta función sabes si ha habido alguna novedad o notificación en un socket, es decir, si se ha recibido algo. Los sockets se almacenan en un array en la estructura https://msdn.microsoft.com/en-us/library/windows/desktop/ms737873%28v=vs.85%29.aspx
El programa seguiría el hilo logico siguiente:

Si select() recibe una notificación del SOCKET a la escucha
  Acepta conexión del nuevo cliente
  Si el cliente se ha conectado correctamente añade el socket a fd_set
Si no, si recibe una notificación del algun socket que no sea el de escucha
  Lee el descriptor que ha enviado el mensaje
  Envia el mismo mensaje al resto de sockets excepto al que lo envía y al socket servidor

Mas o menos ese sería el esquema que debe seguir el bucle.
Justicia es dar a cada uno lo que se merece

kondrag_X1

http://www.cs.dartmouth.edu/~campbell/cs50/socketprogramming.html en el link explica lo básico de los socket como puedes manejar peticiones esta muy bien con sus diagramas.

Pero si quieres controlar varios socket a la vez yo creo que tendrás que usar select
http://www.chuidiang.com/clinux/sockets/socketselect.php

sodark

Ya y si tengo que realizar operaciones al mismo tiempo porque no paran de enviar peticiones? (Obviamente no me refiero para este ejercicio)

mester

Cita de: sodark en 23 Enero 2016, 21:15 PM
Ya y si tengo que realizar operaciones al mismo tiempo porque no paran de enviar peticiones? (Obviamente no me refiero para este ejercicio)


Puedes usar threads o crear procesos hijos con fork(); sino puedes hacer bien el programa con varias condiciones y tal. Pero si quieres hacer las cosas así, pues threads.
Justicia es dar a cada uno lo que se merece

sodark

Cita de: mester en 24 Enero 2016, 18:03 PM
Puedes usar threads o crear procesos hijos con fork(); sino puedes hacer bien el programa con varias condiciones y tal. Pero si quieres hacer las cosas así, pues threads.

La verdad es que a fork no le veo sentido.

- No comparten memoria (para comunicarse se han de crear las pipes y una rutina de gestion de comunicacion)
- Se duplica todo el espacio de memoria, toda variable, por lo que si se conectan 10 clientes tengo 10 x consumo de memoria.

Con los threads te ahorras la rutina de gestion de comunicación entre padre e hijo, tan solo debes poner unos mutex en las zonas criticas. El gasto de memoria es el "mismo" (obviamente un par de variables mas pero no todo duplicado).

Lo que me parecería interesante es que explicaras como hacer un multiservidor que atienda las peticiones de multiples clientes (llegando a la vez y realizando operaciones que puedan llevar mas de 1 segundo en dar resultado al cliente) sin tener que hacer esperar a los clientes ya que es un solo hilo de ejecució.

Mas que nada porque a mi no se me ocurre y así aprendería algo nuevo :)