PDA

Ver la Versión Completa : Duda sobre WM_COPYDATA y C


aguml
04-03-2017, 22:36:20
Hola amigos, ya me ayudaron una vez para conseguir comunicar dos procesos con builder pero ahora me surge la duda de si eso es posible en C con procesos de consola o si no es posible en C pues c++ de consola.
La misma duda pero con sockets ya que estuve mirando pero nada de lo que veo me compila y da millones de errores y además no entiendo las cosas que hacen.
Algún tutorial donde lo explique bien y a ser posible en español me seria muy instructivo. Gracias de antemano.

escafandra
04-03-2017, 23:32:01
El mensaje WM_COPYDATA sólo es posible recibirlo en app de ventanas, por lo que no funciona en una consola.

Sin embargo puedes hacer una app de ventanas sin usar la VCL de Builder, con su bucle de mensajes capaz de recibir el mensaje WM_COPYDATA y procesarlo. Esta APP puede tener una ventana invisible diseñada sólo para recibir los mensajes WM_COPYDATA. Esto es posible tanto en C como en C++.

Otra forma de comunicar dos procesos en con los archivos de memoria compartida, en este caso no hace falta un bucle de mensajes pero si un mecanismo que permita consultar dicha memoria en un bucle.

También puedes usar las Atom Tables.

Por último tienes la posibilidad de crear un sistema cliente/servidor con sockets a bajo nivel con lo que prescindes de la VCL. En este caso tampoco necesitas un bucle de mensajes Windows pero si un bucle para tratar los sockets. Este último sistema te servirá para comunicar aplicaciones entre el mismo o diferentes PCs


Saludos.

escafandra
04-03-2017, 23:59:33
Para contestar a tu duda sobre WM_COPYDATA en "C" te muestro este ejemplo de una aplicación sin ventana visible que se manda a sí misma un mensaje WM_COPYDATA y lo responde. Está escrita en C


#include <windows.h>
#pragma hdrstop

//---------------------------------------------------------------------------
LRESULT __stdcall WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(uMsg == WM_COPYDATA){
PCOPYDATASTRUCT Data = (PCOPYDATASTRUCT)lParam;
MessageBox(hWnd, (PCHAR)Data->lpData, "Mensaje recibido", 0);
}

return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

char Name[] = "Hide";
#pragma argsused
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND Handle;
MSG message;
WNDCLASS WinClass = {0,(WNDPROC)WindowProc,0,0,0,0,0,0,"", Name};
COPYDATASTRUCT Data = {0};
char *MSG = "Hola";
Data.lpData = MSG;
Data.cbData = strlen(MSG) + 1;

RegisterClass(&WinClass);
Handle = CreateWindow(Name, "", 0,0,0,0,0, HWND_DESKTOP, (HMENU)0, 0, NULL);
SendMessage(Handle, WM_COPYDATA, 0, (LPARAM)&Data);
while(GetMessage(&message, NULL, 0, 0)){
TranslateMessage(&message);
DispatchMessage(&message);
}

return 0;
}


Saludos.

aguml
05-03-2017, 10:35:21
Muy interesante ¿y que hay que cambiar para que la ventana no sea invisible?
Otra cosa, si quisiera enviar el mensaje a otra ventana ¿solo necesito el handle de esa ventana?
Me interesa mucho lo que dices de sockets a bajo nivel ¿no tienes ningún texto donde se explique con ejemplos y cosas así?
Y sobre lo de la memoria compartida tengo mucha curiosidad de ver como se hace eso porque no tengo ni idea.

