1. 程式人生 > 其它 >[Java 網路程式設計 03] TCP/UDP 基於socket的實現 2021.11.11

[Java 網路程式設計 03] TCP/UDP 基於socket的實現 2021.11.11

1. 網路程式設計要素:

  1. 如何定位到網路上的一臺或多臺主機 IP地址和埠號
  2. 找到後如何通訊 TCP/UDP
  3. JAVA,萬物皆物件,去找相應的類

2. IP地址

ip地址的包: InetAddress

  • 唯一定位一臺網路上的計算機

  • 本機localhost地址: 127.0.0.1

  • ip地址分類:

    • IPV4/IPV6
      • IPV4
      • IPV6
    • 公網-私網
      • ABCD類地址
      • 192.168.xxx.xxx, 給組織內部使用

3. InetAddress包

沒有建構函式,只能通過靜態方法返回物件

直接檢視java文件,獲取本機地址/域名地址等,

得到的返回值為一個物件,獲取他們的資訊

InetAddress localhost = InetAddress.getLocalHost();

localhost.getAddress();
localhost.getcanonicalHostName();
localhost.getHostAddress();
localhost.getHostName();

4. 埠

埠表示計算機上一個程式的程序

  • 不同的程序有不同的埠號,用來區分軟體

  • 埠被規定為0--65535

  • TCP/UDP個有65536個,TCP和UDP都有埠80,並且可以同時使用

  • 埠分類:

    • 公有埠:0--1023

      • HTTP 80, HTTPS 443
      • FTP 21, Telent 23
    • 程式註冊埠(分配給使用者或程式): 1024--49151

      • Tomcat: 8080
      • MySql: 3306
      • Oracle: 1521
    • 動態/私有: 49152-65535(不常用)

常用的Dos命令:

netstat -ano # 檢視所有的埠
netstat -ano | findstr 80 #檢視指定的埠(|為過濾的意思,把後面的條件帶入前面去查詢)
tasklist | findstr 8696 # 檢視指定埠的程序(去工作管理員檢視到PID之後去找)

本機host檔案目錄: C:\Windows\System32\drivers\etc

InetSocketAddress(ip, port)//有構造方法,可以被呼叫
//同樣的方法返回埠號,ip地址,hostname等

5. 通訊協議

TCP:使用者傳輸協議,UDP:使用者資料包協議,IP:網路互聯協議

  • TCP

    • 連線,穩定

    • 三次握手,四次揮手

      連線--斷開流程

      A:瞅啥
      B:瞅你咋地
      A:幹一場
      
      A:我要斷開了
      B: 我知道你要斷開了
      B: 你真的斷開了嗎
      A:我真的斷開了
      
    • 客戶端,服務端

    • 傳輸完成,釋放連線,效率低

  • UDP

    • 不連線,不穩定
    • 客戶端,服務端,沒有明確的界限
    • 不管有沒有準備好,都發過來

6. TCP通訊

  • 客戶端:
    • 連線伺服器 Socket
    • 傳送訊息(IO流)
  • 服務端:
    • 建立服務埠ServerSocket
    • 等待使用者連線 accept
    • 接受使用者訊息(IO流)
