簡介
Socket的圆,英文含義是【插座、插孔】半火,一般稱之為套接字越妈,用于描述IP地址和端口∨ヌ牵可以實(shí)現(xiàn)不同程序間的數(shù)據(jù)通信梅掠。
Socket起源于Unix,而Unix基本哲學(xué)之一就是“一切皆文件”店归,都可以用“打開open –> 讀寫write/read –> 關(guān)閉close”模式來操作瓤檐。Socket就是該模式的一個(gè)實(shí)現(xiàn),網(wǎng)絡(luò)的Socket數(shù)據(jù)傳輸是一種特殊的I/O娱节,Socket也是一種文件描述符挠蛉。Socket也具有一個(gè)類似于打開文件的函數(shù)調(diào)用:Socket(),該函數(shù)返回一個(gè)整型的Socket描述符肄满,隨后的連接建立谴古、數(shù)據(jù)傳輸?shù)炔僮鞫际峭ㄟ^該Socket實(shí)現(xiàn)的。
- 網(wǎng)絡(luò)上的兩個(gè)程序通過一個(gè)雙向的通信連接實(shí)現(xiàn)數(shù)據(jù)的交換稠歉,這個(gè)連接的一端稱為一個(gè)socket掰担。
- 建立網(wǎng)絡(luò)通信連接至少要一對端口號(socket)。socket本質(zhì)是編程接口(API)怒炸,對TCP/IP的封裝带饱,TCP/IP也要提供可供程序員做網(wǎng)絡(luò)開發(fā)所用的接口,這就是Socket編程接口阅羹;HTTP是轎車勺疼,提供了封裝或者顯示數(shù)據(jù)的具體形式;Socket是發(fā)動(dòng)機(jī)捏鱼,提供了網(wǎng)絡(luò)通信的能力执庐。
- Socket的英文原義是“孔”或“插座”。作為BSD UNIX的進(jìn)程通信機(jī)制导梆,取后一種意思轨淌。通常也稱作"套接字"迂烁,用于描述IP地址和端口,是一個(gè)通信鏈的句柄递鹉,可以用來實(shí)現(xiàn)不同虛擬機(jī)或不同計(jì)算機(jī)之間的通信盟步。每種服務(wù)都打開一個(gè)Socket,并綁定到一個(gè)端口上躏结,不同的端口對應(yīng)于不同的服務(wù)址芯。Socket正如其英文原意那樣,像一個(gè)多孔插座窜觉。插座是用來給插頭提供一個(gè)接口讓其通電的谷炸,此時(shí)我們就可以將插座當(dāng)做一個(gè)服務(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("等待客戶端連接...")
// 接收多個(gè)用戶
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)用完畢禀挫,自動(dòng)關(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ā)送 這個(gè)地方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ā)送時(shí)语婴,只有 \n
if "exit" == string(buf[:n-1]) { // 自己寫的客戶端測試, 發(fā)送時(shí)描孟,多了2個(gè)字符, "\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()
// 啟動(dòng)子協(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]))*/
}