Java網(wǎng)絡(luò)編程基礎(chǔ)(BIO)

[TOC]

TCP/IP協(xié)議分層模型

  • TCP/IP分層模型
自頂向下 協(xié)議 功能 PDU(協(xié)議數(shù)據(jù)單元)
應(yīng)用層 HTTP危队、SSH射众、FTP话肖、TELNET等等 通過應(yīng)用進(jìn)程間的交互來完成特定網(wǎng)絡(luò)應(yīng)用 報文
運輸層 TCP剥纷、UDP 在應(yīng)用程序端點之間傳輸應(yīng)用層報文 報文段
網(wǎng)絡(luò)層 IP 將數(shù)據(jù)包從一臺主機(jī)傳輸?shù)搅硪慌_主機(jī) 數(shù)據(jù)報
鏈路層 以太網(wǎng)抒钱、wifi、電纜接入網(wǎng)的DOCSIS等 數(shù)據(jù)鏈路層將網(wǎng)絡(luò)層交下來的IP數(shù)據(jù)報組裝成幀鼎天,在兩個相鄰節(jié)點間的鏈路上傳送幀
物理層 與傳輸媒介相關(guān) 實現(xiàn)相鄰計算機(jī)節(jié)點之間比特流的透明傳送舀奶,盡可能屏蔽掉具體傳輸介質(zhì)和物理設(shè)備的差異 比特

下層提供接口供上層使用,為上層服務(wù)斋射。

Socket簡述

Socket是運輸層提供給應(yīng)用層的接口育勺,使得應(yīng)用層可以基于socket接口實現(xiàn)基于TCP/UDP協(xié)議的通信但荤。它處于應(yīng)用層和運輸層之間。Socket是面向應(yīng)用程序端點的涧至,封裝了IP地址和端口號纱兑,二者標(biāo)識了網(wǎng)絡(luò)主機(jī)上的某一個應(yīng)用程序,IP地址標(biāo)識網(wǎng)絡(luò)主機(jī)化借,端口號標(biāo)識主機(jī)上的應(yīng)用程序,這樣唯一指定了通信地址捡多,便可以發(fā)起應(yīng)用程序端之間的網(wǎng)絡(luò)通信蓖康。

在操作系統(tǒng)層面,socket是抽象成文件的垒手,它和文件相似蒜焊,都是數(shù)據(jù)來源,只不過socket底層還有網(wǎng)絡(luò)通信鏈路和各種底層協(xié)議的支持科贬,可以簡單的看成一個遠(yuǎn)端的文件泳梆,使用socket打開文件,對這個特殊的文件進(jìn)行讀寫操作和交互榜掌,從而達(dá)到通信(分享)的目的优妙。

基于BIO的TCP通信

當(dāng)客戶端想要打開一個連接到服務(wù)器的TCP/IP連接時,就要使用到Java Socket類憎账。socket類只需要被告知連接的IP地址和TCP端口套硼,其余的都有Java實現(xiàn)。

假如我們想要打開一個監(jiān)聽服務(wù)胞皱,來監(jiān)聽客戶端連接某些指定TCP端口的連接邪意,那就需要使用Java ServerSocket類。當(dāng)客戶端通過Socket連接服務(wù)器端的ServerSocket監(jiān)聽時反砌,服務(wù)器端會指定這個連接的一個Socket雾鬼,此時客戶端與服務(wù)器端間的通信就變成Socket與Socket之間的通信。

ServerSocket類

ServerSocket宴树,由其名稱即可知策菜,是作為服務(wù)端的socket對象,在網(wǎng)絡(luò)編程中森渐,它又稱為歡迎套接字做入,專門監(jiān)聽主機(jī)上的某個端口,如果有連接到此端口同衣,則產(chǎn)生一個與該連接相連的Socket類對象竟块。

Socket類是對網(wǎng)絡(luò)套接字的封裝,也即為主機(jī)應(yīng)用端通信接口的抽象(可能是遠(yuǎn)程也可能是本地耐齐,但是是通過網(wǎng)絡(luò)來通信的進(jìn)程)浪秘。

