網(wǎng)絡那些事(1)——認知Socket

相信能夠認知到socket層面的大家,至少對網(wǎng)絡的分層有一定的了解。這里便簡單的呈現(xiàn)一張經(jīng)典的網(wǎng)絡協(xié)議的層級圖方便對應理解。

OSI網(wǎng)絡模型

在Java(Kotlin)/Android中,Socket是對TCP/UDP層協(xié)議的封裝接口,負責完成三次握手的連接建立,我們使用socket可以輕易的完成在C/S模式(即客戶端/服務器端)下數(shù)據(jù)的簡單傳輸。下面我們首先分析一下服務器端Socket的:1.監(jiān)聽端口;2.獲取客戶端發(fā)來的輸入流;3.向客戶端發(fā)送服務器端的輸出流。

object Main {
    @JvmStatic fun main(args: Array<String>) {
        val server = SfServer()//初始化Server,開始監(jiān)聽
        server.startListen()
    }
}

import java.net.ServerSocket
class SfServer {
    var serverSocket: ServerSocket? = null//在服務端要使用ServerSocket而不是Socket
    fun startListen() {
        try {
            serverSocket = ServerSocket(9000)//實例化ServerSocket的過程是一個阻塞的過程,只有綁定成功或者失敗才會繼續(xù)執(zhí)行
            System.out.println("監(jiān)聽端口9000成功")
            while (true) {
                val socket = serverSocket!!.accept()//accept是監(jiān)聽端口,同樣是阻塞的,只有監(jiān)聽到來訪者才會繼續(xù)執(zhí)行
                val dealThread = Thread(DealClientSocket(socket))//當執(zhí)行到這一行說明accept已監(jiān)聽到來訪者,這時獲取來訪者的socket,單開一個線程對其進行處理,主線程繼續(xù)監(jiān)聽阻塞。
                dealThread.start()
            }
        } catch(e: Exception) {
               e.printStackTrace()
            System.out.println("綁定失敗")
        }
    }
}

以上就是server端的第一步:監(jiān)聽端口。接下來看在處理這一個客戶端的訪問時,如何獲取輸入流和輸出流。


import java.io.*
import java.net.Socket
import java.nio.charset.Charset
import java.util.*

class DealClientSocket(val clientSocket: Socket) : Runnable {
    //將建立握手的socket傳入構(gòu)造函數(shù),交給這個類處理。
    @Throws(UnsupportedOperationException::class)
    override fun run() {
        val input = socket.inputStream
        val str = readLine(input)
        System.out.println(str)
        val pw = PrintWriter(socket.outputStream)

        pw.println("來自server的問候")
        pw.flush()
        
        input.close()
        pw.close()
        socket.close()
    }

    @Throws(IOException::class)
    private fun readLine(`is`: InputStream): String {
        val lineByteList = ArrayList<Byte>()
        var readByte: Byte
        do {
            readByte = `is`.read().toByte()
            lineByteList.add(java.lang.Byte.valueOf(readByte))
        } while (readByte.toInt() != 10)
        
        val byteArr = lineByteList.toByteArray()
        return String(byteArr, Charset.defaultCharset())
    }
}

1.這里獲取socket中的InputStream,將數(shù)據(jù)一行一行取出。
2.使用PrintWriter包裝OutputStream,將要回復的數(shù)據(jù)發(fā)出。
3.關閉輸入輸出流和socket連接。

客戶端的代碼與server端代碼也只有一丁點的不同:


import android.util.Log
import java.io.IOException
import java.io.InputStream
import java.io.PrintWriter
import java.net.Socket
import java.nio.charset.Charset

class SimpleSocket : Runnable {
    val HOST = "10.59.47.206"
    val PORT = 9000
    override fun run() {
        var socket = Socket(HOST, PORT)//不同點:需要指定ip地址和端口號
        val pw = PrintWriter(socket.getOutputStream())
        val input = socket.getInputStream()
        pw.println("from app")
        pw.flush()
        
        val back = readLine(input)
        Log.e("sfhttp", "Server:$back")
       
        input.close()
        pw.close()
        socket.close()
    }
    
    @Throws(IOException::class)
    private fun readLine(`is`: InputStream): String {
        val lineByteList = ArrayList<Byte>()
        var readByte: Byte
        do {
            readByte = `is`.read().toByte()
            lineByteList.add(java.lang.Byte.valueOf(readByte))
        } while (readByte.toInt() != 10)
        val byteArr = lineByteList.toByteArray()
        return String(byteArr, Charset.defaultCharset())
    }
}

值得注意的是,上述兩端有一個隱形的“坑”:當獲取輸入流的時候,沒有使用BufferedReader(InputStreamReader(socket.getInputStream))進行封裝,而是直接操作的inputStream。
雖然java提供了上述封裝,但它有個特點,一旦不滿足某些條件,就一定會阻塞住,有時候雖然數(shù)據(jù)都打印出來了,竟然還莫名其妙的阻塞著。這就非常的蛋疼了。于是我們干脆自定義readLine方法,讀取一行。這一行的結(jié)束標志就是byte.toInt=10(換行符\n).因此我們在PrintWriter調(diào)用的時候都是用pringln()方法自帶換行符。

這個特點將在上層應用和協(xié)議——HTTP協(xié)議中得到體現(xiàn)。因此不能小看,需要牢記。

總結(jié)

  • Socket是TCP/UDP協(xié)議層的封裝接口
  • Socket、ServerSocket的使用,互相發(fā)送和讀取信息
  • Socket 需要注意換行符的使用
  • HTTP的實現(xiàn)是基于Socket.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容