golang网络编程笔记

| 2018年2月12日

网络编程类型

我们一般提到的网络编程类型有两种:tcp和udp,都属于网络传输层协议支持下的协议,而且在目前也是网络编程中最基础的。这其中具体说来有有个socket的概念,tcp和udp都是协议,而socket就是一个具体的实现了,所以有tcp socket和udpsocket。golang是自身就带有网络库的net的,使用非常方便。 同时我这里还会简单介绍一下另外一种协议,用户态的,基于udp的,但是具有tcp的连接回话管理,重传确认等机制-kcp。 网络编程主要的目的是进行通信,所以就会涉及到至少2个端的通信,一般我们把这两个端分为服务端和客户端,服务端主要是起一个服务,实际上也就是监听一个服务端口,接受客户端的连接请求,并且接受请求命令进行服务功能的执行。客户端主要就是向服务端发起连接请求连接到服务端,并且先服务端发送命令字,让服务端执行服务。所以在后面的介绍中就会有一个客户端程序和服务端程序。

TCP Socket

在Go语言的net包中有一个类型TCPConn,这个类型可以用来作为客户端和服务器端交互的通道。net包中包含了对针对服务端和客户端的所有方法。

TCP Server

首先我们看看针对服务端的,服务端的主要流程有

  1. 监听端口 - listener, err := net.ListenTCP(“tcp”, tcpAddr)
  2. 等待请求,接受请求 - conn, err := listener.Accept()
  3. 读取客户端发送的内容 - buffer := make([]byte, 1024) conn.Read(buffer)
  4. 回写内容给客户端 -conn.Write([]byte(daytime))
  5. 断开链接 -conn.Close() 以上就可以看作是一个服务端启动,接受请求,处理请求的过程。下面看一个详细的例子;
package main

import (
"fmt"
"net"
"os"
)
var (
count = 0
)
func main() {
ihost := ":5555"
tcpAddr, err := net.ResolveTCPAddr("tcp", ihost)
if err != nil {
fmt.Printf("Fatal error: ", err)
os.Exit(1)
}
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
fmt.Printf("Fatal error: ", err)
os.Exit(1)
}
for {
conn, err := listener.Accept()
if err != nil {
continue
}
count += 1
rep := fmt.Sprintf("hello %d", count)
conn.Write([]byte(rep))
conn.Close()
}
}

TCP Client

客户端的处理过程是这样的:

  1. 连接服务端 - conn, err := net.DialTCP(“tcp”, nil, tcpAddr)
  2. 写数据给服务端 - _, err = conn.Write([]byte(“hello server”))
  3. 读取服务端的返回内容 - result, err := ioutil.ReadAll(conn)
  4. 关闭连接 defer conn.Close() 具体的示例代码如下:
package main

import (
"fmt"
"io/ioutil"
"net"
"os"
)

func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args)
os.Exit(1)
}
ihost := os.Args[1]
tcpAddr, err := net.ResolveTCPAddr("tcp4", ihost)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err)
os.Exit(1)
}
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err)
os.Exit(1)
}
defer conn.Close()
_, err = conn.Write([]byte("hello server"))
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err)
os.Exit(1)
}
result, err := ioutil.ReadAll(conn)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err)
os.Exit(1)
}
fmt.Println(string(result))
os.Exit(0)
}

UDP Socket

udp的协议先对比tcp要简单一点,udp的通信无需创建lister,直接进行数据的传输。

UDP server

UDP Server的主要过程是这样的:

  1. 监听端口 - socket, err := net.ListenUDP(“udp4”,
  2. 读取数据 - read, remoteAddr, err := socket.ReadFromUDP(data)
  3. 返回数据 - _, err = socket.WriteToUDP(senddata, remoteAddr) 示例代码如下:
package main

import (
"fmt"
"net"
"os"
)
var (
count = 0
)
func main() {
ihost := ":5555"
udpAddr, err := net.ResolveUDPAddr("udp4", ihost)
if err != nil {
fmt.Printf("Fatal error: ", err)
os.Exit(1)
}
listener, err := net.ListenUDP("udp", udpAddr)
if err != nil {
fmt.Printf("Fatal error: ", err)
os.Exit(1)
}
defer listener.Close()
for {
count += 1
handleConn(listener)
}
}
func handleConn(c *net.UDPConn) {
fmt.Println("begin to do udp request")
buffer := make([]byte, 128)
_, addr, err := c.ReadFromUDP(buffer[0:])
if err != nil {
return
}
rep := fmt.Sprintf("get: %s return: hell %d", buffer, count)

_, err = c.WriteToUDP([]byte(rep), addr)
if err != nil {
return
fmt.Println("send data fail: ", err)
}
}

UDP Client

udp的客户端也是比较简单的,和tcp的简单客户端是差不多的(tcp的客户端服务端连接还有一种长连接方式),客户端的处理过程是这样的:

  1. 连接服务端 - conn, err := net.DialUDP(“udp”, nil, udpAddr)
  2. 写数据给服务端 - _, err = conn.Write([]byte(“hello server”))
  3. 读取服务端的返回内容 - data := make([]byte, 1024) read, addr, err := conn.ReadFromUDP(data)
  4. 关闭连接 defer conn.Close() 具体示例代码如下:
package main

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

func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args)
os.Exit(1)
}
ihost := os.Args[1]
udpAddr, err := net.ResolveUDPAddr("udp4", ihost)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error 1: %s", err)
os.Exit(1)
}
conn, err := net.DialUDP("udp", nil, udpAddr)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error 2: %s", err)
os.Exit(1)
}
defer conn.Close()
_, err = conn.Write([]byte("hello server"))
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err)
os.Exit(1)
}
data := make([]byte, 1024)
read, addr, err := conn.ReadFromUDP(data)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err)
os.Exit(1)
}
fmt.Println("read: ", read, " add:", addr)
fmt.Println("read data: ", string(data))
os.Exit(0)
}

golang kcp

在upd上进行增强的协议,KCP 是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。这部分暂时没有深入分析,只是尝试写了一个最简单的demo。留在后面再继续分析。

KCP server

package main
import (
"fmt"
"net"
"time"

kcp "github.com/xtaci/kcp-go"
)

func main() {
listen, err := kcp.Listen("0.0.0.0:6666")
if err != nil {
panic(err)
return
}
for {
conn, err := listen.Accept()
if err != nil {
panic(err)
return
}

go handleconn(conn)
}
}

func handleconn(conn net.Conn) {
fmt.Println("new client", conn.RemoteAddr())
buf := make([]byte, 65536)
count := 0
for {
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
n, err := conn.Read(buf)
fmt.Println("new client read: ", n, " ", string(buf))
if err != nil {
fmt.Println("new client read err", n)
conn.Close()
return
}
count++
conn.Write(buf[:n])
}
}

KCP client

package main
import (
"bytes"
"fmt"
"time"

kcp "github.com/xtaci/kcp-go"
)

func main() {
conn, err := kcp.Dial("0.0.0.0:6666")
if err != nil {
panic(err)
return
}
for {
ret, err := conn.Write([]byte("hello kcp!!!"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(ret)
datas := bytes.NewBuffer(nil)
var buf[512] byte
n, err := conn.Read(buf[0:])
datas.Write(buf[0:n])
fmt.Println("read: ", string(datas.Bytes()))
time.Sleep(1 * time.Second)
}
}
看完本文有收获?请分享给更多人
关注「黑光技术」,关注大数据+微服务