當(dāng)socket對象之間連接建立成功蒋情,進(jìn)程持有的socket即可以抽象為遠(yuǎn)端文件,通過對這個文件進(jìn)行讀寫進(jìn)行數(shù)據(jù)交換也就是所謂的通信耸携。

ServerSocket的使用目的是監(jiān)聽主機(jī)端口棵癣,接收到連接后返回一個Socket對象建立通信,這是基于TCP的可靠連接通信夺衍。UDP通信中無需雙方建立連接狈谊。

  • ServerSocket(縮寫成SS)的核心屬性:
    1. bindAddr, 宿主主機(jī)的IP地址
    2. port沟沙,監(jiān)聽的端口號
    3. backlog河劝,請求傳入連接隊列的最大長度
  • 核心API
  1. 實例化對象
// 創(chuàng)建一個未綁定的SS.此方法創(chuàng)建的SS需要調(diào)用bind方法綁定端口
public ServerSocket() throws IOException;

// 創(chuàng)建一個綁定到指定端口號的SS
// 端口號指定為0,則自動選擇
// backlog=50
public ServerSocket(int port) throws IOException {
        this(port, 50, null);
    }

public ServerSocket(int port, int backlog) throws IOException;

// 核心方法
// @param bindAddr the local InetAddress the server will bind to
// InetAddress 是對IP地址的封裝
public ServerSocket(int port, int backlog, InetAddress bindAddr) 
  throws IOException ;
  1. bind
    手動綁定到端
public void bind(SocketAddress endpoint) throws IOException
public void bind(SocketAddress endpoint, int backlog) throws IOException ;

endpoint是SocketAddress類型矛紫,使用其實現(xiàn)類InetSocketAddress來構(gòu)造地址(IP+Port)赎瞎。指定端地址的創(chuàng)建SS方法不需要再手動綁定。

  1. 監(jiān)聽連接
public Socket accept() throws IOException ;

阻塞方法颊咬,在連接到達(dá)之前一直阻塞务甥,有連接時返回一個對該連接抽象的Socket對象

  1. 其他
    獲取地址信息: getInetAddress(),getLocalPort(),getLocalSocketAddress()[IP+Port]
    檢查狀態(tài):isBound(),isClosed()
    關(guān)閉:close() (ServerSocket實現(xiàn)了Closeable接口)
    accept超時時間:setSoTimeout(int timeout),getSoTimeout()
  • 示例
public class TestServerSocket {

    private static final int PORT = 12345;
    private static final int BACKLOG = 100;

    public static void main(String[] args) throws IOException {
        ServerSocket ss = getServerSocket();
        System.out.println("server start...");
        while (true) {
            Socket socket = ss.accept();
            System.out.println("connect incoming: " 
                                        + socket.getRemoteSocketAddress());

        }
    }

    private static ServerSocket getServerSocket() throws IOException {
        ServerSocket ss = new ServerSocket(PORT);
        // or
        ss = new ServerSocket(PORT, BACKLOG);
        // or
        InetAddress bindAddr = InetAddress.getLocalHost();
        ss = new ServerSocket(PORT, BACKLOG, bindAddr);
        // or
        ss = new ServerSocket();
        SocketAddress endpoint = new InetSocketAddress("localhost", PORT);
        ss.bind(endpoint, BACKLOG);

        return ss;
    }
}

Socket類

A socket is an endpoint for communication between two machines.

  • Socket使用:
  1. 創(chuàng)建連接到遠(yuǎn)端端點的Socket對象(實例化和連接)
  2. 讀寫socket進(jìn)行面向流的通信
  • 創(chuàng)建socket(連接)
// 創(chuàng)建一個未連接的socket對象,可顯式調(diào)用connect連接
public Socket()喳篇;
// 指定代理創(chuàng)建一個未連接的socket對象敞临,可顯式調(diào)用connect連接
public Socket(Proxy proxy);
// 創(chuàng)建一個連接到指定地址的socket對象
public Socket(String host, int port)
        throws UnknownHostException, IOException杭隙;
public Socket(InetAddress address, int port) throws IOException哟绊;
// 同時為socket綁定本地地址,也可以顯式調(diào)用bind方法來綁定本地地址痰憎;默認(rèn)是自動分配(端口號)
public Socket(String host, int port, InetAddress localAddr,
                  int localPort) throws IOException 

