1. 程式人生 > >Java Socket編程基礎篇

Java Socket編程基礎篇

位置 socket原理 套接字 緩沖 args 系統 nts baidu post

原文地址:Java Socket編程----通信是這樣煉成的

Java最初是作為網絡編程語言出現的,其對網絡提供了高度的支持,使得客戶端和服務器的溝通變成了現實,而在網絡編程中,使用最多的就是Socket。像大家熟悉的QQ、MSN都使用了Socket相關的技術。下面就讓我們一起揭開Socket的神秘面紗。

Socket編程

網絡基礎知識點:

  1. 兩臺計算機間進行通訊需要以下三個條件
    IP地址、協議、端口號:
    • IP地址:定位應用所在機器的網絡位置。(比如家庭住址:北京市朝陽區XX街道XX小區)
    • 端口號:唯一識別到機器的指定應用。(門牌號)
    • 協議:數據傳輸的形式。(TCP、UDP等)
  2. IP地址

    為實現網絡中不同計算機之間的通信,每臺計算機都必須有一個唯一的標識---IP地址。

  3. 端口
    區分一臺主機的多個不同應用程序,端口號範圍為0-65535,其中0-1023位為系統保留。
    如:HTTP:80 FTP:21 Telnet:23
    IP地址+端口號組成了所謂的Socket,Socket是網絡上運行的程序之間雙向通信鏈路的終結點,是TCP和UDP的基礎。

  4. Socket套接字:
    網絡上具有唯一標識的IP地址和端口組合在一起才能構成唯一能識別的標識符套接字。

  5. Socket原理機制:
    通信的兩端都有Socket。
    網絡通信其實就是Socket間的通信。
    數據在兩個Socket間通過IO傳輸 。

  6. Java中的網絡支持
    針對網絡通信的不同層次,Java提供了不同的API,其提供的網絡功能有四大類:
    • InetAddress:用於標識網絡上的硬件資源,主要是IP地址。
    • URL:統一資源定位符,通過URL可以直接讀取或寫入網絡上的數據。
    • Sockets:使用TCP協議實現的網絡通信Socket相關的類
    • Datagram:使用UDP協議,將數據保存在用戶數據報中,通過網絡進行通信。

InetAddress

InetAddress類代表了一個網絡目標地址,包括主機名和IP地址信息。
該類沒有任何構造方法。

// 獲得本機的 主機名和IP地址
InetAddress address = InetAddress.getLocalHost
(); String hostName = address.getHostName();//獲取計算機名(本機主機名就是計算機名) String hostAddress = address.getHostAddress();//獲取IP地址 System.out.println("hostName: " + hostName +" &hostAddress:" + hostAddress); // 獲取遠程機器的 主機名和IP地址 InetAddress addr = InetAddress.getByName("www.baidu.com"); // 這裏不要加協議 比如:http://www.baidu.com String domainName = addr.getHostName();//獲得主機名 String ipName = addr.getHostAddress();//獲得IP地址 System.out.println("domainName:" + domainName + " &ipName:" + ipName);

使用URL網絡資源

通過URL對象的openStream()方法可以得到指定資源的輸入流,通過流能夠讀取或訪問網頁上的資源。

//使用URL讀取網頁內容
URL url = new URL("http://www.baidu.com");
InputStream is = url.openStream();//通過openStream方法獲取資源的字節輸入流
InputStreamReader isr = new InputStreamReader(is,"UTF-8");//將字節輸入流轉換為字符輸入流,如果不指定編碼,中文可能會出現亂碼
BufferedReader br = new BufferedReader(isr);//為字符輸入流添加緩沖,提高讀取效率
String data = br.readLine();//讀取數據
while(data!=null){
    System.out.println(data);//輸出數據
    data = br.readLine();
}
br.close();
is.close();

TCP編程模型

TCP協議是面向連接的、可靠的、有序的、以字節流的方式發送數據,通過三次握手方式建立連接,形成傳輸數據的通道,在連接中進行大量數據的傳輸,效率會稍低。

Java中基於TCP協議實現網絡通信的類:

  • 客戶端的Socket類。
  • 服務器端的ServerSocket類。

技術分享圖片

Socket通信的步驟:

  • 創建ServerSocket和Socket。
  • 打開連接到Socket的輸入和輸出流。
  • 按照TCP協議對Socket進行讀寫操作。
  • 關閉輸入輸出流,關閉Socket。

服務器端:

  • 創建ServerSocket對象,綁定監聽端口。
  • 通過accept()方法監聽客戶端請求。
  • 連接建立後,通過輸入流讀取客戶端發送的請求信息。
  • 通過輸出流向客戶端發送響應信息。
  • 關閉相關資源。
/**
 * 基於TCP協議的Socket通信,實現用戶登錄,服務端
 */
//1、創建一個服務器端Socket,即ServerSocket,指定綁定的端口,並監聽此端口
ServerSocket serverSocket = new ServerSocket(10086);//1024-65535的某個端口
//2、調用accept()方法開始監聽,等待客戶端的連接
Socket socket = serverSocket.accept();
//3、獲取輸入流,並讀取客戶端信息
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String info = null;
while ((info = br.readLine()) != null) {
    System.out.println("我是服務器,客戶端說:" + info);
}
socket.shutdownInput();//關閉輸入流
//4、獲取輸出流,響應客戶端的請求
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("歡迎您!");
pw.flush();

