Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: Zodiak98 en 22 Enero 2016, 23:48 pm



Título: [Sockets] Conexiones múltiples de clientes a servidor.
Publicado por: Zodiak98 en 22 Enero 2016, 23:48 pm
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
  1. #ifndef WIN32_LEAN_AND_MEAN
  2. #define WIN32_LEAN_AND_MEAN
  3. #endif
  4.  
  5. #include <windows.h>
  6. #include <WinSock2.h>
  7. #include <WS2tcpip.h>
  8. #include <IPHlpApi.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11.  
  12. #pragma comment(lib, "Ws2_32.lib")
  13.  
  14. #define DEFAULT_PORT "27016"
  15. #define DEFAULT_BUFLEN 516
  16.  
  17. int main() {
  18. WSADATA wsaData;
  19. int iResult;
  20.  
  21. char recvbuf[DEFAULT_BUFLEN];
  22. int recvbuflen = DEFAULT_BUFLEN;
  23. int recvBytes;
  24.  
  25. iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
  26. if (iResult != 0) {
  27. printf("WSAStartup failed with error: %d\n", iResult);
  28. return 1;
  29. }
  30.  
  31. struct addrinfo *result = NULL,
  32. hints;
  33.  
  34. ZeroMemory(&hints, sizeof(hints));
  35. hints.ai_family = AF_INET;
  36. hints.ai_socktype = SOCK_STREAM;
  37. hints.ai_protocol = IPPROTO_TCP;
  38. hints.ai_flags = AI_PASSIVE;
  39.  
  40. iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
  41. if (iResult != 0) {
  42. printf("getaddrinfo error: %d\n", iResult);
  43. WSACleanup();
  44. return 1;
  45. }
  46.  
  47. SOCKET ListenSocket = INVALID_SOCKET;
  48.  
  49. ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
  50.  
  51. if (ListenSocket == INVALID_SOCKET) {
  52. printf("Error at socket(): %ld\n", WSAGetLastError());
  53. freeaddrinfo(result);
  54. WSACleanup();
  55. return 1;
  56. }
  57.  
  58. iResult = bind(ListenSocket, result->ai_addr, result->ai_addrlen);
  59. if (iResult == SOCKET_ERROR) {
  60. printf("Error at bind: %d\n", iResult);
  61. freeaddrinfo(result);
  62. closesocket(ListenSocket);
  63. WSACleanup();
  64. return 1;
  65. }
  66.  
  67. freeaddrinfo(result);
  68.  
  69. if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
  70. printf("Listen failed with: %ld\n", WSAGetLastError());
  71. closesocket(ListenSocket);
  72. WSACleanup();
  73. return 1;
  74. }
  75.  
  76. SOCKET ClientSocket = INVALID_SOCKET;
  77.  
  78. ClientSocket = accept(ListenSocket, NULL, NULL);
  79. if (ClientSocket == INVALID_SOCKET) {
  80. printf("accept failed with error: %d\n", WSAGetLastError());
  81. closesocket(ListenSocket);
  82. WSACleanup();
  83. return 1;
  84. }
  85.  
  86. printf("A CONNECTION HAS BEEN FOUND!!\n");
  87.  
  88. iResult = shutdown(ClientSocket, SD_SEND);
  89. if (iResult == SOCKET_ERROR) {
  90. printf("shutdown failed with error: %d\n", iResult);
  91. closesocket(ClientSocket);
  92. WSACleanup();
  93. return 1;
  94. }
  95.  
  96. do {
  97. recvBytes = recv(ClientSocket, recvbuf, recvbuflen, 0);
  98. if (recvBytes > 0) {
  99. printf("Client >> ");
  100. for (int i = 0; i < recvBytes; ++i) {
  101. printf("%c", recvbuf[i]);
  102. }
  103. //printf("Bytes received: %d\n\n", recvBytes);
  104. }
  105. else if (recvBytes == 0) {
  106. printf("Connection closing...\n");
  107. }
  108. else {
  109. printf("recv failed: %d\n", WSAGetLastError());
  110. closesocket(ClientSocket);
  111. WSACleanup();
  112. return 1;
  113. }
  114. } while (recvBytes > 0);
  115.  
  116. closesocket(ClientSocket);
  117. WSACleanup();
  118.  
  119. system("pause>nul");
  120.  
  121. return 0;
  122. }
  123.  

