- 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 ***/
當然上面是一個很簡單的範例,未來再繼續增加複雜的結構啦!