//客戶端程式碼:
public class TcpClientDemo01 {
    public static void main(String[] args){
        InetAddress ServerIP = null;
        Socket socket = null;
        OutputStream os = null;
        try {
            //1. 要知道服務其的地址和埠號
            ServerIP = InetAddress.getByName("127.0.0.1");
            int port = 9999;
            //2. 建立socket通訊
            socket = new Socket(ServerIP, port);
            //3. 連線完成後,傳送IO流
            os = socket.getOutputStream();
            os.write("whats the fuck".getBytes());

        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            if(os!= null) {
                try {
                    os.close();
                    socket.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}
//伺服器端程式碼:
public class TcpServerDemo01 {
    public static void main(String[] args) {
        ServerSocket serverSocket =null;
        Socket socket =null;
        InputStream is =null;
        ByteArrayOutputStream baos =null;
        try {
            //1. 建立客戶端要被連線的IP地址和埠號
            serverSocket = new ServerSocket(9999);
            //2. 等待客戶端連線
            socket = serverSocket.accept();
            //3. 讀取IO流
            is = socket.getInputStream();
            //輸入流可能亂碼,為防止,套接一個輸出流,C輸出,S輸入,套接一個頭輸出
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];//定義緩衝區
            int len;
            while((len = is.read(buffer))!=-1){//獲得buffer的每一個位置
                baos.write(buffer, 0, len-1);
            }
            System.out.println(baos.toString());
            //finally裡面關閉流等管道操作
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(baos != null) {
                try {
                    baos.close();
                    is.close();
                    socket.close();
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

7. TCP檔案傳輸(確定傳輸完成時間)管道判斷

client:! 檔案在module下面,不在src目錄下面, 在src上一級

public class TcpClient {
    public static void main(String[] args) throws Exception{
        //1.建立socket通訊
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
     	//2. 讀入檔案,寫出流
        File test = new File("test.jpg");
        FileInputStream fis = new FileInputStream(test);
        OutputStream fos = socket.getOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while((len = fis.read(buffer)) != -1){
            fos.write(buffer, 0, len);
        }
        //3. socket通知伺服器流寫出結束
        //通知伺服器傳送完成是由socket進行的,不是output流
        socket.shutdownOutput();
		//4. 建立輸入流,等待伺服器接收結束的通知
        InputStream fDidDown = socket.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer2 = new byte[1024];
        int len2;
        while((len2 = fDidDown.read(buffer2))!= -1){
            baos.write(buffer2, 0, len2);
        }
        System.out.println(baos.toString());
		//關閉
        baos.close();
        fDidDown.close();
        fos.close();
        fis.close();
        socket.close();
    }
}

server:

public class TcpServer {
    public static void main(String[] args) throws Exception {
        //1. 建立服務並監聽
        ServerSocket serverSocket = new ServerSocket(9999);
        Socket socket = serverSocket.accept();
        //2. 接收輸入並儲存檔案
        InputStream is = socket.getInputStream();
        OutputStream fout = new FileOutputStream(new File("receive.jpg"));
        byte[] buffer = new byte[1024];
        int len;
        while((len = is.read(buffer)) != -1){
            fout.write(buffer, 0, len);
        }
        //3. 輸出流通知客戶端輸出完畢
        OutputStream fHaveDone = socket.getOutputStream();
        fHaveDone.write("檔案接收完成".getBytes());
        //關閉
        fHaveDone.close();
        fout.close();
        is.close();
        socket.close();
        serverSocket.close();
    }
}

8. Tomcat

tomcat檔案目錄啟動start, 配置檔案修改,專案位置:webapps/root

9. UDP通訊

用到兩個包: DatagramSocket, DatagramPacket

重點:datagramPacket的構造方法,傳送端五個引數,接收端3個引數

sender:

  1. 建立socket連線 2. 新建一個數據包 3. 傳送 4. 關閉socket通訊
public class UdpSender {
    public static void main(String[] args) throws IOException {
        //1. 建立一個socket
        DatagramSocket socket = new DatagramSocket();
        //2. 新建一個包
        String Msg = "a UDP packet";
        byte[] Msg_Byte = Msg.getBytes();
        DatagramPacket dataPacket = new DatagramPacket(Msg_Byte, 0,
                Msg_Byte.length, InetAddress.getByName("127.0.0.1"), 9999);
        //3. 傳送包
        socket.send(dataPacket);
        //4. 關閉socket
        socket.close();
    }
}

receiver:

  1. 提供通訊 2. 接收資料 3. 操作資料 4. 關閉socket通訊
public class UdpReceiver {
    public static void main(String[] args) throws IOException {
        //1. 提供服務
        DatagramSocket socket = new DatagramSocket(9999);
        //2. 接收資料包
        byte[] buffer = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
        socket.receive(datagramPacket);
        //3. 操作資料
        System.out.println(new String(datagramPacket.getData(), 0, datagramPacket.getData().length));
        //4. 關閉socket
        socket.close();
    }
}

10. UDP 聊天實現

低版本: sender可以一直從鍵盤讀取,receiver 可以一直接收

Sender:

public class Sender {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(9999);//本地埠號
        while(true){
            //InputStreamReader引數是鍵盤輸入,把InputStream給bufferedReader, 然後讀取下一行
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            String Msg = bufferedReader.readLine();//獲取下一個輸入
            DatagramPacket datagramPacket = new DatagramPacket(//新建包,要連線的Socket在這裡打包
                    Msg.getBytes(), 0, Msg.getBytes().length, new InetSocketAddress("localhost", 9998));
            socket.send(datagramPacket);//傳送
            if (Msg.equals("bye")){
                break;
            }
        }
        socket.close();
        System.out.println("聊天結束");
    }
}

Receiver:

public class Receiver {
    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket(9998);//本地埠號
        while(true){
            //接收輸入
            byte[] buffer = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
            socket.receive(datagramPacket);
            //把輸入轉換為String
            String Msg_output = new String(datagramPacket.getData(), 0, datagramPacket.getData().length);
            System.out.println(Msg_output);
            if (Msg_output.equals("bye")){//有問題不能實現
                break;
            }
        }
        System.out.println("聊天結束");
        socket.close();
    }
}

11. 多執行緒UDP諮詢視窗

Java UPD的多執行緒線上諮詢聊天視窗的實現

先寫功能塊,寫一個傳送執行緒和接收執行緒,( 就是重構,抽取特徵,構建成類)

然後建立兩個使用者,實現這兩個方法

Sender

public class Sender implements Runnable{
    private int fromPort;
    private String toIP;
    private int toPort;
    DatagramSocket socket;
    BufferedReader bufferedReader;

    public Sender(int fromPort, String toIP, int toPort) {
        this.fromPort = fromPort;
        this.toIP = toIP;
        this.toPort = toPort;
        try{
            socket = new DatagramSocket(fromPort);
            bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        while(true){
            String Msg = null;
            try {
                Msg = bufferedReader.readLine();
                DatagramPacket datagramPacket = new DatagramPacket(
                        Msg.getBytes(), 0, Msg.getBytes().length, new InetSocketAddress(toIP, toPort));
                socket.send(datagramPacket);
                if (Msg.equals("bye")){
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        socket.close();
        System.out.println("聊天結束");
    }
}

Receiver

public class Receiver implements Runnable {
    DatagramSocket socket;
    private int myPort;

    public Receiver(int myPort) {
        this.myPort = myPort;
        try{
            socket = new DatagramSocket(myPort);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        while(true){
            byte[] buffer = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
            try {
                socket.receive(datagramPacket);
                String Msg_output = new String(datagramPacket.getData(), 0, datagramPacket.getData().length);
                int FromPort = datagramPacket.getPort();
                System.out.println("["+FromPort + "]" + Msg_output);
                if (Msg_output.equals("bye")){//又問題不能實現
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("聊天結束");
        socket.close();
    }
}

User1

public class User1 {
    public static void main(String[] args) {
        new Thread(new Sender(9991,"localhost",10002)).start();
        new Thread(new Receiver(10001)).start();
    }
}

User2

public class User2 {
    public static void main(String[] args) {
        new Thread(new Sender(9992,"localhost",10001)).start();
        new Thread(new Receiver(10002)).start();
    }
}

12. URL

統一資源定位符:

5部分組成:

#有五部分組成:
協議//:ip地址:埠/專案名稱/資源
URL rul = new URL("http://localhost:8080/helloworld/index.jsp?username=kk&password=123");
//很多方法
url.getHost();getProtocol();...

通過URL類下載東西

 public class UrlDownload {
    public static void main(String[] args) throws IOException {
        //下載地址
        URL url = new URL("http:loaclhost:8080/projectname/file.txt");
        //連線到這個資源
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        //獲得輸入流,寫到本地
        InputStream inputStream = httpURLConnection.getInputStream();
        FileOutputStream fos = new FileOutputStream("download.txt");
        byte[] buffer = new byte[1024];
        int len;
        while((len = inputStream.read(buffer))!=-1){
            fos.write(buffer, 0, len);
        }
        fos.close();
        inputStream.close();
        httpURLConnection.disconnect();//斷開連線
    }
}

(審查元素,去下載收費歌曲)