/**
連接票髓、bind
*/
public void connect(SocketAddress endpoint) throws IOException ;
/*
* Connects this socket to the server with a specified timeout value.
* A timeout of zero is interpreted as an infinite timeout. The connection
* will then block until established or an error occurs.
*/
public void connect(SocketAddress endpoint, int timeout) throws IOException 铣耘;
// Binds the socket to a local address.
public void bind(SocketAddress bindpoint) throws IOException
  • 讀寫socket進(jìn)行通信
socket.getInputStream();
socket.getOutputStream();
  • 示例
    private static final int PORT = 12345;
    private static final String HOST = "localhost";
    private static final int LOCAL_PORT = 23456;

    public static void main(String[] args) throws IOException {
        Socket socket = getSocket();
        // communicate
        OutputStream os = socket.getOutputStream();
        os.write("hellosocket".getBytes());
        os.flush();
        socket.close();

    }

    private static Socket getSocket() throws IOException {
        Socket sock = new Socket();
        SocketAddress endpoint = new InetSocketAddress(HOST, PORT);
        sock.connect(endpoint);
        // or sock = new Socket(HOST, PORT);
        // or sock = new Socket(InetAddress.getLocalHost(), PORT);
        // or sock = new Socket(HOST, PORT, InetAddress.getLocalHost(),
        // LOCAL_PORT);
        return sock;
    }

基于BIO的UDP通信

UDP的工作方式與TCP相比略有不同洽沟。使用UDP通信時,在客戶端與服務(wù)器之間并沒有建立連接的概念蜗细,客戶端發(fā)送到服務(wù)器的數(shù)據(jù)裆操,服務(wù)器可能(也可能并沒有)收到這些數(shù)據(jù),而且客戶端也并不知道這些數(shù)據(jù)是否被服務(wù)器成功接收炉媒。當(dāng)服務(wù)器向客戶端發(fā)送數(shù)據(jù)時也是如此踪区。

正因為是不可靠的數(shù)據(jù)傳輸,UDP相比與TCP來說少了很多的協(xié)議開銷吊骤。

使用UDP通信不需要建立連接缎岗,沒有TCP三次握手的連接動作,只需要在通信時指定消息的接收者地址即可白粉,類似于短信传泊,交互需要發(fā)送-接收-發(fā)送鼠渺,每一步都是獨立的。

DatagramSocket

DatagramSocket類實現(xiàn)“UDP通信”眷细,包括客戶端和服務(wù)器端往堡。雖然UDP方式的網(wǎng)絡(luò)通訊不需要建立專用的網(wǎng)絡(luò)連接醒陆,但是畢竟還是需要發(fā)送和接收數(shù)據(jù)钓猬,DatagramSocket實現(xiàn)的就是發(fā)送數(shù)據(jù)時的發(fā)射器誊锭,以及接收數(shù)據(jù)時的監(jiān)聽器的角色。

  • 創(chuàng)建實例
// 創(chuàng)建一個自動分配本地端口的DatagramSocket
public DatagramSocket() throws SocketException ;
// 指定綁定的本地地址
public DatagramSocket(SocketAddress bindaddr) throws SocketException 校读;
// 指定本地綁定端口
public DatagramSocket(int port) throws SocketException 奔害;
//  指定本地綁定地址和端口
public DatagramSocket(int port, InetAddress laddr) throws SocketException;

  • bind
    public synchronized void bind(SocketAddress addr) throws SocketException

  • send
    發(fā)送信息

/**
     * Sends a datagram packet from this socket. The
     * {@code DatagramPacket} includes information indicating the
     * data to be sent, its length, the IP address of the remote host,
     * and the port number on the remote host.
     *
*/
public void send(DatagramPacket p) throws IOException

發(fā)送的目的地址在報文對象內(nèi)配置地熄。

  • receive
    /**
     * Receives a datagram packet from this socket. When this method
     * returns, the {@code DatagramPacket}'s buffer is filled with
     * the data received. The datagram packet also contains the sender's
     * IP address, and the port number on the sender's machine.
     * <p>
     * This method blocks until a datagram is received. The
     * {@code length} field of the datagram packet object contains
     * the length of the received message. If the message is longer than
     * the packet's length, the message is truncated.
     * <p>
    */
    public synchronized void receive(DatagramPacket p) throws IOException 

