02 客戶端

目錄

一  介紹Socket

二:Socket對象

  1 創建Socket

   2 常用方法

三  客戶端和服務端例子


在Java中使用Socket套接字編寫網絡通信。Socket是頂層的抽象對象。客戶端使用Socket,服務端使用ServerSocket

要實現網絡通信有三個要素

 IP地址

 端口號

 協議

一  介紹Socket

 1:Socket可以看成一個操作讀/寫字節的流。

      流的兩端是客戶端和服務端,流的通道就是網絡中的傳輸層(如TCP)。流傳輸的就是數據包。我們操作Socket就是實現服務端和客戶端直接的通信。要實現通信有七個步驟

   a:連接遠程主機

   b:發送數據

   c:就收數據

   d:關閉數據

   e:綁定端口

    f:監聽入站數據

    g:在綁定的端口上接受遠程主機的連接

 客戶端和服務端都有前四步,後三步是服務端纔有

二:Socket對象

  1 創建Socket

Socket類的構造函數

 不同的構造函數,都有一樣的功能那就是創建客戶端Socket對象,實現連接。

 如 有參構造函數 Socket socket = new Socket(String,int):指定ip,和端口號

 如  無參構造函數 Socket socket = new Socket()

      SocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), port);

      socket.connect(address )

    使用無參構造函數的優點

    a:可以使用一個socket重複連接 只要設置不同的SocketAddress

    b:關閉socket的時候不需要判斷 !=null 當然還有一種方式使用try()關閉的方式

         Socket socket;

          try(socket = new Socket(String,int)){

              }catch(){

               }

     這種方式必須要JDK1.7或者以上版本。而且不需要手動關閉的對象必須實現java.io.Closeable接口 滿足這些條件就可以在try()中創建那些自動關閉的對象。

如 使用代理 Socket(Proxy)

   2 常用方法

     獲取Socket信息

   

   getInetAddress()和getPort()獲取我們要遠程連接的主機的地址和端口號

   getLocalAddress()和getLocalPort()獲取本地主機的地址和端口號。

  判斷連接還是關閉

  

 isConnected():表示是否有過連接,不是表示正在連接。可能有過連接,但是斷開了。

 isClosed():表示是否關閉:如果一次都沒有連接 這個返回false

 所以萬全的判斷方式 if(socket. isConnected() && socket.isClosed()){ //連接中}

Socket設置

TcpNoDelay

禁用納格算法,將數據立即發送出去。納格算法是以減少封包傳送量來增進TCP/IP網絡的效能,當我們調用下面代碼,如:

Socket socket = new Socket();  
socket.connect(new InetSocketAddress(host, 8000));  
InputStream in = socket.getInputStream();  
OutputStream out = socket.getOutputStream();  
String head = "hello ";  
String body = "world\r\n";  
out.write(head.getBytes());  
out.write(body.getBytes()); 

我們發送了hello,當hello沒有收到ack確認(TCP是可靠連接,發送的每一個數據都要收到對方的一個ack確認,否則就要重發)的時候,根據納格算法,world不會立馬發送,會等待,要麼等到ack確認(最多等100ms對方會發過來的),要麼等到TCP緩衝區內容>=MSS,很明顯這裏沒有機會,我們寫了world後再也沒有寫數據了,所以只能等到hello的ack我們纔會發送world,除非我們禁用納格算法,數據就會立即發送了。

納格算法參考:http://zh.wikipedia.org/wiki/%E7%B4%8D%E6%A0%BC%E7%AE%97%E6%B3%95

另外有一篇講解納格算法和delay ack的文章(挺不錯的):http://blog.csdn.net/frankggyy/article/details/6624401  

SoLinger

當我們調用socket.close()返回時,socket已經write的數據未必已經發送到對方了,例如

Socket socket = new Socket();  
socket.connect(new InetSocketAddress(host, 8000));  
InputStream in = socket.getInputStream();  
OutputStream out = socket.getOutputStream();  
String head = "hello ";  
String body = "world\r\n";  
out.write(head.getBytes());  
out.write(body.getBytes()); 
socket.close();

這裏調用了socket.close()返回時,hello和world未必已經成功發送到對方了,如果我們設置了linger而不小於0,如:

bool on = true;
int linger = 100;
....
socket.setSoLinger(boolean on, int linger)
......
socket.close();

那麼close會等到發送的數據已經確認了才返回。但是如果對方宕機,超時,那麼會根據linger設定的時間返回。
UrgentData和OOBInline

TCP的緊急指針,一般都不建議使用,而且不同的TCP/IP實現,也不同,一般說如果你有緊急數據寧願再建立一個新的TCP/IP連接發送數據,讓對方緊急處理。

所以這兩個參數,你們可以忽略吧,想知道更多的,自己查下資料。

SoTimeout

設置socket調用InputStream讀數據的超時時間,以毫秒爲單位,如果超過這個時候,會拋出java.net.SocketTimeoutException。

KeepAlive

keepalive不是說TCP的常連接,當我們作爲服務端,一個客戶端連接上來,如果設置了keeplive爲true,當對方沒有發送任何數據過來,超過一個時間(看系統內核參數配置),那麼我們這邊會發送一個ack探測包發到對方,探測雙方的TCP/IP連接是否有效(對方可能斷點,斷網),在Linux好像這個時間是75秒。如果不設置,那麼客戶端宕機時,服務器永遠也不知道客戶端宕機了,仍然保存這個失效的連接。

SendBufferSize和ReceiveBufferSize

TCP發送緩存區和接收緩存區,默認是8192,一般情況下足夠了,而且就算你增加了發送緩存區,對方沒有增加它對應的接收緩衝,那麼在TCP三握手時,最後確定的最大發送窗口還是雙方最小的那個緩衝區,就算你無視,發了更多的數據,那麼多出來的數據也會被丟棄。除非雙方都協商好

當然我們也可以通過getXXX後去對應的參數值

三  客戶端和服務端例子

客戶端

package com.caoyong.socket;

import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
/**
 * 
 * Description:客戶端
 * @author CaoYong
 * @time 2018年9月4日 下午9:23:55
 */
public class Client {
	
	public void start() {
		//端口號
		Integer port = 8002;
		Socket client = new Socket();
		SocketAddress address;
		OutputStream out;
		try {
			//綁定ip和端口
			address = new InetSocketAddress(InetAddress.getLocalHost(), port);
			//連接 設置連接時間10秒 超過會報異常
			client.connect(address,10000);
			//設置超時時間 10秒
			client.setSoTimeout(10000);
			//獲取字節流
			out = client.getOutputStream();
			byte[] b = {11,22,33};
		    out.write(b);
		    //刷新
		    out.flush();
		    //關閉
		    client.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		new Client().start();
	}
}

 

服務端

package com.caoyong.socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

/**
 * 
 * Description:服務端
 * @author CaoYong
 * @time 2018年9月4日 下午9:24:15
 */
public class Server {
	
	public void start() {
		//端口號
		Integer port =8002;
		//服務端ServerSocket對象
		SocketAddress adderss;
		InputStream in;
		try {
			ServerSocket socket = new ServerSocket();
			adderss = new InetSocketAddress(port);
			//綁定端口
			socket.bind(adderss);
			//接受 並創建Socket
			Socket server = socket.accept();
			//底層流 可以在外層包裝其他流
			in = server.getInputStream();
			byte[] info = new byte[3];
			 while(in.read(info) != -1) {
				 for(byte b : info) {
					System.out.println("客戶端信息:"+b); 
				 }
			 }
			 //關閉
			 socket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		new Server().start();
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章