//5、關閉資源
pw.close();
os.close();
br.close();
isr.close();
is.close();
socket.close();

客戶端:

  • 創建Socket對象,指明需要連接的服務器的地址和端口號。
  • 連接建立後,通過輸出流向服務器端發送請求信息。
  • 通過輸入流獲取服務器響應的信息
  • 關閉相關資源。
//1、創建客戶端Socket,指定服務器地址和端口
Socket socket = new Socket("localhost",10086);
//2、獲取輸出流,向服務器端發送信息
OutputStream os = socket.getOutputStream();//字節輸出流
PrintWriter pw = new PrintWriter(os);//將輸出流包裝成打印流
pw.write("用戶名:admin;密碼:123");
pw.flush();
socket.shutdownOutput();
//3、獲取輸入流,並讀取服務器端的響應信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
while((info=br.readLine())!= null){
    System.out.println("我是客戶端,服務器說:"+info);
}

//4、關閉資源
br.close();
is.close();
pw.close();
os.close();
socket.close();

多線程實現服務器與客戶端之間的通信。

服務器端循環調用accept()等待客戶端連接。服務器端接收到數據後即交給一個線程進行異步處理,然後再次等待客戶端連接。

static ExecutorService executor = Executors.newFixedThreadPool(10);

public static void main(String[] args) throws Exception {

    /**
     * 基於TCP協議的Socket通信,實現用戶登錄,服務端
     */
    //1、創建一個服務器端Socket,即ServerSocket,指定綁定的端口,並監聽此端口
    ServerSocket serverSocket = new ServerSocket(10086);//1024-65535的某個端口
    //2、調用accept()方法開始監聽,等待客戶端的連接

    while(true) {
        executor.execute(new ServerTask(serverSocket.accept()));
    }
}


static class ServerTask implements Runnable {

    Socket socket ;

    public ServerTask(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            //3、獲取輸入流,並讀取客戶端信息
            InputStream is = socket.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String info = null;
            while ((info = br.readLine()) != null) {
                System.out.println("我是服務器,客戶端說:" + info);
            }
            socket.shutdownInput();//關閉輸入流
            //4、獲取輸出流,響應客戶端的請求
            OutputStream os = socket.getOutputStream();
            PrintWriter pw = new PrintWriter(os);
            pw.write("歡迎您!");
            pw.flush();

            // demo 就不放在finally裏關閉了
            is.close();
            isr.close();
            os.close();
            pw.close();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}

UDP編程模型

UDP協議(用戶數據報協議)是無連接的、不可靠的、無序的,速度快。

進行數據傳輸時,首先將要傳輸的數據定義成數據報(Datagram),大小限制在64k,在數據報中指明數據索要達到的Socket(主機地址和端口號),然後再將數據報發送出去。

DatagramPacket類:表示數據報包
DatagramSocket類:進行端到端通信的類

服務器端實現步驟:

 + 創建DatagramSocket,指定端口號
 + 創建DatagramPacket
 + 接受客戶端發送的數據信息
 + 讀取數據
//服務器端,實現基於UDP的用戶登錄

//1、創建服務器端DatagramSocket,指定端口
DatagramSocket socket =new DatagramSocket(10010);

//2、創建暫存的數據報文,用於接受客戶端發送的數據
byte[] data = new byte[1024];
DatagramPacket packet =new DatagramPacket(data,data.length);

//3、接受客戶端發送的數據
socket.receive(packet);//此方法在接受數據報之前會一致阻塞

//4、讀取數據
String info = new String(data);
System.out.println("我是服務器,客戶端告訴我:"+info);

//向客戶端響應數據

//1、定義客戶端的地址、端口號、數據
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "歡迎您!".getBytes();
//2、創建數據報,包含響應的數據信息
DatagramPacket packet2 = new DatagramPacket(data2,data2.length,address,port);
//3、響應客戶端
socket.send(packet2);
//4、關閉資源
socket.close();

客戶端實現步驟:

  • 定義發送消息。
  • 創建DatagramPacket,包含將要發送的消息。
  • 創建DatagramSocket。
  • 發送數據。
//客戶端
//1、定義服務器的地址、端口號、數據
InetAddress address = InetAddress.getByName("localhost");
int port = 10010;
byte[] data = "用戶名:admin;密碼:123".getBytes();
//2、創建數據報,包含發送的數據信息
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
//3、創建DatagramSocket對象
DatagramSocket socket = new DatagramSocket();
//4、向服務器發送數據
socket.send(packet);

//接受服務器端響應數據
//======================================
//1、創建數據報,用於接受服務器端響應數據
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
//2、接受服務器響應的數據
socket.receive(packet2);
String raply = new String(data2, 0, packet2.getLength());
System.out.println("我是客戶端,服務器說:" + raply);
//4、關閉資源
socket.close();

註意問題:

1、多線程的優先級問題:
根據實際的經驗,適當的降低優先級,否側可能會有程序運行效率低的情況
2、是否關閉輸出流和輸入流:
對於同一個socket,如果關閉了輸出流,則與該輸出流關聯的socket也會被關閉,所以一般不用關閉流,直接關閉socket即可
3、使用TCP通信傳輸對象,IO中序列化部分
4、socket編程傳遞文件,IO流部分

Java Socket編程基礎篇