DatagramPacket

DatagramPacket類實現(xiàn)對于網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù)封裝,也就是說芯杀,該類的對象代表網(wǎng)絡(luò)中交換的數(shù)據(jù)端考。在UDP方式的網(wǎng)絡(luò)編程中,無論是需要發(fā)送的數(shù)據(jù)還是需要接收的數(shù)據(jù)揭厚,都必須被處理成DatagramPacket類型的對象却特,該對象中包含發(fā)送到的地址、發(fā)送到的端口號以及發(fā)送的內(nèi)容等筛圆。其實DatagramPacket類的作用類似于現(xiàn)實中的信件裂明,在信件中包含信件發(fā)送到的地址以及接收人,還有發(fā)送的內(nèi)容等太援,郵局只需要按照地址傳遞即可闽晦。在接收數(shù)據(jù)時,接收到的數(shù)據(jù)也必須被處理成DatagramPacket類型的對象提岔,在該對象中包含發(fā)送方的地址仙蛉、端口號等信息,也包含數(shù)據(jù)的內(nèi)容碱蒙。和TCP方式的網(wǎng)絡(luò)傳輸相比荠瘪,IO編程在UDP方式的網(wǎng)絡(luò)編程中變得不是必須的內(nèi)容,結(jié)構(gòu)也要比TCP方式的網(wǎng)絡(luò)編程簡單一些赛惩。

    /**
     * Constructs a {@code DatagramPacket} for receiving packets of
     * length {@code length}, specifying an offset into the buffer.
     * <p>
     * The {@code length} argument must be less than or equal to
     * {@code buf.length}.
     *
     * @param   buf      buffer for holding the incoming datagram.
     * @param   offset   the offset for the buffer
     * @param   length   the number of bytes to read.
     *
     * @since 1.2
     */
    public DatagramPacket(byte buf[], int offset, int length) {
        setData(buf, offset, length);
        this.address = null;
        this.port = -1;
    }

 
    public DatagramPacket(byte buf[], int length) {
        this (buf, 0, length);
    }

    /**
     * Constructs a datagram packet for sending packets of length
     * {@code length} with offset {@code ioffset}to the
     * specified port number on the specified host. The
     * {@code length} argument must be less than or equal to
     * {@code buf.length}.
     *
     * @param   buf      the packet data.
     * @param   offset   the packet data offset.
     * @param   length   the packet data length.
     * @param   address  the destination address.
     * @param   port     the destination port number.
     * @see java.net.InetAddress
     *
     * @since 1.2
     */
    public DatagramPacket(byte buf[], int offset, int length,
                          InetAddress address, int port) {
        setData(buf, offset, length);
        setAddress(address);
        setPort(port);
    }

    /**
     * Constructs a datagram packet for sending packets of length
     * {@code length} with offset {@code ioffset}to the
     * specified port number on the specified host. The
     * {@code length} argument must be less than or equal to
     * {@code buf.length}.
     *
     * @param   buf      the packet data.
     * @param   offset   the packet data offset.
     * @param   length   the packet data length.
     * @param   address  the destination socket address.
     * @throws  IllegalArgumentException if address type is not supported
     * @see java.net.InetAddress
     *
     * @since 1.4
     */
    public DatagramPacket(byte buf[], int offset, int length, 
                                      SocketAddress address) {
        setData(buf, offset, length);
        setSocketAddress(address);
    }

    public DatagramPacket(byte buf[], int length,
                          InetAddress address, int port) {
        this(buf, 0, length, address, port);
    }

    public DatagramPacket(byte buf[], int SocketAddress address) {
        this(buf, 0, length, address);
    }

指定address的用作發(fā)送的數(shù)據(jù)封裝哀墓,沒有address的用來封裝接收到的數(shù)據(jù)。

示例

  • UDPserver
/*
* UDP 服務(wù)端
*/

