- Socket Communication Between JAVA and C/C++
Download: Java_C_Comm.zip
最近需要讓多個不同的語言互相溝通傳輸,所以我採用的Socket做為通訊的基礎,在這一篇裡我們以Java Socket和C Socket為例,而Java與C的通訊概念跟這一篇[C/C++] Socket Connection是一樣的,只是要注意的是你所傳送的型態會不會有little/big endian的問題,以及各語言函式所判定的結束字元。
對Java設計者而言,Java提供了ServerSocket和Socket這兩個類別,設計者只需要import java.net使用這兩個類別就可以完成Server-Client的設計。
對C/C++設計者而言,如果不使用已經封裝好的Socket API,多半是使用Unix Socket的基礎來堆出一個Server-Client的程式,自然會比Java困難許多。
這裡為了照顧兩個平台的使用者,我將C語言的Socket部份同時使用Unix Socket以及WinSocket來完成,並且以Java Socket的OOP概念將他們封裝成Server與Client類別。
/*** Server類別定義如下:***/
class Server{
public:
Server(const int port);
~Server();
#ifdef _WIN32
bool initWsa();
#endif
bool create();
bool bind(const int port);
bool listen();
SOCKET accept();
bool isValid() const;
void close();
private:
int state;
SOCKET socket;
};
多數人會將accept後的socket存放在Server的類別裡,這樣的定義很不好,因為Server類別裡不應該去實做send(), recv()這兩個輸出輸入的成員函式,因為Server最主要的功用就是監聽並建立連線。
/*** Client 類別定義如下: ***/
class Client{
public:
Client(){};
Client(const char* ip, const int port);
~Client();
#ifdef _WIN32
bool initWsa();
#endif
bool create();
bool connect(const char* ip, const int port);
int send(const char* buf) const;
int recv(char* buf);
bool isValid() const;
int operator = (SOCKET socket);
int operator << (const char* buf) const;
int operator >> (char* buf);
void close();
private:
int state;
SOCKET socket;
};
其中的operator = 的overload是為了給為Server socket做accept用的。
兩個程式片段有些地方重複,為什麼不先自訂一個Socket類別,然後讓兩個類別都去繼承Socket?如同前面所提的,Server是不應該去實做send(), recv() ,所以扣掉這些,這兩個類別相同之處就更少了,我是覺得沒有必要讓他們都去繼承Socket類別。
程式有不少地方仍保留C型態,且例外處理做的不完全,還請包含。
說了這麼多都還沒提到這篇的重點,Java跟C通訊的關鍵,下面以C做為Server,Java做為Client互相傳送的程式碼。
關於Winsocket的設計者而言,必須要小心處理WSA的問題,所以我將WSA清除的部份放在deconstructor裡,直到物件被釋放時才清除WSA,這樣做是為了方便Server端去關閉Client端連線,而不造成Server的WSA也被清掉。
如果覺得光看解說難以懂,那你就下載原始碼慢慢看吧!
/*** C Socket ***/
client.send("Hello! I'm C++ Server.\n");
client.recv(buf);
/*** Java Socket ***/
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
buf = in.readLine();
PrintStream out = new PrintStream(socket.getOutputStream());
out.print("Hello! I'm Java Client.\n");
c socket的部份跟之前範例的使用方式是完全一樣的,只是在傳送的結尾我們要加上\n,因為在Java接收的那一端是以readLine()為基準,所以如果沒有跳行字元\n, Java那一端會繼續阻塞,直到對方連線中斷為止。當字串送達Java那一端之後,換行字元會被去掉,所以在列出console時還是要使用換行列出。
所以簡單的Java與C的通訊該念就只有上面這麼一點,不管是什麼語言的互通,盡量以byte字元基準來傳送,比較不會送達到另一端語言接收後與原先不同的情形發生,但如果是比較複雜的編碼部份,有機會碰到我再補上來吧!
- 編譯:
Linux 用戶:
C語言部份我使用g++,Java部份我使用gcj,所以編譯之前請先裝這兩個編譯器。
編譯方式如下:
全部編譯:
make -f makefile.gcc
只編譯c++部份:
make -f makefile.gcc cpp
只編譯Java部份:
make -f makefile.gcc java
如果你還要使用的更細節的編譯部份,那你直接參考makefile.gcc的設定。
Window 用戶(不保證能正常):
C語言部份我使用mingw-g++,Java部份我使用mingw-gcj
編譯方式如下:
全部編譯:
make -f makefile.mingw
只編譯c++:
make -f makefile.mingw cpp
只編譯java:
make -f makefile.mingw jav至於為什麼java的編譯方式是使用jav,而不是使用java,我也不知道mingw的make是怎麼了,我用java他總是給我說up to date,也就是一直不給我編譯。
Java的部份其實我一直都不是很推薦使用gcj來編譯,但是這裡只是單純為了方便,且程式也很小,所以就將就一下。不然我比較推薦eclipse。
為了要讓windows平台下能夠運作,花了不少時間在編譯設定上面,相關的套件可以參考這一篇[GCC] GCC for Windows (MinGW/DEV-C++)
- 範例畫面:
Server : C++
Client : Java
/*** Server ***/
/*** Client ***/
當然上面是一個很簡單的範例,未來再繼續增加複雜的結構啦!