Java 網(wǎng)絡編程一---Socket(Tcp+Udp)

更詳細網(wǎng)絡知識請點擊這里


兩臺計算機進行網(wǎng)絡通信,必須滿足一下三點

  • IP 地址:兩臺主機必須有唯一的標識
  • 協(xié)議:雙方必須擁有共同的語言,否則出現(xiàn)言語不通,無法交流。
  • 端口號:一臺主機上可以運行多個應用程序蕉世,如何辨別不同應用程序的通信,就需要端口號來區(qū)分

TCP/IP 協(xié)議

TCP/IP 協(xié)議是目前引用最為廣泛的協(xié)議婆硬,是以 TCP 和 IP 為基礎的不同層次上多個協(xié)議的集合狠轻,也稱 TCP/IP 協(xié)議族或協(xié)議棧

  • TCP:Transmission Control Protocol 傳輸控制協(xié)議
  • IP: Internet Protocol 互聯(lián)網(wǎng)協(xié)議

TCP/IP 模型

物理層——>鏈路層——>網(wǎng)絡層——>傳輸層——>引用層

  • 物理層是用戶最直觀接觸到的,比如網(wǎng)線彬犯、雙絞線向楼、網(wǎng)卡等
  • TCP/IP 協(xié)議實際上是在第4層傳輸層
  • 應用層有許多協(xié)議是大家熟知的:HTTP 超文本傳輸協(xié)議、FTP 文件傳輸協(xié)議(可進行文件上傳谐区、下載等傳輸)湖蜕、SMTP 簡單郵件傳輸協(xié)議、Trlnet 遠程登錄服務

IP 地址

為實現(xiàn)網(wǎng)絡中不同計算機之間的通信宋列,每臺計算機都必須有一個唯一的標識---IP地址(類似生活中手機通信昭抒,雙方就必須擁有各自的手機號才能通信)

  • IP 地址格式:數(shù)字型,如 192.168.0.1(類似手機號碼規(guī)定了11位)

端口

一臺主機可以使用多個應用程序炼杖,當某個應用與另一臺計算機通信時灭返,如何保證發(fā)送的消息被對面正確接收(比如說A計算機中QQ發(fā)送消息到B計算機QQ中,而不是B計算機的微信中)坤邪,使用端口就能保證熙含,每個應用都有各自的唯一端口號。

  • 用于區(qū)分不同應用程序
  • 端口號范圍為065535艇纺,其中01023為系統(tǒng)所保留
  • IP 地址和端口號組成了所謂的 Socket怎静,Socket是網(wǎng)絡上運行的程序之間雙向通信鏈路的終結點,是 TCP 和 UDP 的基礎
  • 常見默認協(xié)議端口黔衡,http:20消约,ftp:21,telnet:23

JAVA 中的網(wǎng)絡支持

針對網(wǎng)絡通信的不同層次员帮,Java 提供的網(wǎng)絡功能有四大類

  1. InetAddress:用于標識網(wǎng)絡上的硬件資源,即標識 IP 地址
  2. URL:統(tǒng)一資源定位符导饲,表示Internet 上某一資源的地址捞高,通過 URL 可以直接讀取或寫入網(wǎng)絡上的數(shù)據(jù)
  3. Socket:使用 TCP 協(xié)議實現(xiàn)網(wǎng)絡通信的 Socket 相關的類
  4. Datagram:使用 UDP 協(xié)議氯材,將數(shù)據(jù)保存在數(shù)據(jù)報中,通過網(wǎng)絡進行通信

InetAddress 類常用方法的使用

package com.fy.inetaddress;

import java.lang.reflect.Array;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

public class Demo1 {

