目錄
一杠巡、TCP編程
二、UDP編程
其實(shí)前幾天買了本書雇寇,今天終于到貨了氢拥,滿懷欣喜的打開目錄,socket編程锨侯、數(shù)據(jù)庫嫩海、I/O統(tǒng)統(tǒng)沒有,額囚痴。叁怪。。
回歸正題深滚,關(guān)于TCP/UDP的基礎(chǔ)我們不做過多解釋了奕谭,大家可自行補(bǔ)充或可參考python網(wǎng)絡(luò)編程【python入門系列(十)】輔助理解,才不是打廣告的說痴荐。
一血柳、TCP編程
我們借助net包來模擬一個(gè)系統(tǒng),服務(wù)端可以相應(yīng)來自用戶端的輸入生兆,借助我們之前學(xué)到的并發(fā)难捌,允許它為多個(gè)用戶提供連接,且通過循環(huán)持續(xù)響應(yīng)用戶請求鸦难,在收到exit時(shí)斷開連接:
- Server
Server端借助net包的Listen()方法創(chuàng)建監(jiān)聽:
func Listen(network, address string) (Listener, error)
//network:選用的協(xié)議:TCP栖榨、UDP, 如:“tcp”或 “udp” 注意:只支持小寫字母
//address:IP地址+端口號, 如:“127.0.0.1:8000”或 “:8000”
數(shù)據(jù)交互時(shí)明刷,發(fā)送使用Write()方法婴栽,接收使用Read()方法。
func main() {
listener, err := net.Listen("tcp", "127.0.0.1:8000")
checkErr(err)
defer listener.Close()
for {
conn, err := listener.Accept()
checkErr(err)
go task(conn)
}
}
func task(conn net.Conn) {
defer conn.Close()
addr := conn.RemoteAddr( ).String()
fmt.Println(addr, " connect sucessful" )
buf := make([ ]byte, 1024)
for{
n, err := conn.Read(buf)
checkErr(err)
if string(buf[:n])=="exit"{
conn.Write([]byte("拜拜~~~~"))
conn.Close()
fmt.Println(addr, " Disconnect sucessful" )
return
}
fmt.Printf("read buf = %s\n", string(buf[:n]))
response:="Hello "+string(buf[:n])
conn.Write([]byte(response))
}
}
func checkErr(err error) {
if err != nil {
fmt.Println(err.Error())
return
}
}
#控制臺
127.0.0.1:54815 connect sucessful
read buf = rabbit
read buf = carrot
127.0.0.1:54815 Disconnect sucessful
我們稍加總結(jié)下最重要的數(shù)據(jù)交互規(guī)則:
func taskdemo(c net.Conn) {
//some code...
//Simple read from connection
buffer := make([]byte, 1024)
c.Read(buffer)
//simple write to connection
c.Write([]byte("Hello from server"))
}
- Client
CIient端用到的方法幾乎與server端一致辈末,創(chuàng)建服務(wù)器連接依賴net包的Dial()方法:
func Dial(network, address string) (Conn, error)
//network:選用的協(xié)議:TCP愚争、UDP映皆,如:“tcp”或 “udp”
//address:服務(wù)器IP地址+端口號, 如:“127.0.0.1:8000”或 “www.baidu.com:80”
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("連接失敗,錯(cuò)誤:", err)
return
}
defer conn.Close()
for{
var name string
_, _ = fmt.Scanln(&name)
conn.Write([]byte(name))
buf := make([]byte,1024)
n,_ := conn.Read(buf)
fmt.Println(string(buf[:n]))
}
}
#控制臺
rabbit
Hello rabbit
carrot
Hello carrot
exit
拜拜~~~~
二轰枝、UDP編程
UDP連接是一個(gè)不負(fù)責(zé)任的連接,如果不允許你的消息丟包鞍陨,那記得做好接收反饋,如果沒有接收成功記得重發(fā)缭裆,但是UDP的特點(diǎn)在于它不需要持續(xù)的會(huì)話,意味著在某些場景下他更加隱匿寿烟,比如木馬澈驼、木馬、和木馬缝其。
- Server
UDP編程Server端借助net包的ListenUDP()方法,數(shù)據(jù)交互讀取使用ReadFromUDP()方法内边,發(fā)送使用WriteToUDP()方法,關(guān)于其并發(fā)的處理位置我們借助實(shí)例體會(huì):
func main() {
udpAddr, _ := net.ResolveUDPAddr("udp", "127.0.0.1:8000")
listener, err := net.ListenUDP("udp", udpAddr)
checkErr(err)
fmt.Println("Start listening ... ")
defer listener.Close()
task(listener)
}
func task(conn *net.UDPConn) {
defer conn.Close()
buf := make([ ]byte, 1024)
for{
n, addr, err := conn.ReadFromUDP(buf)
checkErr(err)
fmt.Printf("read buf = %s\n", string(buf[:n]))
go func(){
response:="Hello "+string(buf[:n])
conn.WriteToUDP([]byte(response),addr)
}()
}
}
func checkErr(err error) {
if err != nil {
fmt.Println(err.Error())
return
}
}
#控制臺
Start listening ...
read buf = rabbit
read buf = carrot
我們將UDP的并發(fā)處理放到了閉包這里待锈,另外我們對數(shù)據(jù)交互的規(guī)則簡單整理下:
func taskdemo(c *net.UDPConn) {
//some code...
//simple read
buffer := make([]byte, 1024)
n, addr, err:= c.ReadFromUDP(buffer)
//simple write to connection
c.WriteToUDP([]byte("Hello from server"),addr)
}
- Client
如果我們偷懶假残,直接使用tcp的client是完全可以的,把tcp修改為udp:
func main() {
conn, err := net.Dial("udp", "127.0.0.1:8000")
if err != nil {
fmt.Println("連接失敗炉擅,錯(cuò)誤:", err)
return
}
defer conn.Close()
for{
var name string
_, _ = fmt.Scanln(&name)
conn.Write([]byte(name))
buf := make([]byte,1024)
n,_ := conn.Read(buf)
fmt.Println(string(buf[:n]))
}
}
#控制臺
rabbit
Hello rabbit
carrot
Hello carrot
當(dāng)然谍失,像服務(wù)端一樣,使用ReadFromUDP()方法和WriteToUDP()方法也是可以的快鱼。