Java和.NET下使用socket


前一段時間做過JAVA的Socket客戶端轉換為C#的Socket客戶端的工作,最近開發的項目又需要用Java代碼通過Socket的方式作為客戶端去請求服務端交互數據的功能,這次對.NET和Java的一些常用技術點做個記錄,由於沒有涉及開發大並發量的socket服務器端,對分包、粘包和一些高性能的要求都沒有分析過,本篇文章只以它們之間的常用使用方法以及Java的Socket轉換為C#代碼的方法作一定的記錄。

 

一、Java Socket
在JDK1.4之前,java只能以同步的方式創建socket,異步只能用多線程方式的異步,java.net.ServerSocket 包可以創建服務端的socket處理器,客戶端通過java.net.Socket 連接過來如:
    private ServerSocket server = null;
    private Socket socket = null; 
    InetAddress address = InetAddress.getByName("127.0.0.1");
server = new ServerSocket(3000,10000,address);
//監聽窗口,等待連接
socket = server.accept();
這樣服務端ServerSocket的accept()方法就會同步阻塞等待客戶端連接,在收到客戶端socket連接請求后,就會執行后續操作。服務端通過socket.getInputStream()來獲取客戶端輸入的數據流,通過對socket.getOutputStream()讀取流進行回寫,返回給客戶端信息。Socket客戶端可以用:
        Socket so = new Socket("127.0.0.1",3000);      
        InputStream is = so.getInputStream();     
        OutputStream os = so.getOutputStream();   
的方式建立socket連接,比如這里可以直接對os進行發送數據到服務端:os.write(sendByte)。
JDK1.4后,增加了NIO的包可以創建非阻塞的異步Socket服務端和客戶端,在java.nio包下java.nio.channels.ServerSocketChannel用於創建socket服務端,java.nio.channels.SocketChannel包用於創建socket接收端或客戶端,它們通過java.nio.channels.Selector管理NIO的ServerSocketChannel通道,Selector通過事件的方式響應客戶端的請求,為Selector注冊事件的方式如:
// 通過open()方法找到Selector
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
// 注冊接收請求事件到selector
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
事件還包括:SelectionKey.OP_READ讀取事件,SelectionKey.OP_WRITE寫操作事件,為serverSocketChannel注冊事件后,就可以寫一個循環,每當selector.select()接收到值時,就開始判斷是什么事件,然后按照事件執行具體邏輯代碼。建立異步Socket服務端代碼如:
serverSocketChannel = ServerSocketChannel.open();
//配置為非阻塞
serverSocketChannel.configureBlocking(false);
//獲取通道關聯的服務器套接字
ServerSocket serverSocket = serverSocketChannel.socket();
//服務端綁定IP端口
serverSocket.bind(new InetSocketAddress("127.0.0.1", 3000));
這樣通過serverSocketChannel建立了服務端監聽,然后用上面的Selector為serverSocketChannel添加注冊事件即可作為服務端監聽各種客戶端事件了。
Java中對TCP和UDP分別用不同的包來處理,UDP是用java.net.DatagramSocket包下的方法來處理。

 

二、.NET Socket
.NET下C#封裝的Socket在System.Net.Sockets命名空間下,里面異步、同步的方法均用Socket類即可,並且TCP,UDP的選擇直接根據 Socket的構造函數來確定,比如:Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ProtocolType可以選擇TCP還是UDP,當為UDP時,需要用SocketType.Dgram類型的數據格式。.NET里有封裝 TcpListener 和TcpClient來分別處理TCP請求的服務端和客戶端,UdpClient 類來處理UDP請求的服務端和客戶端,它們是基於Socket套接字的封裝。使用上TcpListener、TcpClient和Java的風格很像,通過tcpClient.GetStream()返回數據流再操作數據,而UdpClient是通過直接send,receive的方式發送接收數據。.NET的Socket服務器端建立監聽如:
IPEndPoint hostEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(hostEP);
socket.Listen(500); 
Socket client = socket.Accept();
通過Accept方法同步等待獲取一個連接的socket,當然也可以異步的方式等待客戶端的連接請求,有新請求后,就可以用這個socket來處理數據。它可以用同步阻塞接收方法,如:
public int Receive(byte[] buffer);
public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags);
等用於同步接收客戶端數據,也可以異步接收客戶端數據的方式:
可以APM方式:public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state);
或者事件方式:public bool ReceiveAsync(SocketAsyncEventArgs e);
處理數據的方式比如:
byte[] bytesSendStr = new byte[1024];
int bytes = client.Receive(bytesSendStr);
string sendStr = Encoding.UTF8.GetString(bytesSendStr, 0, bytes);
sendStr = “服務端返回:” + sendStr;
bytesSendStr = Encoding.UTF8.GetBytes(sendStr); 
client.Send(bytesSendStr, bytesSendStr.Length,SocketFlags.None);
服務端接收數據應該反復Receive客戶端數據,直到接收數據返回的整數小於1,防止沒有讀取完數據,並且讀取結果我覺得最好讀取到byte里,因為一般socket都可能會用byte的前面字段作為標識,比如標識收取數據的長度,數據類型等。
作為Socket服務端,一般都要能同時處理大量的客戶端socket請求,因此單線程來處理肯定不行,當每accept到一個客戶端請求后,就開啟一個線程來處理,存在大量線程開啟的問題,並且客戶端請求后發送數據和服務端返回數據等會導致服務端同步等待,造成線程消耗資源的浪費。因此一般服務端都會用異步的方式來處理客戶端的請求。

 

三、Java Socket和C# Socket的不同
一般它們的區別有:創建socket的方式不一樣;發送數據和接收數據方式不一樣;還有就是Java中一般是用方法的方式設置配置參數,.NET是以屬性的方式直接設置。
下面以TCP的方式說明幾個,如Java創建客戶端方式:
Socket socket = new Socket("127.0.0.1",3000);
 .NET創建客戶端方式:
Socket newclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//服務器的IP和端口
IPEndPoint ie = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000); 
newclient.Connect(ie);
Java發送接收數據方式:
Socket socket = new Socket(“127.0.0.1”,3000);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
//發送數據
os.write(sendByte);
//讀取服務端數據
returnStr = InputStreamTOString(is); 
.NET發送接收數據方式:
byte[] msg = Encoding.UTF8.GetBytes(content);
//newclient.ReceiveTimeout = 10000;
//直接發送
int bytesSent = newclient.Send(msg);
//直接接收服務端
int bytesRec = newclient.Receive(data);
設置Socket參數的方法,比如Java設置參數:
//讓socket及時發送,防止緩存數據不多時不發送
socket.setTcpNoDelay(true);
//接收數據超時時間
socket.setSoTimeout(30000);
.NET對應Java的上述設置為:
newclient.NoDelay = true;
newclient.ReceiveTimeout = 30000;
但如果為了把JAVA程序轉換為C#程序,並且代碼風格保持基本不變,可以用開源的一個項目:IKVM,它的主要一個類庫:IKVM.OpenJDK.Core.dll,內部的命名空間基本和JAVA的包名保持一致,幾乎一一對應,除了語法方面的寫法不同,其他JAVA類庫的寫法都一樣,因此寫的代碼幾乎可以和JAVA一樣。
上面粗略寫了JAVA和.NET的一些Socket知識,如有不對,歡迎指正!

 

 如轉載,請注明來自:http://lawson.cnblogs.com/ 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM