一、TCP编程
使用 Go 语言的 net 包实现 TCP服务端和 TCP客户端。
1.1 TCP服务端
package main
import (
"bufio"
"fmt"
"net"
)
//处理TCP连接
func Process(conn net.Conn) {
defer conn.Close()
for {
reader := bufio.NewReader(conn)
var buf [128]byte
n, err := reader.Read(buf[:]) //接收数据
if err != nil {
fmt.Println("read from client failed, err:", err)
break
}
recStr := string(buf[:n])
fmt.Println("收到client端发送的消息:", recStr)
conn.Write([]byte(recStr)) //发送响应
}
}
func Response() {
listen, err := net.Listen("tcp", "127.0.0.1:20000")
if err != nil {
fmt.Println("listen failed, err:", err)
return
}
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println("accept failed, err: ", err)
continue
}
go Process(conn)
}
}
func main() {
Response()
}
1.2 TCP客户端
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func Send() {
conn, err := net.Dial("tcp", "127.0.0.1:20000") //建立tcp连接
if err != nil {
fmt.Println("err :", err)
return
}
defer conn.Close() //关闭连接
inputReader := bufio.NewReader(os.Stdin)
for {
input, _ := inputReader.ReadString('\n') //读取用户输入
inputInfo := strings.Trim(input, "\r\n")
if strings.EqualFold(inputInfo, "quit") { //输入quit就退出
return
}
_, err := conn.Write([]byte(inputInfo)) //发送消息
if err != nil {
return
}
buf := [512]byte{}
n, err := conn.Read(buf[:]) //接收响应
if err != nil {
fmt.Println("recv failed, err :", err)
return
}
fmt.Println(string(buf[:n]))
}
}
func main() {
Send()
}
运行测试:
1.3 TCP粘包问题
粘包是指TCP待发送的多个数据包被默认当成一个数据包发送并接收,因为 TCP 以流模式进行数据传递,在保持长连接的时候可以进行多次的收和发,同样粘包问题也可能发生在接收端。
主要原因有以下两点:
- 由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法。简单来说就是当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去。
- 接收端接收不及时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后通知应用层取数据。当应用层由于某些原因不能及时的把TCP的数据取出来,就会造成TCP缓冲区中存放了几段数据。
package main
import (
"bufio"
"fmt"
"net"
)
//处理TCP连接
func Process(conn net.Conn) {
defer conn.Close()
for {
reader := bufio.NewReader(conn)
var buf [128]byte
n, err := reader.Read(buf[:]) //接收数据
if err != nil {
fmt.Println("read from client failed, err:", err)
break
}
recStr := string(buf[:n])
fmt.Println("收到client端发送的消息:", recStr)
conn.Write([]byte(recStr)) //发送响应
}
}
func Response() {
listen, err := net.Listen("tcp", "127.0.0.1:20000")
if err != nil {
fmt.Println("listen failed, err:", err)
return
}
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println("accept failed, err: ", err)
continue
}
go Process(conn)
}
}
func main() {
Response()
}
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func Send() {
conn, err := net.Dial("tcp", "127.0.0.1:20000") //建立tcp连接
if err != nil {
fmt.Println("err :", err)
return
}
defer conn.Close() //关闭连接
inputReader := bufio.NewReader(os.Stdin)
for {
input, _ := inputReader.ReadString('\n') //读取用户输入
inputInfo := strings.Trim(input, "\r\n")
if strings.EqualFold(inputInfo, "quit") { //输入quit就退出
return
}
_, err := conn.Write([]byte(inputInfo)) //发送消息
if err != nil {
return
}
buf := [512]byte{}
n, err := conn.Read(buf[:]) //接收响应
if err != nil {
fmt.Println("recv failed, err :", err)
return
}
fmt.Println(string(buf[:n]))
}
}
func main() {
Send()
}
运行结果:
收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?
收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?
当我们指定在每次发送数据包中指明包长度大小就可以解决粘包问题了,这样就需要对数据包进行封包和拆包。封包就是给一段数据加上固定长度的包头,数据包就由包头和包体两部分组成了。这里假设数据包的前 4 个字节为包长度。
package proto
import (
"bufio"
"bytes"
"encoding/binary"
)
// 解决粘包问题,封包和拆包操作
// 封包操作,前4个字节为包长
func Encode(message string) ([]byte, error) {
// 读取4字节的包长
var length = int32(len(message))
var pkg = new(bytes.Buffer)
// 缓冲区中写入消息头
err := binary.Write(pkg, binary.LittleEndian, length)
if err != nil {
return nil, err
}
// 缓冲区中写入消息实体
err = binary.Write(pkg, binary.LittleEndian, []byte(message))
if err != nil {
return nil, err
}
return pkg.Bytes(), nil
}
// 拆包操作,前4字节为包长
func Decode(reader *bufio.Reader) (string, error) {
// 读取前4字节数据
lengthByte, _ := reader.Peek(4)
lengthBuff := bytes.NewBuffer(lengthByte)
var length int32
err := binary.Read(lengthBuff, binary.LittleEndian, &length)
if err != nil {
return "", err
}
// 判断返回缓冲区中现有可读字节数
if int32(reader.Buffered()) < length+4 {
return "", err
}
// 读取真正的消息数据
pack := make([]byte, 4+length)
_, err = reader.Read(pack)
if err != nil {
return "", err
}
return string(pack[4:]), nil
}
更改客户端和服务端:
package main
import (
"bufio"
"fmt"
"io"
"net"
"test3/netLearn/tcp/proto"
)
func Process2(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
msg, err := proto.Decode(reader) //增加拆包操作
if err == io.EOF {
break
}
if err != nil {
fmt.Println("read from client failed, err:", err)
break
}
fmt.Println("收到client发来的数据:", msg)
}
}
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:30000")
if err != nil {
fmt.Println("listen failed, err :", err)
return
}
defer listen.Close()
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println("accept failed, err:", err)
continue
}
go Process2(conn)
}
}
package main
import (
"fmt"
"net"
"test3/netLearn/tcp/proto"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:30000")
if err != nil {
fmt.Println("tcp connect failed", err)
return
}
defer conn.Close()
for i := 0; i < 20; i++ {
msg := `Hello, Hello. How are you?`
data, err := proto.Encode(msg) //增加封包操作
if err != nil {
fmt.Println("encode msg failed, err:", err)
return
}
conn.Write(data)
}
}
运行结果:
二、UDP编程
2.1 UDP服务端
package main
import (
"fmt"
"net"
)
func main() {
listen, err := net.ListenUDP("udp", &net.UDPAddr{ //监听指定端口
IP: net.IPv4(0, 0, 0, 0),
Port: 30000,
})
if err != nil {
fmt.Println("listen failed, err:", err)
return
}
defer listen.Close()
for {
var data [1024]byte
n, addr, err := listen.ReadFromUDP(data[:]) //接收UDP数据
if err != nil {
fmt.Println("read udp failed, err :", err)
continue
}
fmt.Printf("data:%v addr:%v count:%v\n", string(data[:n]), addr, n)
_, err = listen.WriteToUDP(data[:n], addr) //返回消息
if err != nil {
fmt.Println("write to udp failed, err :", err)
continue
}
}
}
2.2 UDP客户端
package main
import (
"fmt"
"net"
)
func main() {
socket, err := net.DialUDP("udp", nil, &net.UDPAddr{ //连接UDP服务器
IP: net.IPv4(0, 0, 0, 0),
Port: 30000,
})
if err != nil {
fmt.Println("连接服务器失败, err:", err)
return
}
defer socket.Close()
sendData := []byte("hello udp server")
_, err = socket.Write(sendData) //向UDP服务器发送数据
if err != nil {
fmt.Println("发送数据失败,err :", err)
return
}
data := make([]byte, 4096)
n, remoteAddr, err := socket.ReadFromUDP(data) //接收响应的数据
if err != nil {
fmt.Println("接收数据失败, err:", err)
return
}
fmt.Printf("recv:%v addr:%v count:%v\n", string(data[:n]), remoteAddr, n)
}
运行测试:
三、HTTP编程
3.1 服务端
package main
import (
"fmt"
"net/http"
)
func main() {
//http://127.0.0.1:8000/go
// 单独写回调函数
http.HandleFunc("/go", myHandler)
//http.HandleFunc("/ungo",myHandler2 )
// addr:监听的地址
// handler:回调函数
http.ListenAndServe("127.0.0.1:8000", nil)
}
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.RemoteAddr, "连接成功")
fmt.Println("method:", r.Method)
fmt.Println("url:", r.URL)
fmt.Println("header:", r.Header)
fmt.Println("body:", r.Body)
//回复
w.Write([]byte("www.51mh.com"))
}
3.2 客户端
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
resp, _ := http.Get("http://127.0.0.1:8000/go")
if resp != nil {
defer resp.Body.Close()
}
fmt.Println(resp.Status)
fmt.Println(resp.Header)
buf := make([]byte, 1024)
for {
// 接收服务端信息
n, err := resp.Body.Read(buf)
if err != nil && err != io.EOF {
fmt.Println(err)
return
} else {
fmt.Println("读取完毕")
res := string(buf[:n])
fmt.Println(res)
break
}
}
}
结果响应:
标签:12,nil,err,fmt,编程,网络,Println,Hello,conn From: https://www.cnblogs.com/istitches/p/17748648.html