public class UDPServer {
    public static void main(String[] args) throws Exception {
        // 綁定端口用于在端口監(jiān)聽數(shù)據(jù)到來
        DatagramSocket ds = new DatagramSocket(12345);
        // 接收數(shù)據(jù)的buffer
        byte[] buf = new byte[1024];
        DatagramPacket dp = new DatagramPacket(buf, 1024);
        // 阻塞方法喷兼,阻塞直到有數(shù)據(jù)到達(dá)
        ds.receive(dp);
        // 解析數(shù)據(jù)
        String data = new String(dp.getData(), 0, dp.getLength());
        String ip = dp.getAddress().getHostAddress();
        int port = dp.getPort();

        System.out.println("ip地址:" + ip + " 端口號:" + port + " 消息:" + data);
        // 關(guān)閉socket
        ds.close();
    }
}

  • UDPclient
public class UDPClient {
    public static void main(String[] args) throws Exception {
        // 創(chuàng)建socket
        DatagramSocket ds = new DatagramSocket();
        String message = "Hello Java World!";
        // 封裝報文對象
        DatagramPacket dp = new DatagramPacket(message.getBytes(), 
                           message.length(), InetAddress.getByName("127.0.0.1"),
                12345);
        // 發(fā)送報文
        ds.send(dp);
        ds.close();
    }
}

參考資料
[1] Java網(wǎng)絡(luò)教程
[2] Java網(wǎng)絡(luò)編程:UDP通信

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末篮绰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子褒搔,更是在濱河造成了極大的恐慌阶牍,老刑警劉巖喷面,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異走孽,居然都是意外死亡惧辈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門磕瓷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盒齿,“玉大人,你說我怎么就攤上這事困食”呶蹋” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵硕盹,是天一觀的道長符匾。 經(jīng)常有香客問我,道長瘩例,這世上最難降的妖魔是什么啊胶? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮垛贤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘聘惦。我一直安慰自己某饰,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布善绎。 她就那樣靜靜地躺著黔漂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪禀酱。 梳的紋絲不亂的頭發(fā)上瘟仿,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機(jī)與錄音比勉,去河邊找鬼劳较。 笑死,一個胖子當(dāng)著我的面吹牛浩聋,可吹牛的內(nèi)容都是我干的观蜗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼衣洁,長吁一口氣:“原來是場噩夢啊……” “哼墓捻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤砖第,失蹤者是張志新(化名)和其女友劉穎撤卢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梧兼,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡放吩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了羽杰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渡紫。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖考赛,靈堂內(nèi)的尸體忽然破棺而出惕澎,到底是詐尸還是另有隱情,我是刑警寧澤颜骤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布唧喉,位于F島的核電站,受9級特大地震影響忍抽,放射性物質(zhì)發(fā)生泄漏欣喧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一梯找、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧益涧,春花似錦锈锤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扭弧,卻和暖如春阎姥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸽捻。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工呼巴, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人御蒲。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓衣赶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親厚满。 傳聞我的和親對象是個殘疾皇子府瞄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 計算機(jī)網(wǎng)絡(luò)概述 網(wǎng)絡(luò)編程的實質(zhì)就是兩個(或多個)設(shè)備(例如計算機(jī))之間的數(shù)據(jù)傳輸。 按照計算機(jī)網(wǎng)絡(luò)的定義碘箍,通過一定...
    蛋炒飯_By閱讀 1,224評論 0 10
  • 網(wǎng)絡(luò)編程 網(wǎng)絡(luò)編程對于很多的初學(xué)者來說遵馆,都是很向往的一種編程技能鲸郊,但是很多的初學(xué)者卻因為很長一段時間無法進(jìn)入網(wǎng)絡(luò)編...
    程序員歐陽閱讀 2,014評論 1 37
  • 1 網(wǎng)絡(luò)編程----TCPNo24 【 public class Server { public static...
    征程_Journey閱讀 1,258評論 0 4
  • 我學(xué)會了包餃子。奶奶包餃子有一手货邓,我是跟她學(xué)的秆撮。 我們先去菜場買了點面粉,這是做餃子的主要材料逻恐。然后像吻,我們要...
    施千惠閱讀 299評論 0 0
  • 不要像個落難者,告訴所有人你的不幸复隆〔Υ遥總有一天你會明白,你的委屈要自己消化挽拂,你的故事不用逢人就講起惭每,真正理解的你沒有...
    齊魯大哥閱讀 244評論 0 0