    public static void main(String[] args) {
        try {
            //獲取本機的InetAddress實例
            InetAddress inetAddress = InetAddress.getLocalHost();
            System.out.println("計算機地址:"+inetAddress.getHostAddress());
            System.out.println("計算機名:"+inetAddress.getHostName());
            byte[] bytes = inetAddress.getAddress();//直接獲取字節(jié)數(shù)組形式的IP地址
            System.out.println("字節(jié)數(shù)組形式的IP"+Arrays.toString(bytes));
            System.out.println(inetAddress);//直接輸出InetAddtress實例
            
            System.out.println("---------------");
            //根據(jù)電腦名稱獲取InetAddress實例        
            InetAddress inetAddress2 = InetAddress.getByName("DESKTOP-FCI9M7Q");
            System.out.println("計算機地址:"+inetAddress2.getHostAddress());
            System.out.println("計算機名:"+inetAddress2.getHostName());
        
            System.out.println("---------------");
            //根據(jù)電腦IP地址也能獲取實例
            InetAddress inetAddress3 = InetAddress.getByName("192.168.56.1");
            System.out.println("計算機地址:"+inetAddress2.getHostAddress());
            System.out.println("計算機名:"+inetAddress2.getHostName());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

}

URL 類常用方法的使用

package com.fy.url;

import java.net.MalformedURLException;
import java.net.URL;

public class URLDemo {

    public static void main(String[] args) {
        try {
            //創(chuàng)建一個URL實例
            URL imooc = new URL("http://www.baidu.com");
            //通過一個存在的URL實例創(chuàng)建一個新的實例硝岗?表示后面的參數(shù)氢哮,#表示錨點
            URL url = new URL(imooc, "/index.html?username=tom#test");
            //獲取協(xié)議
            System.out.println(url.getProtocol());
            System.out.println(url.getHost());
            //獲取端口號,如果端口號未指定即使用的是默認端口號型檀,此時返回-1
            System.out.println("查詢端口:"+url.getPort());
            System.out.println("查詢文件路徑:"+url.getPath());
            System.out.println("查詢文件名:"+url.getFile());
            System.out.println("查詢相對路徑:"+url.getRef());
            System.out.println("查詢字符串:"+url.getQuery());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        
    }

}

package com.fy.url;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;

public class URLDemo2 {

    public static void main(String[] args) {
        try {
            URL url = new URL("http://www.baidu.com");
            //通過URL的openStream 方法獲取URL對象所表示的資源的字節(jié)輸入流
            InputStream is = url.openStream();
            //轉化為字符輸入流
            InputStreamReader isr = new InputStreamReader(is,"utf-8");
            //為字符輸入流添加緩沖
            BufferedReader br = new BufferedReader(isr);
            String data = br.readLine();//讀取數(shù)據(jù)
            while (data != null) {//循環(huán)讀取數(shù)據(jù)
                System.out.print(data);//輸出數(shù)據(jù)
                data = br.readLine();
            }
            br.close();
            isr.close();
            is.close();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

Tcp SocKet 通信

TCP 協(xié)議是面向連接冗尤、可靠、有序的胀溺,以字節(jié)流的方式發(fā)送數(shù)據(jù)基于 TCP 協(xié)議實現(xiàn)網(wǎng)絡通信的類:

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

Tcp Socket 通信實現(xiàn)步驟

  1. 創(chuàng)建 ServerSocket 和 Socket
  2. 打開連接到 Socket 的輸入/輸出流
  3. 按照協(xié)議對 Socket 進行讀/寫操作
  4. 關閉輸入輸出流裂七、關閉 Socket

服務器端

  1. 創(chuàng)建 ServerSocket 對象,綁定監(jiān)聽端口
  2. 通過 accept() 方法監(jiān)聽客戶端的請求
  3. 建立連接后仓坞,通過輸入流獲取客戶端的請求數(shù)據(jù)
  4. 通過輸出流向客戶端發(fā)送相應消息
  5. 關閉所有資源
package com.fy.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/*
 * 基于 TCP 協(xié)議的 Socket 通信背零,實現(xiàn)用戶登錄
 * 服務器端
 */
public class TcpServer {
    
    
    public static void main(String[] args) {
        try {
            // 1. 創(chuàng)建一個服務器端的ServerSocket,指定綁定端口并且監(jiān)聽
            ServerSocket serverSocket = new ServerSocket(6666);
            System.out.println("---服務器即將啟動,等待客戶端連接---");
            // 2. 調用 accept()方法監(jiān)聽客戶端請求
            Socket socket = serverSocket.accept();
            // 3. 利用輸入流讀取客戶端信息
            InputStream is = socket.getInputStream();//字節(jié)輸入流
            InputStreamReader isr = new InputStreamReader(is);//將字節(jié)流轉化為字符流
            BufferedReader br = new BufferedReader(isr);//為輸入字符流創(chuàng)建緩沖
            
            String info = null;
            while((info=br.readLine())!=null){//循環(huán)讀取客戶端的信息
                System.out.println("我是服務器无埃,客戶端說"+info);
            }
            socket.shutdownInput();// 關閉輸入流
            // 4.創(chuàng)建輸出流徙瓶,響應客戶端的請求
            OutputStream os = socket.getOutputStream();
            PrintWriter pw = new PrintWriter(os);//包裝為打印流(與輸出字符流類似)
            pw.write("服務器端說,歡迎你嫉称!");
            pw.flush();//調用 flush()方法刷新緩存
            
            // 5.關閉所有資源
            pw.close();
            os.close();
            br.close();
            isr.close();
            is.close();
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
}

客戶端

  1. 創(chuàng)建 Socket 對象侦镇,指明需要連接的服務器地址和端口號
  2. 連接建立后,通過輸出流向服務器發(fā)出需求
  3. 通過輸入流接收來自服務器的相應
  4. 關閉所有資源
package com.fy.socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

/*
 * 基于 TCP 協(xié)議的 Socket 通信织阅,實現(xiàn)用戶登錄
 * 客戶端
 */
public class TcpClient {
    public static void main(String[] args) {
        try {
            // 1.創(chuàng)建一個Socket壳繁,指定服務器地址和端口
            Socket socket = new Socket("localhost", 6666);
            // 2.創(chuàng)建一個輸出流向服務器發(fā)送消息
            OutputStream os = socket.getOutputStream();// 創(chuàng)建輸出字節(jié)流
            OutputStreamWriter osw = new OutputStreamWriter(os);//轉化為字符流
            BufferedWriter bw = new BufferedWriter(osw);//創(chuàng)建緩沖流
            bw.write("用戶名:admin;密碼:123");
            bw.flush();
            socket.shutdownOutput();// 關閉輸出流
            // 3. 創(chuàng)建輸入流蒲稳,接受服務器響應
            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);
            }
            // 4.關閉所有資源
            br.close();
            isr.close();
            is.close();
            bw.close();
            os.close();
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

利用多線程實現(xiàn)多客戶端的通信

  1. 服務器端穿件 ServerSocket氮趋,循環(huán)調用 accept() 方法等待客戶端連接
  2. 客戶端創(chuàng)建一個 Socket 并請求和服務器端連接
  3. 服務器端接受客戶端請求后,創(chuàng)建 socket 與該客戶建立專線連接
  4. 建立連接的兩個 socket 在一個單獨的線程進行通信
  5. 服務器端繼續(xù)等待新的連接
線程代碼
package com.fy.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

/*
 * 服務器線程處理類
 */
public class TcpServerThread extends Thread {
    // 和本線程相關的Socket
    Socket socket = null;

    // 構造方法初始化socket
    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    // 線程操作執(zhí)行響應客戶端請求
    public void run() {
        InputStream is = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        OutputStream os = null;
        PrintWriter pw = null;
        try {
            // 創(chuàng)建輸入流江耀,響應客戶端的請求
            is = socket.getInputStream();
            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);

            String info = null;
            while ((info = br.readLine()) != null) {// 循環(huán)讀取客戶端的信息
                System.out.println("我是服務器剩胁,客戶端說" + info);
            }
            socket.shutdownInput();// 關閉輸入流
            os = socket.getOutputStream();
            pw = new PrintWriter(os);
            pw.write("服務器端說,歡迎你祥国!");
            pw.flush();// 調用 flush()方法刷新緩存
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 關閉所有資源
                if (pw != null) {
                    pw.close();
                }
                if (os != null) {
                    os.close();
                }
                if (br != null) {
                    br.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (is != null) {
                    is.close();
                }
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

同時將服務器該為如下代碼
package com.fy.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/*
 * 基于 TCP 協(xié)議的 Socket 通信昵观,實現(xiàn)用戶登錄
 * 服務器端
 */
public class TcpServer {
    
    
    public static void main(String[] args) {
        try {
            // 創(chuàng)建一個服務器端的ServerSocket,指定綁定端口并且監(jiān)聽
            ServerSocket serverSocket = new ServerSocket(6666);
            Socket socket = null;
            int count = 0;
            System.out.println("---服務器即將啟動,等待客戶端連接---");
            // 利用while循環(huán)監(jiān)聽
            while(true){
                 // 調用 accept()方法監(jiān)聽,等待客戶端請求
                 socket = serverSocket.accept();
                 // 創(chuàng)建一個新的線程
                 TcoServerThread sThread = new TcpServerThread(socket);
                 // 啟動一個線程
                 sThread.start();
                 count++;// 統(tǒng)計客戶端的數(shù)量
                 System.out.println("客戶端的數(shù)量:"+count);
                 //創(chuàng)建InetAdress對象舌稀,以獲取客戶端IP地址
                 InetAddress address = socket.getInetAddress();
                 System.out.println("當前客戶端的Ip地址:"+ address.getHostAddress());
            }
        
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末啊犬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子壁查,更是在濱河造成了極大的恐慌觉至,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睡腿,死亡現(xiàn)場離奇詭異语御,居然都是意外死亡峻贮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門应闯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纤控,“玉大人,你說我怎么就攤上這事碉纺〈颍” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵骨田,是天一觀的道長耿导。 經(jīng)常有香客問我,道長盛撑,這世上最難降的妖魔是什么碎节? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮抵卫,結果婚禮上狮荔,老公的妹妹穿的比我還像新娘。我一直安慰自己介粘,他們只是感情好殖氏,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著姻采,像睡著了一般雅采。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上慨亲,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天婚瓜,我揣著相機與錄音,去河邊找鬼刑棵。 笑死巴刻,一個胖子當著我的面吹牛,可吹牛的內容都是我干的蛉签。 我是一名探鬼主播胡陪,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碍舍!你這毒婦竟也來了柠座?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤片橡,失蹤者是張志新(化名)和其女友劉穎妈经,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡狂塘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年录煤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荞胡。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖了嚎,靈堂內的尸體忽然破棺而出泪漂,到底是詐尸還是另有隱情,我是刑警寧澤歪泳,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布萝勤,位于F島的核電站,受9級特大地震影響呐伞,放射性物質發(fā)生泄漏敌卓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一伶氢、第九天 我趴在偏房一處隱蔽的房頂上張望趟径。 院中可真熱鬧,春花似錦癣防、人聲如沸蜗巧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幕屹。三九已至,卻和暖如春级遭,著一層夾襖步出監(jiān)牢的瞬間望拖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工挫鸽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留说敏,地道東北人。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓掠兄,卻偏偏與公主長得像像云,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蚂夕,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內容