escafandra
05-03-2017, 11:57:21
Muy interesante ¿y que hay que cambiar para que la ventana no sea invisible?
Estudia la API CreateWindow (https://msdn.microsoft.com/en-us/library/windows/desktop/ms632679(v=vs.85).aspx) y los estilos: window styles (https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx).

Otra cosa, si quisiera enviar el mensaje a otra ventana ¿solo necesito el handle de esa ventana?
Si, y que la ventana receptora trate el mensaje WM_COPYDATA

Me interesa mucho lo que dices de sockets a bajo nivel ¿no tienes ningún texto donde se explique con ejemplos y cosas así?
Winsock Functions (https://msdn.microsoft.com/en-us/library/windows/desktop/ms741394(v=vs.85).aspx) y recuerda este hilo (http://clubdelphi.com/foros/showthread.php?t=86756) donde te puse este ejemplo (http://clubdelphi.com/foros/showpost.php?p=482512&postcount=15).

Y sobre lo de la memoria compartida tengo mucha curiosidad de ver como se hace eso porque no tengo ni idea.
En el hilo que mencioné antes (http://clubdelphi.com/foros/showthread.php?t=86756) ya te di un enlace sobre los archivos de memoria compartida, aunque en la actualidad está roto. Aquí (http://delphiaccess.com/foros/index.php/topic/1548-archivos-de-memoria-compartida/) lo tienes en C++.


Saludos.

aguml
05-03-2017, 14:58:51
Muchas gracias por toda la información. Sobre sockets en Windows encontré esto: http://www.programacionenc.net/index.php?option=com_content&view=article&id=73:sockets-en-windows&catid=37:programacion-cc&Itemid=55
Tengo algunas dudas sobre el ejemplo que pones de sockets:
- Por lo que he leído accept espera a que se conecte un cliente pero, si son por ejemplo 2 clientes ¿como seria?
- Si cliente y servidor están en la misma máquina ¿se usaría AF_UNIX? Eso es lo que entendí.
- ¿Cómo sería para que fuesen a sincrónicos?

escafandra
05-03-2017, 22:26:56
Tengo algunas dudas sobre el ejemplo que pones de sockets:
- Por lo que he leído accept espera a que se conecte un cliente pero, si son por ejemplo 2 clientes ¿como seria?

La función accept devuelve un socket de comunicación que usaremos para comunicarnos con ese cliente. para poder aceptar otro cliente debemos volver a ejecutar accept, para que esto ocurra y podamos trabajar con varios clientes, se usan hilos de forma que se crea un hilo nuevo por cada cliente aceptado, cada hilo usará el socket proporcionado por accept


- Si cliente y servidor están en la misma máquina ¿se usaría AF_UNIX? Eso es lo que entendí.

Puedes usar siempre AF_INET, lo importante es si usarás un protocolo TCP (SOCK_STREAM) o UDP (SOCK_DGRAM) revisa la forma de crear un socket (https://msdn.microsoft.com/es-es/library/windows/desktop/ms740506(v=vs.85).aspx).


- ¿Cómo sería para que fuesen a sincrónicos?

Para establecer al socket como no bloqueante se usa la función ioctlsocket (https://msdn.microsoft.com/es-es/library/windows/desktop/ms738573(v=vs.85).aspx)
También puedes usar un tipo bloqueante con un TimeOut con la función select (https://msdn.microsoft.com/es-es/library/windows/desktop/ms740141(v=vs.85).aspx)


Saludos.

aguml
06-03-2017, 08:11:37
Ok muchas gracias. Voy a leer y a probar y ya cuento como me fue.

aguml
06-03-2017, 12:00:58
Ando bastante liado con todo esto y por ahora tengo este codigo aunque falta por dejarlo como quiero pero poco a poco:
#include <stdio.h>
#include <windows.h>

#define MAX_THREADS 2

DWORD WINAPI MyThreadFunction( LPVOID lpParam );
void ErrorHandler(LPTSTR lpszFunction);

int main(int argc, char *argv[]){

DWORD dwThreadIdArray[MAX_THREADS];
HANDLE hThreadArray[MAX_THREADS];
int i;

for(i=0; i<MAX_THREADS; i++ )
{
// Creo el hilo para iniciar la ejecución en su función

hThreadArray[i] = CreateThread(
NULL, // default security attributes
0, // use default stack size
MyThreadFunction, // thread function name
0, // argument to thread function
0, // use default creation flags
&dwThreadIdArray[i]); // returns the thread identifier

// Compruebo el valor de retorno
// Si CreateThread falla, termino la ejecución
// Esto limpiará automáticamente los hilos y la memoria

if (hThreadArray[i] == NULL)
{
ErrorHandler("CreateThread");
ExitProcess(3);
}
} // Fin del bucle de creacion de hilos

// Espero a que terminen todos los hilos
WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);

// Cierro todos los handles de los hilos
for(i=0; i<MAX_THREADS; i++)
{
CloseHandle(hThreadArray[i]);
}
printf("Fin de recepcion de mensajes");
getchar();
return (EXIT_SUCCESS);
}

DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
HANDLE hStdout;

WSADATA wsaData;
SOCKET conn_socket,comm_socket;
SOCKET comunicacion;
struct sockaddr_in server;
struct sockaddr_in client;
struct hostent *hp;
int resp,stsize;
char RecvBuff[100];

//Inicializamos la DLL de sockets
resp=WSAStartup(MAKEWORD(1,0),&wsaData);
if(resp){
printf("Error al inicializar socket\n");
getchar();
return resp;
}

//Obtenemos la IP que usará nuestro servidor...
// en este caso localhost indica nuestra propia máquina...
hp=(struct hostent *)gethostbyname("localhost");

if(!hp){
printf("No se ha encontrado servidor...\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

// Creamos el socket...
conn_socket=socket(AF_INET,SOCK_STREAM, 0);
if(conn_socket==INVALID_SOCKET) {
printf("Error al crear socket\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

memset(&server, 0, sizeof(server)) ;
memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
server.sin_family = hp->h_addrtype;
server.sin_port = htons(6000);

// Asociamos ip y puerto al socket
resp=bind(conn_socket, (struct sockaddr *)&server, sizeof(server));
if(resp==SOCKET_ERROR){
printf("Error al asociar puerto e ip al socket\n");
closesocket(conn_socket);
WSACleanup();
getchar();
return WSAGetLastError();
}

if(listen(conn_socket, 1)==SOCKET_ERROR){
printf("Error al habilitar conexiones entrantes\n");
closesocket(conn_socket);
WSACleanup();
getchar();
return WSAGetLastError();
}

// Aceptamos conexiones entrantes
printf("Esperando conexiones entrantes... \n");
stsize=sizeof(struct sockaddr);
comm_socket=accept(conn_socket,(struct sockaddr *)&client,&stsize);
if(comm_socket==INVALID_SOCKET){
printf("Error al aceptar conexión entrante\n");
closesocket(conn_socket);
WSACleanup();
getchar();
return WSAGetLastError();
}
printf("Conexion entrante desde: %s\n", inet_ntoa(client.sin_addr));

// Como no vamos a aceptar más conexiones cerramos el socket escucha
closesocket(conn_socket);

printf("Recibiendo Mensajes... \n");
recv (comm_socket, RecvBuff, sizeof(RecvBuff), 0);
printf("Datos recibidos: %s \n", RecvBuff);

// Cerramos el socket de la comunicacion
closesocket(comm_socket);

// Cerramos liberia winsock
WSACleanup();
return 0;
}

void ErrorHandler(char *lpszFunction)
{
// Retrieve the system error message for the last-error code.

LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();

FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );

// Display the error message.

lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR) lpMsgBuf) + lstrlen((LPCTSTR) lpszFunction) + 40) * sizeof(TCHAR));
printf("%s failed with error %d: %s",lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR) lpDisplayBuf, TEXT("Error"), MB_OK);

// Free error-handling buffer allocations.

LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}

La idea es que ambos procesos se conecten al servidor y envien la cadena con el pid y el tiempo de ejecucion pero me sale lo siguiente en la consola:

Esperando conexiones entrantes...
Error al asociar puerto e ip al socket
Conexion entrante desde: 127.0.0.1
Recibiendo Mensajes...
Datos recibidos: El proceso con PID: 3748 ha funcionado durante 500 milisegundos.


Y se queda esperando en:
WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);
Si pongo que se cree un solo hilo va bien pero la idea de usar hilos es para que pueda conectar alguno mas jejeje.
¿Me pueden explicar porque me falla y que debo cambiar?

aguml
06-03-2017, 13:13:35
Al final he conseguido que funcione pero quiero aprender a hacer bien las cosas y me gustaria que lo corrigiese alguien y me diga si hago algo mal y me pueda explicar como hacerlo correctamente.
Aquí está el código:
Servidor:
#include <stdio.h>
#include <windows.h>

#define MAX_THREADS 2

typedef struct DatosServidor {
SOCKET conn_socket;
} DATOS_SERVIDOR;

DWORD WINAPI MyThreadFunction( LPVOID lpParam );
void ErrorHandler(char *NameFunction);

int main(int argc, char *argv[]){

//Necesarias para crear los hilos
DWORD dwThreadIdArray[MAX_THREADS];
HANDLE hThreadArray[MAX_THREADS];
int i;

//Necesarias para crear el servidor
DATOS_SERVIDOR datos;
WSADATA wsaData;
struct sockaddr_in server;
struct hostent *hp;
int resp;

//Inicializamos la DLL de sockets
resp=WSAStartup(MAKEWORD(1,0),&wsaData);
if(resp){
printf("Error al inicializar socket\n");
getchar();
return resp;
}

//Obtenemos la IP que usará nuestro servidor...
// en este caso localhost indica nuestra propia máquina...
hp=(struct hostent *)gethostbyname("localhost");

if(!hp){
printf("No se ha encontrado servidor...\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

// Creamos el socket...
datos.conn_socket=socket(AF_INET,SOCK_STREAM, 0);
if(datos.conn_socket==INVALID_SOCKET) {
printf("Error al crear socket\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

memset(&server, 0, sizeof(server)) ;
memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
server.sin_family = hp->h_addrtype;
server.sin_port = htons(6000);

// Asociamos ip y puerto al socket
resp=bind(datos.conn_socket, (struct sockaddr *)&server, sizeof(server));
if(resp==SOCKET_ERROR){
printf("Error al asociar puerto e ip al socket\n");
closesocket(datos.conn_socket);
WSACleanup();
getchar();
return WSAGetLastError();
}

if(listen(datos.conn_socket, 1)==SOCKET_ERROR){
printf("Error al habilitar conexiones entrantes\n");
closesocket(datos.conn_socket);
WSACleanup();
getchar();
return WSAGetLastError();
}

printf("Esperando conexiones entrantes... \n");

for(i=0; i<MAX_THREADS; i++ )
{
// Creo el hilo para iniciar la ejecución en su función

hThreadArray[i] = CreateThread(
NULL, // default security attributes
0, // use default stack size
MyThreadFunction, // thread function name
&datos, // argument to thread function
0, // use default creation flags
&dwThreadIdArray[i]); // returns the thread identifier

// Compruebo el valor de retorno
// Si CreateThread falla, termino la ejecución
// Esto limpiará automáticamente los hilos y la memoria

if (hThreadArray[i] == NULL)
{
ErrorHandler("CreateThread");
getchar();
return 3;
}
} // Fin del bucle de creacion de hilos

// Espero a que terminen todos los hilos
WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);

// Como no vamos a aceptar más conexiones cerramos el socket escucha
closesocket(datos.conn_socket);

// Cerramos liberia winsock
WSACleanup();

// Cierro todos los handles de los hilos
for(i=0; i<MAX_THREADS; i++)
{
CloseHandle(hThreadArray[i]);
}
printf("Fin de recepcion de mensajes");
getchar();
return (EXIT_SUCCESS);
}

DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
char RecvBuff[100];
SOCKET comm_socket;
DATOS_SERVIDOR *datos;
struct sockaddr_in client;
int stsize;

datos=(DATOS_SERVIDOR*)lpParam;

// Aceptamos conexiones entrantes
stsize=sizeof(struct sockaddr);
comm_socket=accept(datos->conn_socket,(struct sockaddr *)&client,&stsize);
if(comm_socket==INVALID_SOCKET){
printf("Error al aceptar conexión entrante\n");
getchar();
return WSAGetLastError();
}
printf("Conexion entrante desde: %s\n", inet_ntoa(client.sin_addr));

printf("Recibiendo Mensajes... \n");
recv (comm_socket, RecvBuff, sizeof(RecvBuff), 0);
printf("Datos recibidos: %s \n", RecvBuff);

// Cerramos el socket de la comunicacion
closesocket(comm_socket);

return 0;
}

void ErrorHandler(char *NameFunction)
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();

// Recupero el mensaje de error del sistema para el último error ocurrido
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );

// Muestro el mensaje de error
printf("Error: %s failed with error %d: %s",NameFunction, dw, lpMsgBuf);

// Libero la memoria allocada
LocalFree(lpMsgBuf);
}

Cliente:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main(int argc, char *argv[]){

WSADATA wsaData;
SOCKET conn_socket;
struct sockaddr_in server;
struct hostent *hp;
int resp,salida;
unsigned long inicio,fin,lapso;
char SendBuff[100];
HWND windowHandle;
DWORD processID;

//Obtengo el tiempo en el que inicia el proceso
inicio=GetTickCount();

//Inicializamos la DLL de sockets
resp=WSAStartup(MAKEWORD(1,0),&wsaData);
if(resp){
printf("Error al inicializar socket\n");
getchar();
return -1;
}

//Obtenemos la IP del servidor... en este caso
// localhost indica nuestra propia máquina...
hp=(struct hostent *)gethostbyname("localhost");

if(!hp){
printf("No se ha encontrado servidor...\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

// Creamos el socket...
conn_socket=socket(AF_INET,SOCK_STREAM, 0);
if(conn_socket==INVALID_SOCKET) {
printf("Error al crear socket\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

memset(&server, 0, sizeof(server)) ;
memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
server.sin_family = hp->h_addrtype;
server.sin_port = htons(6000);

// Nos conectamos con el servidor...
if(connect(conn_socket,(struct sockaddr *)&server,sizeof(server))==SOCKET_ERROR){
printf("Fallo al conectarse con el servidor\n");
closesocket(conn_socket);
WSACleanup();
getchar();
return WSAGetLastError();
}
printf("Conexion establecida con: %s\n", inet_ntoa(server.sin_addr));

//Esto lo pongo para tener una salida del proceso aleatoria
//No es necesaria para nada pero al menos asi consigo simular
//que el proceso hace algo mas a parte de comunicarse
srand(time(NULL));
do{
salida = rand()%1000;
Sleep(10);
}while(salida != 0);

//Obtengo el tiempo en que termina el proceso
fin=GetTickCount();

//Obtengo el lapso de tiempo transcurrido
lapso=fin-inicio;

processID = GetCurrentProcessId();

//Enviamos el lapso de tiempo que transcurrio...
printf("Enviando lapso de tiempo... \n");
sprintf(SendBuff,"El proceso con PID: %lu ha funcionado durante %lu milisegundos.",processID,lapso);
send(conn_socket,SendBuff,sizeof(SendBuff),0);
printf("Lapso enviado: %lu milisegundos\n", lapso);

// Cerramos el socket y liberamos la DLL de sockets
closesocket(conn_socket);
WSACleanup();
getchar();
return EXIT_SUCCESS;
}

Gracias de antemano ;)

escafandra
06-03-2017, 13:14:30
Tu hilo contiene todo el servidor con lo que se repiten y ten en cuenta que no puedes tener dos servidores escuchando por el mismo puerto, el segundo sencillamente falla. El hilo debe crearse tras accept y el parámetro que pasas el el socket que devuelve accept. Cuando la comunicación con ese cliente termine, el hilo debe hacerlo también, con lo que tendrás que detectar un error de lectura recv < 0 y un posible comando de desconexión que diseñas tu mismo y que al cecibirlo desconectas el socket y terminas el hilo saliendo de su bucle.

WaitForMultipleObjects (https://msdn.microsoft.com/es-es/library/windows/desktop/ms687025(v=vs.85).aspx) espera a que un hilo o todos terminen. Se utiliza para control. En este caso puedes establecer un número máximo de hilos de ejecución y mantener al servidor esperando antes de crear otra conexión en un nuevo hilo.

Saludos.

aguml
06-03-2017, 17:24:41
La verdad es que lo único que creo entender es lo que me dices de accept el cual puse dentro del hilo porque pensé que era bloqueante pero si no es así es tan simple como ponerlo en el bucle for antes de llamar al hilo y pasar el valor retornado en vez del que paso ¿no?
Lo demás no entiendo lo que me quieres decir. Si pudieras corregirme el código y poner comentarios en las partes que modifiques explicando el porque lo entendería mejor.

escafandra
06-03-2017, 21:03:56
Te muestro unos cambios en el servidor y en el cliente. En el server he eliminado el tratamiento de hilos para no enturbiar el código y muestro donde debería haber un bucle para mantenerse activo hasta orden contraria. También muestro como crea un hilo cada vez que acepta un cliente. Efectivamente accept es bloqueante, solo se libera al aceptar un cliente, es por ese motivo por el que no se coloca en el thread.

El server no necesita especificar ninguna dirección, por eso se usa INADDR_ANY. Tambien te muestro una forma más amable de inicializar la dirección tanto en el servidor como en el cliente. Me he tomado la libertad de cambiar algún nombre de variable y de eliminar la estructura parámetro de los hilos, puesto que pasando el Socket de comunicación basta.


#include <windows.h>
#include <winsock2.h>
#include <stdio.h>

DWORD WINAPI ThClient(SOCKET *Sock)
{
int Len=0;
char RecvBuff[100];

// Encontrando la dirección IP (se podría pasar como parámetro...)
struct sockaddr_in client;
int addr_size = sizeof(sockaddr_in);
getpeername(*Sock, (sockaddr*)&client, &addr_size);

printf("Conexion entrante desde: %s\n", inet_ntoa(client.sin_addr));
printf("Recibiendo Mensajes... \n");
do{
// Leemos los datos entrantes
Len = recv(*Sock, RecvBuff, sizeof(RecvBuff), 0); //recibimos los datos que envie
if(Len<0) continue;
printf("Datos recibidos: %s \n", RecvBuff);
// Un comando debería servir para salir del bucle.
if(!strnicmp(RecvBuff, "Adios", 5)) break;
// Si tenemos datos hablamos...
gets(RecvBuff);
strcat(RecvBuff, "\n");
send(*Sock, RecvBuff, lstrlen(RecvBuff)+1, 0);
} while(Len && Len!=-1);

closesocket(*Sock);

return 0;
}

int main(int argc, char *argv[])
{
//Necesarias para crear el servidor
WSADATA wsaData;
SOCKET Sock_e; // Sock de escucha
SOCKET Sock_c; // Sock de comunicación
struct sockaddr_in server, client;
struct hostent *host;
DWORD Port = 6000;
int resp;

//Inicializamos la DLL de sockets
resp = WSAStartup(MAKEWORD(1,0),&wsaData);
if(resp){
printf("Error al inicializar socket\n");
getchar();
return resp;
}

//Obtenemos la IP que usará nuestro servidor...
// en este caso localhost indica nuestra propia máquina...
host = (struct hostent *)gethostbyname("localhost");

if(!host){
printf("No se ha encontrado servidor...\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

// Creamos el socket...
Sock_e = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(Sock_e == INVALID_SOCKET) {
printf("Error al crear socket\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

// memset(&server, 0, sizeof(server)) ;
// memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
// server.sin_family = hp->h_addrtype;
// server.sin_port = htons(6000);

server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(Port);

// Asociamos ip y puerto al socket
resp = bind(Sock_e, (struct sockaddr *)&server, sizeof(server));
if(resp == SOCKET_ERROR){
printf("Error al asociar puerto e ip al socket\n");
closesocket(Sock_e);
WSACleanup();
getchar();
return WSAGetLastError();
}

// bucle de escucha
while(true){
// Escuchando puerto
printf("\nEscuchando por el puerto %d\n", Port);

if(listen(Sock_e, 1) == SOCKET_ERROR){
printf("Error al habilitar conexiones entrantes\n");
closesocket(Sock_e);
WSACleanup();
getchar();
return WSAGetLastError();
}
else{
printf("Esperando conexiones entrantes... \n");
resp = sizeof(sockaddr);
Sock_c = accept(Sock_e,(sockaddr*)&client, &resp);
// Creamos el hilo
CloseHandle(CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThClient, &Sock_c, 0,0));
}
} // Fin del bucle de escucha

// Como no vamos a aceptar más conexiones cerramos el socket escucha
closesocket(Sock_c);

// Cerramos liberia winsock
WSACleanup();
printf("Fin de recepcion de mensajes");
getchar();
return (EXIT_SUCCESS);
}


El cliente también lo he cambiado un poco, sobre todo para indicarte donde deberías poner bucles. En principio serían dos bucles, uno principal que trataría de conectarse contínuamente al server, aunque éste no exista. De este bucle se saldría por actuación del usuario cerrando la app con un comando a tal fin.

Un segundo bucle mantendría la conversación con el hilo que el server nos cree hasta que la finalicemos con algún comando desde el teclado, por ejemplo y enviado al servidor. El server al recibir ese comando saldría del bucle del thread y finalizaría dicho hilo.


#include <windows.h>
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET conn_socket;
struct sockaddr_in server;
struct hostent *host;
DWORD Port = 6000;
int resp, salida;
unsigned long inicio,fin,lapso;
char SendBuff[100];
HWND windowHandle;
DWORD processID;

//Obtengo el tiempo en el que inicia el proceso
inicio = GetTickCount();

//Inicializamos la DLL de sockets
resp=WSAStartup(MAKEWORD(1,0),&wsaData);
if(resp){
printf("Error al inicializar socket\n");
getchar();
return -1;
}


// Aquí debería empezar el Bucle cliente principal
// Para que no cierre aunque no conecte con el server. Solo cerraría por mediación del usuario
// Saliendo del bucle.

// Creamos el socket...
conn_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(conn_socket == INVALID_SOCKET) {
printf("Error al crear socket\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

/*
memset(&server, 0, sizeof(server)) ;
memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
server.sin_family = hp->h_addrtype;
server.sin_port = htons(Port);
*/

//Dirección IP del servidor y Puerto
// localhost indica nuestra propia máquina...
host = (struct hostent *)gethostbyname("localhost");
if(!host){
printf("No se ha encontrado servidor...\n");
getchar();
WSACleanup();
return WSAGetLastError();
}
server.sin_family = AF_INET;
server.sin_port = htons(Port);
server.sin_addr = *((struct in_addr *)host->h_addr);
ZeroMemory(server.sin_zero, 8);

// Nos conectamos con el servidor...
if(connect(conn_socket, (struct sockaddr *)&server,sizeof(server)) == SOCKET_ERROR){
printf("Fallo al conectarse con el servidor\n");
closesocket(conn_socket);
WSACleanup();
getchar();
return WSAGetLastError();
}

// Aquí debería empezar un bucle de comunicación con el servidor según tu propio protocolo

printf("Conexion establecida con: %s\n", inet_ntoa(server.sin_addr));
//Esto lo pongo para tener una salida del proceso aleatoria
//No es necesaria para nada pero al menos asi consigo simular
//que el proceso hace algo mas a parte de comunicarse
srand(time(NULL));
do{
salida = rand()%1000;
Sleep(10);
}while(salida != 0);

//Obtengo el tiempo en que termina el proceso
fin = GetTickCount();

//Obtengo el lapso de tiempo transcurrido
lapso = fin-inicio;
processID = GetCurrentProcessId();

//Enviamos el lapso de tiempo que transcurrio...
printf("Enviando lapso de tiempo... \n");
sprintf(SendBuff,"El proceso con PID: %lu ha funcionado durante %lu milisegundos.",processID,lapso);
send(conn_socket,SendBuff,sizeof(SendBuff),0);
printf("Lapso enviado: %lu milisegundos\n", lapso);

// Cerramos el socket y liberamos la DLL de sockets
closesocket(conn_socket);
WSACleanup();
getchar();
return EXIT_SUCCESS;
}

Esto está muy en pañales pero funciona. Ahora deberás diseñar tus bucles para que la funcionalidad sea la que buscas, y una colección de palabras clave o comandos que se intercambiarán el servidor con cada cliente. Por su puesto, cada palabra clave, significará una acción a llevar a cabo o un indicativo del dato que estamos pasando. Para eso te debes inventar una sintaxis.

Espero que entiendas un poco mejor el tema.

Saludos.

aguml
07-03-2017, 00:33:45
Ahora si entiendo mejor. En realidad tú código es más de lo que necesito creo yo. Realmente la idea es que un cliente se conecte al servidor y luego obtenga el tiempo inicial y cuando se vaya a salir del proceso calcule el lapso de tiempo transcurrido y lo envie al servidor, no necesita ni enviar más nada ni recibir por lo que el servidor tampoco necesita esperar más nada después de recibir el valor ya que sabe que después de eso el cliente se cerrará. No se si me entiendes, no habrá más comunicación con ese cliente y este se cerrará si o si.
Lo que no veo en tu código del server es como sales del bucle infinito que pusiste para los hilos ya que no veo ningún break. En mi caso tenia que especificar el número de hilos para que WaitForMultipleObjects supiese cuando terminar y tu no lo usas por lo que jamás se saldría a no ser que cierre el programa servidor. Nunca se ejecutaría lo que está después del bucle y no veo la manera de conseguirlo sin hacer lo que yo hice de indicar el número de hilos a WaitForMultipleObjects.

aguml
07-03-2017, 13:18:38
Ahora mismo lo tengo asi y sigo teniendo algunas dudas sobre todo en como colocar el bucle del cliente ya que hice algo que creo que no es del todo correcto pero no se seguro. Ademas he colocado un comentario en la zona donde se asignan los parametros para el server en el codigo del cliente que me gustaria que me pudieses solventar.
Aqui el codigo del servidor:
#include <stdio.h>
#include <string.h>
#include <windows.h>

#define MAX_THREADS 2

DWORD WINAPI ThClient(SOCKET *Sock);
void ErrorHandler(char *NameFunction);

int main(int argc, char *argv[]){

//Necesarias para crear los hilos
DWORD dwThreadIdArray[MAX_THREADS];
HANDLE hThreadArray[MAX_THREADS];
int i;

//Necesarias para crear el servidor
WSADATA wsaData;
struct sockaddr_in server,client;
struct hostent *hp;
int resp;
SOCKET Sock_c; //Socket del cliente
SOCKET Sock_e; //Socket de escucha del servidor
DWORD Port=6000;
int stsize;

//Inicializamos la DLL de sockets
resp=WSAStartup(MAKEWORD(1,0),&wsaData);
if(resp){
printf("Error al inicializar socket\n");
getchar();
return resp;
}

//Obtenemos la IP que usará nuestro servidor...
// en este caso localhost indica nuestra propia máquina...
hp=(struct hostent *)gethostbyname("localhost");

if(!hp){
printf("No se ha encontrado servidor...\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

// Creamos el socket...
Sock_e=socket(AF_INET,SOCK_STREAM, 0);
if(Sock_e==INVALID_SOCKET) {
printf("Error al crear socket\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

memset(&server, 0, sizeof(server)) ;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(Port);

// Asociamos ip y puerto al socket
resp=bind(Sock_e, (struct sockaddr *)&server, sizeof(server));
if(resp==SOCKET_ERROR){
printf("Error al asociar puerto e ip al socket\n");
closesocket(Sock_e);
WSACleanup();
getchar();
return WSAGetLastError();
}

if(listen(Sock_e, 1)==SOCKET_ERROR){
printf("Error al habilitar conexiones entrantes\n");
closesocket(Sock_e);
WSACleanup();
getchar();
return WSAGetLastError();
}

printf("Esperando conexiones entrantes... \n");

for(i=0; i<MAX_THREADS; i++ )
{
// Aceptamos conexiones entrantes
stsize=sizeof(struct sockaddr);
Sock_c=accept(Sock_e,(struct sockaddr *)&client,&stsize);
if(Sock_c==INVALID_SOCKET){
printf("Error al aceptar conexión entrante\n");
getchar();
return WSAGetLastError();
}
//printf("Conexion entrante desde: %s\n", inet_ntoa(client.sin_addr));

// Creo el hilo para iniciar la ejecución en su función
hThreadArray[i] = CreateThread(
NULL, // default security attributes
0, // use default stack size
(LPTHREAD_START_ROUTINE)ThClient, // thread function name
&Sock_c, // argument to thread function
0, // use default creation flags
&dwThreadIdArray[i]); // returns the thread identifier

// Compruebo el valor de retorno
// Si CreateThread falla, termino la ejecución
// Esto limpiará automáticamente los hilos y la memoria

if (hThreadArray[i] == NULL)
{
ErrorHandler("CreateThread");
getchar();
return 3;
}
} // Fin del bucle de creacion de hilos

// Espero a que terminen todos los hilos
WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);

// Como no vamos a aceptar más conexiones cerramos el socket escucha
closesocket(Sock_e);

// Cerramos liberia winsock
WSACleanup();

// Cierro todos los handles de los hilos
for(i=0; i<MAX_THREADS; i++)
{
CloseHandle(hThreadArray[i]);
}
printf("Fin de recepcion de mensajes");
getchar();
return (EXIT_SUCCESS);
}

DWORD WINAPI ThClient(SOCKET *Sock)
{
char RecvBuff[100];

memset(RecvBuff,0,sizeof(RecvBuff));
recv (*Sock, RecvBuff, sizeof(RecvBuff), 0);

if(strlen(RecvBuff)==0)
printf("El cliente se ha cerrado antes de enviar los datos\n");
else
printf("%s\n", RecvBuff);

memset(RecvBuff,0,sizeof(RecvBuff));
recv (*Sock, RecvBuff, sizeof(RecvBuff), 0);

if(strlen(RecvBuff)==0)
printf("El cliente se ha cerrado antes de enviar los datos\n");
else
printf("Datos recibidos: %s \n", RecvBuff);

// Cerramos el socket de la comunicacion
closesocket(*Sock);

return 0;
}

void ErrorHandler(char *NameFunction)
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();

// Recupero el mensaje de error del sistema para el último error ocurrido
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );

// Muestro el mensaje de error
printf("Error: %s failed with error %d: %s",NameFunction, dw, lpMsgBuf);

// Libero la memoria allocada
LocalFree(lpMsgBuf);
}

Aqui el codigo del cliente:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main(int argc, char *argv[]){

WSADATA wsaData;
SOCKET conn_socket;
struct sockaddr_in server;
struct hostent *host;
DWORD Port = 6000;
int resp,salida;
unsigned long inicio,fin,lapso;
char SendBuff[100];
HWND windowHandle;
DWORD processID;

//Inicializamos la DLL de sockets
resp=WSAStartup(MAKEWORD(1,0),&wsaData);
if(resp){
printf("Error al inicializar socket\n");
getchar();
return -1;
}

//Obtenemos la IP del servidor... en este caso
// localhost indica nuestra propia máquina...
host=(struct hostent *)gethostbyname("localhost");

if(!host){
printf("No se ha encontrado servidor...\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

// Creamos el socket...
conn_socket=socket(AF_INET,SOCK_STREAM, 0);
if(conn_socket==INVALID_SOCKET) {
printf("Error al crear socket\n");
getchar();
WSACleanup();
return WSAGetLastError();
}

memset(&server, 0, sizeof(server)) ;
server.sin_family = AF_INET;
server.sin_port = htons(Port);
//La siguiente linea, si no fuesen cliente y servidor en el mismo equipo
//¿Tendria que darle la direccion ip del servidor?
//Es que ahora mismo apunta a localhost del cliente ¿no?
server.sin_addr = *((struct in_addr *)host->h_addr);

// Nos conectamos con el servidor...
printf("Conectando con el servidor\n");
while(connect(conn_socket,(struct sockaddr *)&server,sizeof(server))==SOCKET_ERROR)
Sleep(10);

processID = GetCurrentProcessId();
printf("Conexion establecida con: %s con proceso con PID %lu\n", inet_ntoa(server.sin_addr),processID);
sprintf(SendBuff,"Conexion establecida con: %s con proceso con PID: %lu.",inet_ntoa(*((struct in_addr *)host->h_addr)),processID);
send(conn_socket,SendBuff,sizeof(SendBuff),0);

//Obtengo el tiempo en el que inicia el proceso
inicio=GetTickCount();

//Esto lo pongo para tener una salida del proceso aleatoria
//No es necesaria para nada pero al menos asi consigo simular
//que el proceso hace algo mas a parte de comunicarse
srand(time(NULL));
do{
salida = rand()%1000;
Sleep(10);
}while(salida != 0);

//Obtengo el tiempo en que termina el proceso
fin=GetTickCount();

//Obtengo el lapso de tiempo transcurrido
lapso=fin-inicio;

//Enviamos el lapso de tiempo que transcurrio...
printf("Enviando lapso de tiempo... \n");
sprintf(SendBuff,"El proceso con PID: %lu ha funcionado durante %lu milisegundos.",processID,lapso);
send(conn_socket,SendBuff,sizeof(SendBuff),0);
printf("Lapso enviado: %lu milisegundos\n", lapso);

// Cerramos el socket y liberamos la DLL de sockets
closesocket(conn_socket);
WSACleanup();
getchar();
return EXIT_SUCCESS;
}

escafandra
07-03-2017, 18:44:22
Para que el cliente no termine si no conecta con el servidor y continúe intentando la conexión, debe contener un bucle y quedaría algo como esto:

#include <windows.h>
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
WSADATA wsaData;
SOCKET conn_socket;
struct sockaddr_in server;
struct hostent *host;
char ServerAdr[] = "localhost"; // Nombre de la Máquina del Server
DWORD Port = 6000;
int resp, salida;
unsigned long inicio,fin,lapso;
char SendBuff[100];
HWND windowHandle;
DWORD processID;
BOOL Salir = false;
int Result = EXIT_SUCCESS;

//Obtengo el tiempo en el que inicia el proceso
inicio = GetTickCount();

//Inicializamos la DLL de sockets
resp = WSAStartup(MAKEWORD(1,0),&wsaData);
if(resp){
printf("Error al inicializar socket\n");
getchar();
return -1;
}

// Bucle cliente principal
while(!Salir){
// Creamos el socket...
conn_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(conn_socket == INVALID_SOCKET) {
printf("Error al crear socket\n");
Result = WSAGetLastError();
break;
}
else{
//Dirección IP del servidor y Puerto
// localhost indica nuestra propia máquina...
host = (struct hostent *)gethostbyname(ServerAdr);
if(!host){
printf("No se ha encontrado servidor...\n");
Result = WSAGetLastError();
break;
}
else{
server.sin_family = AF_INET;
server.sin_port = htons(Port);
server.sin_addr = *((struct in_addr *)host->h_addr);
ZeroMemory(server.sin_zero, 8);

// Nos conectamos con el servidor...
if(connect(conn_socket, (struct sockaddr *)&server,sizeof(server)) == SOCKET_ERROR)
printf("Fallo al conectarse con el servidor\n");
else{
// Aquí debería empezar un bucle de comunicación con el servidor según tu propio protocolo
printf("Conexion establecida con: %s\n", inet_ntoa(server.sin_addr));
//Esto lo pongo para tener una salida del proceso aleatoria
//No es necesaria para nada pero al menos asi consigo simular
//que el proceso hace algo mas a parte de comunicarse
srand(time(NULL));
do{
salida = rand()%1000;
Sleep(10);
}while(salida != 0);

//Obtengo el tiempo en que termina el proceso
fin = GetTickCount();

//Obtengo el lapso de tiempo transcurrido
lapso = fin-inicio;
processID = GetCurrentProcessId();

//Enviamos el lapso de tiempo que transcurrio...
printf("Enviando lapso de tiempo... \n");
sprintf(SendBuff,"El proceso con PID: %lu ha funcionado durante %lu milisegundos.",processID,lapso);
send(conn_socket,SendBuff,sizeof(SendBuff),0);
printf("Lapso enviado: %lu milisegundos\n", lapso);
Salir = true;
// Cerramos el socket y liberamos la DLL de sockets
closesocket(conn_socket);
}// conect
}// host
}// socket
}//while
WSACleanup();
getchar();
return Result;
}


Se pueden simplificar esos mensajes y salidas de error.

Saludos.

aguml
07-03-2017, 21:01:47
:eek: me gusta como queda. En cuanto pueda lo pruebo y te cuento pero tiene muy buena pinta. Por otro lado se te olvidó el tema de la duda que comentaba en mi codigo del cliente:
//La siguiente linea, si no fuesen cliente y servidor en el mismo equipo
//¿Tendria que darle la direccion ip del servidor?
//Es que ahora mismo apunta a localhost del cliente ¿no?
server.sin_addr = *((struct in_addr *)host->h_addr);
Por último comentar que me parece increible que C estandar no tenga soporte con una libreria propia y estandar para poder portar a otras plataformas sin tener que estar currandoselo en multiples plataformas a la vez ni con macros ni con nada por el estilo. Creo que deberia ser de lo proximo que añadan.

escafandra
07-03-2017, 21:26:33
:eek: me gusta como queda. En cuanto pueda lo pruebo y te cuento pero tiene muy buena pinta. Por otro lado se te olvidó el tema de la duda que comentaba en mi codigo del cliente:
//La siguiente linea, si no fuesen cliente y servidor en el mismo equipo
//¿Tendria que darle la direccion ip del servidor?
//Es que ahora mismo apunta a localhost del cliente ¿no?
server.sin_addr = *((struct in_addr *)host->h_addr);Por último comentar que me parece increible que C estandar no tenga soporte con una libreria propia y estandar para poder portar a otras plataformas sin tener que estar currandoselo en multiples plataformas a la vez ni con macros ni con nada por el estilo. Creo que deberia ser de lo proximo que añadan.

Si te contesté... es aquí donde colocas la dirección del servidor (ServerAdr):

//Dirección IP del servidor y Puerto
// localhost indica nuestra propia máquina...
host = (struct hostent *)gethostbyname(ServerAdr);


Las funciones para sockets con casi idénticas en linux y son un estándar, date cuenta que la comunicación por esta vía es independiente del S.O. En delphi se usan igual.

Saludos.

aguml
08-03-2017, 00:15:58
Una duda más. Cuando en el cliente indicó el puerto ¿es el puerto del servidor? ¿y como indicaría cual es el puerto del cliente? O sea me refiero a si es el mismo puerto tanto para cliente como para servidor o no tiene porque ser el mismo para ambos.

escafandra
08-03-2017, 00:21:00
El puerto debe ser el mismo para cliente y servidor. Eso no quita para que puedan escribirse app que sean al mismo tiempo clientes y servidores en hilos separados, en ese caso debe haber dos puertos diferentes. Este caso es más típico de protocolo UDP pues no requiere conexión previa.


Saludos.

aguml
08-03-2017, 07:32:08
La pregunta quizás no la he planteado bien. Imagino a el emule y su Red kad. Según tengo entendido esa Red no necesita de servidor intermedio para conectar entre los pcs. Si por ejemplo yo le indico a emule que tiene que usar el puerto 6000 y abro en mi router ese puerto para tcp/udp y otra persona hace lo mismo pero con el puerto 5000 ¿cómo es que se conectan entre ambos ordenadores usando puertos diferentes?