Cliente:
Código
  1. #ifndef WIN32_LEAN_AND_MEAN
  2. #define WIN32_LEAN_AND_MEAN
  3. #endif
  4.  
  5. #include <windows.h>
  6. #include <WinSock2.h>
  7. #include <WS2tcpip.h>
  8. #include <IPHlpApi.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11.  
  12. #pragma comment(lib, "Ws2_32.lib")
  13.  
  14. #define DEFAULT_PORT "27016"
  15. #define DEFAULT_BUFLEN 516
  16.  
  17. int main() {
  18. WSADATA wsaData;
  19. int iResult;
  20.  
  21. char sendbuf[DEFAULT_BUFLEN];
  22. int iSendResult;
  23.  
  24. iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
  25. if (iResult != 0) {
  26. printf("WSAStartup failed with error: %d\n", iResult);
  27. return 1;
  28. }
  29.  
  30. struct addrinfo *result = NULL,
  31. *ptr = NULL,
  32. hints;
  33.  
  34. ZeroMemory(&hints, sizeof(hints));
  35. hints.ai_family = AF_UNSPEC;
  36. hints.ai_socktype = SOCK_STREAM;
  37. hints.ai_protocol = IPPROTO_TCP;
  38.  
  39. iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
  40. if (iResult != 0) {
  41. printf("getaddrinfo failed with error: %d/n", iResult);
  42. WSACleanup();
  43. return 1;
  44. }
  45.  
  46. SOCKET ConnectSocket = INVALID_SOCKET;
  47.  
  48. ptr = result;
  49.  
  50. ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
  51.  
  52. if (ConnectSocket == INVALID_SOCKET) {
  53. printf("Error at socket(): %ld\n", WSAGetLastError());
  54. freeaddrinfo(result);
  55. WSACleanup();
  56. return 1;
  57. }
  58.  
  59. iResult = connect(ConnectSocket, ptr->ai_addr, ptr->ai_addrlen);
  60. if (iResult == SOCKET_ERROR) {
  61. closesocket(ConnectSocket);
  62. ConnectSocket = INVALID_SOCKET;
  63. }
  64.  
  65. freeaddrinfo(result);
  66.  
  67. if (ConnectSocket == INVALID_SOCKET) {
  68. printf("Unable to connect to server!\n");
  69. WSACleanup();
  70. return 1;
  71. }
  72.  
  73. do {
  74. printf("Send a message to the server: ");
  75. fgets(sendbuf, DEFAULT_BUFLEN, stdin);
  76.  
  77. iSendResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
  78. if (iSendResult == SOCKET_ERROR) {
  79. printf("send failed: %d\n", WSAGetLastError());
  80. closesocket(ConnectSocket);
  81. WSACleanup();
  82. return 1;
  83. }
  84. //printf("Bytes sent: %d\n\n", iSendResult);
  85. } while (iSendResult > 0);
  86.  
  87. iResult = shutdown(ConnectSocket, SD_SEND);
  88. if (iResult == SOCKET_ERROR) {
  89. printf("shutdown failed: %d\n", iResult);
  90. closesocket(ConnectSocket);
  91. WSACleanup();
  92. return 1;
  93. }
  94.  
  95. closesocket(ConnectSocket);
  96. WSACleanup();
  97.  
  98. system("pause>nul");
  99.  
  100. return 0;
  101. }
  102.  

Estoy trabajando en Visual Studio Community 2015.


Título: Re: [Sockets] Conexiones múltiples de clientes a servidor.
Publicado por: sodark en 22 Enero 2016, 23:59 pm
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

Código
  1.  
  2. while( 1 == 1 ) {
  3. client_socket = accept(server_socket, (struct sockaddr *) &client, &client_length);
  4. if (client_socket < 0) {
  5. close(server_socket);
  6. close(client_socket);
  7. } else {
  8. run_dozer_thread(client_socket);
  9. }
  10. }
  11.  

Donde run dozer thread

Código
  1. /**********************************************
  2. * @Nombre: run_dozer_thread
  3. * @Def: Funcion encargada lanzar el Thread del Dozer
  4. * @Arg: int socket_cliente
  5. * @Ret:   void
  6. **********************************************/
  7. void run_dozer_thread(int client_socket) {
  8. pthread_t thread_dozer_id;
  9. pthread_create(&thread_dozer_id, NULL, thread_manage_dozer, (void *)client_socket);
  10. }
  11.  


Título: Re: [Sockets] Conexiones múltiples de clientes a servidor.
Publicado por: mester en 23 Enero 2016, 13:16 pm
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:
Código:
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.


Título: Re: [Sockets] Conexiones múltiples de clientes a servidor.
Publicado por: kondrag_X1 en 23 Enero 2016, 13:24 pm
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


Título: Re: [Sockets] Conexiones múltiples de clientes a servidor.
Publicado por: 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)


Título: Re: [Sockets] Conexiones múltiples de clientes a servidor.
Publicado por: mester en 24 Enero 2016, 18:03 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.


Título: Re: [Sockets] Conexiones múltiples de clientes a servidor.
Publicado por: sodark en 25 Enero 2016, 00:42 am
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 :)