1. 程式人生 > >java學習day20--網路程式設計

java學習day20--網路程式設計

一.網路模型與協議:
OSI 七層模式 : 應用層,表示層,會話層,傳輸層,網路層,鏈路層,物理層
五層模型: 應用層, 傳輸層,網路層,鏈路層,物理層
四層模型 : 應用層, 傳輸層,網路層,鏈路層

應用層:http(超文字傳輸協議) ftp(檔案傳輸協議) stmp (郵件傳送協議) pop3(郵件接收協議), ssh ( 安全shell,用於遠端登入)
傳輸層: tcp(安全可靠的協議) udp(不可靠)
網路層:ip

關於ip地址,作用是用來定位到網路上的另一臺計算機
查詢:     windows下可以使用 ipconfig來檢視ip地址
          linux 下可以使用 ifconfig來檢視ip地址

關於埠:作用是用來標記,要訪問對方的哪個程式
常用的埠號:
mysql 3306
oracle 1521
sqlserver 1433
redis 6379
tomcat 8080
apache(http的服務) 80
ftp 21
ssh 22

二.傳輸層協議:
tcp協議:
TCP 協議的特點是: TCP 協議是一個有連線、可靠的協議。所謂有連線,指的是在進行 TCP通訊之前,兩個需要通訊的主機之間要首先建立一條資料通道,就好像打電話進行交流之前,首先要讓電話接通一樣。所謂可靠,指的是 TCP 協議能夠保證: 1、傳送端傳送的資料不會丟失; 2、接收端接受的資料包的順序,會按照發送端傳送的包的順序接受。也就是說, TCP協議能夠保證資料能夠完整無誤的傳輸。

udp協議:
與 TCP 協議相比, UDP 是一個無連線,不可靠的協議。 即:資料的傳送方只負責將資料傳送出去,資料的接受方只負責接受資料。傳送方和接收方不會相互確認資料的傳輸是否成功。
相對於 TCP 而言, UDP 有一個優點:效率較高。因此,當我們在對資料傳輸的正確率
不太關心,但是對傳輸效率要求較高的情況下,可以採用 UDP 協議。典型的使用 UDP 協議的是網路語音以及視訊聊天應用

三.java中的網路程式設計
Socket API 對tcp,udp協議做了封裝,能夠連線到對方主機,收發資料

tcp的例子:建立連線
伺服器端

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerTest {
    public static void main(String[] args) throws IOException {
        //傳一個埠號與客戶端進行連線,埠號一般使用4位以上的數字
        ServerSocket ss = new ServerSocket(12345);
        System.out.println("等待客戶端連線");
        //等待客戶端連線伺服器方法,直到有客戶端連線
        Socket socket = ss.accept();
        //將接收到的資料讀入
        InputStream is = socket.getInputStream();
        byte[] by = new byte[1024];
        while(true) {
            int len = is.read(by);
            if(-1 == len) {
                break;
            }
            System.out.println(new String(by, 0, len));
        }
        //關閉
        socket.close();
    }
}

客戶端

import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

public class UserTest {
    public static void main(String[] args) throws IOException {
        //因為連線的是本機,所以將本機地址以及要連線的伺服器埠號傳入
        Socket socket = new Socket("localhost", 12345);
        Scanner sc = new Scanner(System.in);
        while(true) {
            String line = sc.nextLine();
            socket.getOutputStream().write(line.getBytes());
        }
    }
}

上述程式只是進行一個伺服器和一個客戶端進行一次性對話,接下來實現支援多個客戶端連線的伺服器
只需要將伺服器端進行重寫

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class ServerTest {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(12345);
        //建立一個執行緒池
        ExecutorService threadPool = Executors.newCachedThreadPool();
        System.out.println("等待客戶端連線");
        while(true) {
            Socket socket = ss.accept();  //每連線一個新使用者呼叫一次accept()
            //讓執行緒池中的執行緒來處理
            threadPool.submit(() -> {
                try {
                    InputStream is = socket.getInputStream();  //讀取接收的資訊
                    InetAddress add = socket.getInetAddress();  //獲取資訊源的地址
                    byte[] by = new byte[1024];
                    while(true) {
                        int len = is.read(by);
                        if(-1 == len) {
                            break;
                        }
                        System.out.println(add + " " + new String(by, 0, len));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

上面的方式實現了多個客戶端向服務端傳送訊息的功能,但是隻有服務端自己能看到訊息,所以要進行改進,讓聊天室內所有的使用者都能看到訊息,即將伺服器端接收的訊息給所有客戶端傳送一份

伺服器端

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class ServerTest {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(12345);
        //建立一個執行緒池
        ExecutorService threadPool = Executors.newCachedThreadPool();
        //建立一個集合用來儲存所有的客戶端socket
        ConcurrentHashMap<Socket, InetAddress> map = new ConcurrentHashMap<>();
        System.out.println("等待客戶端連線");
        while(true) {
            //每接收一個新使用者呼叫一次accept()
            Socket socket = ss.accept();
            InetAddress add = socket.getInetAddress();
            //將新使用者新增到集合中
            map.put(socket, add);
            threadPool.submit(() -> {
               try {
                   InputStream is = socket.getInputStream();
                   byte[] by = new byte[1024];
                   while(true) {
                       int len = is.read(by);
                       if(-1 == len) {
                           break;
                       }
                       System.out.println(add + " " + new String(by, 0, len));
                        String content = add + " " + new String(by, 0, len);
                        //遍歷map集合,將內容寫到每個使用者中
                       for(Socket userSocket : map.keySet()) {
                           OutputStream os = userSocket.getOutputStream();
                           os.write(content.getBytes());
                       }
                   }
               } catch (Exception e) {
                   e.printStackTrace();
               }
            });
        }
    }
}

客戶端
 

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Scanner;

public class UserTest {
    public static void main(String[] args) throws IOException {
        //因為連線的是本機,所以將本機地址以及要連線的伺服器埠號傳入
        Socket socket = new Socket("localhost", 12345);
        Scanner sc = new Scanner(System.in);

        //建立一個執行緒,讀入傳送過來的訊息,並進行輸出
        new Thread(() -> {
            try {
                InputStream is = socket.getInputStream();
                byte[] by = new byte[1024];
                while(true) {
                    int len = is.read(by);
                    if(-1 == len) {
                        break;
                    }
                    System.out.println(new String(by, 0, len));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        while(true) {
            String line = sc.nextLine();
            socket.getOutputStream().write(line.getBytes());
        }
    }
}