Lors de la réalisation d’une application, j’ai été confronté au problème suivant : faire communiquer Java et C++. Il existe plusieurs moyen pour parvenir à cela :
- L’utilisation des pipes
- L’utilisation d’un fichier commun
- L’utilisation des stdin et stdout
- L’utilisation d’une solution réseau : corba, socket…
C’est cette dernière solution que j’ai choisi et dont ce billet va traiter.
Lors de ce type de communication, il faut nécessairement un serveur et un client. Le serveur devra être exécuté avant le client qui s’y connectera. Une fois la connexion établie, il n’y a plus de différences entre le client et le serveur, les deux attendront un éventuel message, et les deux seront prêt à en émettre.
Pour pouvoir se faire (être prêt à émettre et à recevoir en même temps), le client et le serveur devront être multithread, un thread sera chargé d’attendre un message et un autre d’en envoyer.
Dans cet exemple, l’application Java aura le rôle de serveur, et application C++ aura le rôle de client. Si vous comprenez bien le mécanisme, vous n’aurez pas de mal à les intervertir.
Tout d’abord, intéressons nous au serveur (Serveur.java)
Pour faire un Thread en Java, une méthode consiste à faire une classe qui étend de « Thread» et qui redéfinit la méthode « run» . Le contenu de cette méthode sera celui exécuté par le thread. Dans notre cas, cette méthode devra attendre qu’un client se connecte puis attendra un éventuel message :
/**
*
* @author Romain Bouleis www.bouleis.fr
*
*/
public class Server extends Thread{
private int port;
private boolean run = true;
private PrintWriter out;
private BufferedReader in;
public Server(int port) {
this.port = port;
}
/**
* {@docRoot} start the server
*/
public void run() {
ServerSocket server;
Socket client;
try {
//crée un nouveau socket
server = new java.net.ServerSocket (this.port);
//attend un client
client = server.accept();
System.out.println("[Server] client connected");
//writer sur le socket
out = new PrintWriter(client.getOutputStream(), true);
//reader sur le soket
in = new BufferedReader (new InputStreamReader(client.getInputStream()));
String msg = "";
//tant que le serveur est actif et que le client est connecte
while(this.run && msg != null) {
//attend un message
msg = in.readLine();
//traiter le message
System.out.println("[server] message reçu : "+msg);
//arete le serveur si le message est "exit"
if("exit".equals(msg)) this.run = false;
}
//ferme le reader et le writer
out.close();
in.close();
//ferme le socket
client.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
Server s = new Server(8000);
s.start();
}
}
Ce code executera un serveur attendant un client sur le port 8000. Pour pouvoir envoyer un message à un éventuel client, il faut rajouter une méthode :
public void sendCommand(String c) {
if(out != null) out.println(c);
else System.err.println("no client");
}
public static void main(String args[]) {
Server s = new Server(8000);
s.start();
//attendre la connexion d'un client
s.sendCommand("envoie de cette ligne au client");
}
On peut tester le serveur grâce à telnet : telnet localhost 8000. Lors d’envoie de messages, ils devraient s’afficher dans la console depuis laquelle le serveur a été lancé.
Intéressons nous maintenant au client. De la même façon, un thread sera crée pour attendre des messages du serveur.
#define SOCKET_PORT 8000
using namespace std;
int socketDescriptor;
Client::Client() { }
//se connecte au serveur
void Client::init(char* socket_host) {
struct sockaddr_in serverAddress;
struct hostent *hostInfo;
cout << "[Client] connecting..." <<
//Choix de l'hôte
hostInfo = gethostbyname(socket_host);
if (hostInfo == NULL) {
cout << "[Client] problem interpreting host: " <<
exit(1);
}
// Creation du socket.
socketDescriptor = socket(PF_INET, SOCK_STREAM, 0);
if (socketDescriptor < 0) {
cerr << "[Client] cannot create socket\n";
exit(1);
}
serverAddress.sin_family = hostInfo->h_addrtype;
memcpy((char *) &serverAddress.sin_addr.s_addr,
hostInfo->h_addr_list[0], hostInfo->h_length);
serverAddress.sin_port = htons(SOCKET_PORT);
memset(serverAddress.sin_zero, '\0', sizeof serverAddress.sin_zero);
connect(socketDescriptor, (struct sockaddr *)&serverAddress, sizeof(serverAddress));
cout << "[Client] connected to server" <<
}
//cree un thread executant la methode runWaitMessage
pthread_t Client::runWaitMessageThread() {
pthread_t attente;
(void) pthread_create(&attente, NULL, Client::runWaitMessage, (void*) &socketDescriptor);
return attente;
}
//attends des messages
void * Client::runWaitMessage(void * args) {
cout << "[Client] Waiting messages thread running..." <
char line[255];
memset(line, '\0', sizeof(line));
while(recv(socketDescriptor, line, sizeof(line), 0) > 1) {
//remove the \n at the end of the string
unsigned int i=0;
char line_[strlen(line)-1];
for(i=0;i<strlen(line);i++)
line_[i] = line[i];
}
line_[i] = '\0';
cout << "[Client] " << line_ << " received" <<
//on traite le message
memset(line, '\0', sizeof(line));
}
return (void *) 1;
}
//send a command
void Client::sendCommande(string s) {
char * message;
string s_ = s;
s += "\r\n";
message = (char *) s.c_str();
send(socketDescriptor,message,strlen(message),0);
cout << "[Client] " << s_ << " sent" <
}
//close the socket
void Client::closeClient() {
close(socketDescriptor);
cout << "[Client] disconnected" <<
}
Enfin, on peut lancer le client avec la méthode suivante :
int main(int argc, char* argv[]) {
Client *client = new Client();
client->init("localhost");
client->runWaitMessageThread();
client->sendCommande(message);
}