go 語言socket 編程

簡介

Socket,英文含義是【插座、插孔】,一般稱之為套接字,用于描述IP地址和端口??梢詫?shí)現(xiàn)不同程序間的數(shù)據(jù)通信。

Socket起源于Unix,而Unix基本哲學(xué)之一就是“一切皆文件”,都可以用“打開open –> 讀寫write/read –> 關(guān)閉close”模式來操作。Socket就是該模式的一個實(shí)現(xiàn),網(wǎng)絡(luò)的Socket數(shù)據(jù)傳輸是一種特殊的I/O,Socket也是一種文件描述符。Socket也具有一個類似于打開文件的函數(shù)調(diào)用:Socket(),該函數(shù)返回一個整型的Socket描述符,隨后的連接建立、數(shù)據(jù)傳輸?shù)炔僮鞫际峭ㄟ^該Socket實(shí)現(xiàn)的。

  • 網(wǎng)絡(luò)上的兩個程序通過一個雙向的通信連接實(shí)現(xiàn)數(shù)據(jù)的交換,這個連接的一端稱為一個socket。
  • 建立網(wǎng)絡(luò)通信連接至少要一對端口號(socket)。socket本質(zhì)是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網(wǎng)絡(luò)開發(fā)所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數(shù)據(jù)的具體形式;Socket是發(fā)動機(jī),提供了網(wǎng)絡(luò)通信的能力。
  • Socket的英文原義是“孔”或“插座”。作為BSD UNIX的進(jìn)程通信機(jī)制,取后一種意思。通常也稱作"套接字",用于描述IP地址和端口,是一個通信鏈的句柄,可以用來實(shí)現(xiàn)不同虛擬機(jī)或不同計算機(jī)之間的通信。每種服務(wù)都打開一個Socket,并綁定到一個端口上,不同的端口對應(yīng)于不同的服務(wù)。Socket正如其英文原意那樣,像一個多孔插座。插座是用來給插頭提供一個接口讓其通電的,此時我們就可以將插座當(dāng)做一個服務(wù)端,不同的插頭當(dāng)做客戶端。
服務(wù)端
package main

import (
    "fmt"
    "net"
    "strings"
)

func main() {
    listener ,err := net.Listen("tcp",":8000")
    if err != nil {
        fmt.Println("listen err :",err)
    }

    defer listener.Close()
    fmt.Println("等待客戶端連接...")

    // 接收多個用戶
    for {
        conn ,err := listener.Accept()
        if err != nil {
            fmt.Println("err=",err)
            return
        }
        go HandleConn(conn)
    }

/*  conn, err := listener.Accept()
    if err != nil {
        fmt.Println("accept err:",err)
    }
    defer conn.Close()
    fmt.Println("客戶端與服務(wù)器連接建立成功...")

    buf := make([]byte,1024)

    n,err := conn.Read(buf)

    if err != nil {
        fmt.Println("read err: ",err)
    }
    _,err = conn.Write([]byte("收到了"))
    if err != nil {
        fmt.Println("收到了:",err)
    }
    fmt.Println("服務(wù)器讀到數(shù)據(jù):",string(buf[:n]))*/
}

func HandleConn(conn net.Conn) {
    //函數(shù)調(diào)用完畢,自動關(guān)閉conn
    defer conn.Close()

    //獲取客戶端的網(wǎng)絡(luò)地址信息
    addr := conn.RemoteAddr().String()
    fmt.Println(addr, " conncet sucessful")

    buf := make([]byte, 2048)


    //把數(shù)據(jù)轉(zhuǎn)換為大寫,再給用戶發(fā)送 這個地方for 是為了客戶端和服務(wù)端保持連接不中斷
    for {
        //讀取用戶數(shù)據(jù)
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("err = ", err)
            return
        }
        fmt.Printf("[%s]: %s\n",  addr,  string(buf[:n]))
        fmt.Println("len = ", len(string(buf[:n])))

        //if "exit" == string(buf[:n-1]) {  // nc測試,發(fā)送時,只有 \n
        if  "exit" == string(buf[:n-1]) {  // 自己寫的客戶端測試, 發(fā)送時,多了2個字符, "\r\n"
            fmt.Println(addr, " exit")
            return
        }
        fmt.Println("ok",string(buf[:n]))
        conn.Write([]byte(strings.ToUpper(string(buf[:n]))))
    }
}

客戶端

package main

import (
    "fmt"
    "net"
    "os"
)

func main() {
    conn,err := net.Dial("tcp","127.0.0.1:8000")

    if err != nil {
        fmt.Println("Dial err:",err)
    }

    defer conn.Close()


    // 啟動子協(xié)程,接收用戶鍵盤輸入
    go func() {
        str := make([]byte, 1024)         // 創(chuàng)建用于存儲用戶鍵盤輸入數(shù)據(jù)的切片緩沖區(qū)。
        for {                             // 反復(fù)讀取
            n, err :=os.Stdin.Read(str)        // 獲取用戶鍵盤輸入
            if err != nil {
                fmt.Println("os.Stdin.Read err:", err)
                return
            }
            // 將從鍵盤讀到的數(shù)據(jù),發(fā)送給服務(wù)器
            _, err = conn.Write(str[:n])       // 讀多少,寫多少
            if err != nil {
                fmt.Println("conn.Write err:", err)
                return
            }
        }
    }()

    // 主協(xié)程,接收服務(wù)器回發(fā)數(shù)據(jù),打印至屏幕
    buf := make([]byte, 1024)                // 定義用于存儲服務(wù)器回發(fā)數(shù)據(jù)的切片緩沖區(qū)
    for {
        n, err := conn.Read(buf)          // 從通信 socket 中讀數(shù)據(jù),存入切片緩沖區(qū)
        if err != nil {
            fmt.Println("conn.Read err:", err)
            return
        }
        fmt.Printf("服務(wù)器回發(fā):%s\n", string(buf[:n]))
    }

/*  n,err := conn.Write([]byte("are you ok"))
    if err != nil {
        fmt.Println("write err:",err)
    }
    fmt.Println(n)

    buf := make([]byte,1024)

    n,err = conn.Read(buf)

    if err != nil {
        fmt.Println("read err: ",err)
    }
    fmt.Println("客戶端讀到數(shù)據(jù):",string(buf[:n]))*/
}

參考文獻(xiàn)

http://www.itdecent.cn/p/dc8e23de0367

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • Socket編程 什么是Socket Socket,英文含義是【插座、插孔】,一般稱之為套接字,用于描述IP地址和...
    王玉偉的偉閱讀 7,925評論 0 2
  • 1. 概述 Socket 起源于 Unix,而 Unix 基本哲學(xué)之一就是“一切皆文件”,都可以用“打開 open...
    楚江云閱讀 926評論 0 0
  • 網(wǎng)絡(luò)編程 一.楔子 你現(xiàn)在已經(jīng)學(xué)會了寫python代碼,假如你寫了兩個python文件a.py和b.py,分別去運(yùn)...
    go以恒閱讀 2,250評論 0 6
  • 說明 本文 翻譯自 realpython 網(wǎng)站上的文章教程 Socket Programming in Pytho...
    keelii閱讀 2,432評論 0 16
  • 欲知前事如何,可以點(diǎn)擊作者頭像收看。急更中,敬請期待情節(jié)發(fā)展。 曼姝在街上一步一步地走著,她本來可以回到自己當(dāng)初寓...
    婉青婉青閱讀 786評論 2 0

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