1. 程式人生 > >Socket程式設計學習筆記(一)

Socket程式設計學習筆記(一)

一、什麼是Socket程式設計:

socket是一種最簡單的客戶機/伺服器通訊模式。即客戶程序向伺服器程序發出某種服務請求,伺服器響應該請求。如圖所示,同常,一個伺服器程序會同時為多個客戶程序服務,圖中的伺服器程序B1同時為客戶程序A1、A2和B2提供服務。
這裡寫圖片描述
Socket也稱為“套接字”,用於描述IP地址和埠,是一個通訊鏈的控制代碼。應用程式常通過“套接字”向網路發出請求或應答網路請求。它具有以下幾個特點:
①Socket是連線執行在網路上的2個程式間的雙向通訊的端點。
②網路通訊實際上Socket的通訊。
③通訊的兩端都有Socket,資料在兩個Socket間通過IO進行傳輸。

二、使用Socket進行網路通訊的過程

①伺服器程式將一個套接字繫結到一個特定的埠,並通過此套接字等待和監聽客戶端的連線請求。
②客戶程式根據伺服器程式所在的主機名和埠號發出連線請求。
③如果一切正常,伺服器接收連線請求,並獲得一個新的繫結到不同埠地址的套接字。
④客戶和伺服器通過連續讀寫套接字進行通訊。

這裡寫圖片描述

一般說來Socket程式設計又分為基於TCP的Socket程式設計和基於UDP的Socket程式設計

三、建立TCP服務端/客戶端:

建立TCP服務端
①建立一個ServerSocket物件
②呼叫accept()方法接收客戶端請求
③從Socket中獲取IO流
④對I/O流進行讀寫操作,完成與客戶端的互動
⑤關閉I/O流和Socket
建立TCP客戶端
①建立一個Socket物件
②從Socket中獲取IO流
③對IO流進行讀寫操作,完成與服務端的互動
④關閉IO流和Socket
強調:客戶端和服務端進行資料傳輸時,客戶端的輸入流對應服務端的輸出流,客戶端的輸出流對應服務端的輸入流。

//服務端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerSocketTest1 {

    public static void main(String[] args) throws IOException{
        ServerSocket serverSocket = new
ServerSocket(8080);//將服務端與8080埠繫結 Socket socket = serverSocket.accept();//監聽8080埠 System.out.println("ip:"+socket.getInetAddress()); System.out.println("port:"+socket.getPort()); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); String str = null; while((str = reader.readLine()) != null){ if(str.equals("quit"))//如果接受到的資訊是"quit",就退出 break; System.out.println("get:"+str); writer.println("server get it"); } serverSocket.close(); socket.close(); reader.close(); writer.close(); } }

這種寫法的問題是如果同時有多個客戶端訪問,就會出現排隊現象,非常影響效率。所以,我們可以使用執行緒的知識,每次有客戶連線,就線上程中處理該請求。修改後的服務端程式碼如下

//客戶端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerSocketTest {

    public static void main(String[] args)throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080,2);
        int flag = 0;
        System.out.println("server is ready");
        while(true){
            Socket socket = serverSocket.accept();
            System.out.println("Connect:"+socket.getLocalAddress());
            System.err.println("client id:"+(++flag));
            System.out.println("Client Port:"+socket.getPort());
            handleConnection(socket);//線上程中進行處理
        }
        //serverSocket.close();
    }

    private static void handleConnection(Socket socket)throws IOException{
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
                    String str = null;
                    while((str = reader.readLine()) != null){
                        if(str.equals("quit"))
                            break;
                        System.out.println("Receive:"+str);
                        writer.println("Server received");
                    }
                    socket.close();
                    reader.close();
                    writer.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
    }

}

四、基於UDP的Socket程式設計

1.建立傳送端:
①建立DatagramSocket物件,該端點建立時,系統會隨機分配一個埠,如果不想隨機配置,可以手動指定。
②將資料進行Packet包(DatagramPacket)封裝,必須要指定目的地址和埠
③通過Socket服務的Send方法將該包發出
④將Socket關閉
2.建立接收端:
①建立DatagramSocket物件,要監聽一個埠
②通過Socket的receive方法將資料存入資料包中
③通過資料包DatagramPacket的方法getData()、getAddress()、getPort()獲取包中的指定資訊。
④將socket關閉

//接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class SocketReceiveTest {

    public static void main(String[] args) throws IOException{
        DatagramSocket socket = new DatagramSocket(8080);//監聽8080埠
        byte[] bytes = new byte[100];
        DatagramPacket packet1 = new DatagramPacket(bytes, bytes.length);
        socket.receive(packet1);
        System.out.println(new String(packet1.getData()));
        String str = "hello smart";
        DatagramPacket packet2 = new DatagramPacket(str.getBytes(), str.length(), 
                InetAddress.getByName("localhost"), packet1.getPort());
        socket.send(packet2);//接收到訊息後返回內容,埠就是傳送端的傳送埠。
        socket.close();
    }

}
//傳送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class SocketSendTest {

    public static void main(String[] args) throws IOException{
        DatagramSocket socket = new DatagramSocket();//這裡可以不指定本方的傳送埠
        String str = "hello world";
        DatagramPacket packetSend = new DatagramPacket(str.getBytes(), str.length(), 
                InetAddress.getByName("localhost"), 8080);//指定傳送到localhost的8080埠
        System.out.println("send port:"+packetSend.getPort());
        socket.send(packetSend);
        byte[] bytes = new byte[100]; 
        DatagramPacket packetReceive = new DatagramPacket(bytes, bytes.length);
        socket.receive(packetReceive);
        System.out.println(new String(packetReceive.getData()));
        socket.close();
    }

}