3.网络编程
3.1.TCP编程
package main
import (
"bufio"
"fmt"
"net"
)
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
}
// 把接收数据转换string类型
recvStr := string(buf[:n])
fmt.Println("收到client端发来的数据:",recvStr)
write, _ := conn.Write([]byte(recvStr))
fmt.Println("发送数据量:",write, " 字节!")
}
}
func main() {
// 监听套接字
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 faield, err:", err)
continue
}
// 启动一个goroutine处理连接
go process(conn)
}
}
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main () {
conn, err := net.Dial("tcp", "127.0.0.1:20000")
// 连接发生错误,打印错误
if err != nil {
fmt.Println("err:", err)
return
}
defer conn.Close()
// 输入传入信息
inputReader := bufio.NewReader(os.Stdin)
for {
fmt.Print("请输入传输信息:")
// 读取输入信息
input, _ := inputReader.ReadString('\n')
// \r\n 分割
inputInfo := strings.Trim(input, "\r\n")
// 如果输入Q推出
if strings.ToUpper(inputInfo) == "Q" {
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]))
}
}
3.2.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[:])
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
}
}
}
package main
import (
"fmt"
"net"
)
func main() {
// 创建udp连接套接字
socket, err := net.DialUDP("udp", nil, &net.UDPAddr{
IP:net.IPv4(0,0,0,0),
Port:30000,
})
if err != nil {
fmt.Println("连接服务端失败,err:",err)
return
}
defer socket.Close()
sendData := []byte("hello world")
// 发送数据
_, err = socket.Write(sendData)
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)
}
3.3TCP粘包
package main
import (
"bufio"
"fmt"
"io"
"net"
)
func process(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
var buf [1024]byte
for {
n, err := reader.Read(buf[:])
if err == io.EOF {
break
}
if err != nil {
fmt.Println("read from client failed, err:",err)
break
}
recvStr := string(buf[:n])
fmt.Println("接收到client发来的数据:", recvStr)
}
}
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 process(conn)
}
}
package main
import (
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp","127.0.0.1:30000")
if err != nil{
fmt.Println("dial failed,err",err)
return
}
defer conn.Close()
for i:=0;i<20;i++ {
msg := "hello,hi my name is Tom"
conn.Write([]byte(msg))
}
}
my name is Tomhello,hi my name is Tomhello,hi my name is Tomhello,hi my name is Tomhello,hi my name is Tom
接收到client发来的数据: hello,hi my name is Tomhello,hi my name is Tomhello,hi my name is To my name is Tomhello,hi my name is Tomhello,hi my name is Tomhello,hi my name is Tomhello,hi my name is Tomhello,hi my name is Tomhello,hi my name is Tomhello,hi my name is Tomhello,hi my name is Tom
- 客户端分10次发送数据,但是在服务端没有成功输出10次,使多条数据粘到了一起。
3.3.1为什么会出现粘包
-
主要原因tcp数据传输为流式模式,在保持长连接的时候可以进行多次的收发消息。
-
粘包有可能发生在发送端,也有可能发生在接收端。
1.由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法。简单来说就是当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去。
2.接收端接收不及时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后通知应用层取数据。当应用层由于某些原因不能及时的把TCP的数据取出来,就会造成TCP缓冲区中存放了几段数据。
3.3.2解决办法
-
出现”粘包”的关键在于接收方不确定将要传输的数据包的大小,因此我们可以对数据包进行封包和拆包的操作。
-
封包是给一段数据加上包头,这样数据包就分为包头和包体。包头长度固定,并且它存储了包体长度。根据包头长度固定以及包头重含有包体长度的变量就能正确的拆分出一个完整数据包。
-
可以自定义一个协议,比如数据包的前4个字节为包头,里面存储的是发送的数据的长度
package main
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
)
func Encode(message string) ([]byte, error){
// 读取消息的长度,转换成int32类型(占4个字节)
var length = int32(len(message))
fmt.Printf("length:%v\n",length)// length:11
var pkg = new(bytes.Buffer)
//fmt.Println(pkg) //空对象
// 写入消息头
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
}
fmt.Printf("message:%v\n",pkg)//message:hello world
fmt.Println(pkg.Bytes())//[11 0 0 0 104 101 108 108 111 32 119 111 114 108 100]
return pkg.Bytes(), nil
}
func Decode(reader *bufio.Reader) (string, error) {
lengthByte, _ := reader.Peek(4)//读取消息的长度(占4个字节)
lengthBuff := bytes.NewBuffer(lengthByte)
var length int32
err := binary.Read(lengthBuff, binary.LittleEndian, &length)
if err != nil {
return "", err
}
// Buffered返回缓冲中现有的可读取的字节数
if int32(reader.Buffered()) < length + 4 {
return "", err
}
// 读取真正消息数据
pack := make([]byte, int(4+length))
_, err = reader.Read(pack)
if err != nil {
return "", err
}
return string(pack[4:]), nil
}
func main (){
msg := "hello world"
a,_ := Encode(msg)
fmt.Printf("type:%T\n",a)
// data, _:=Decode(a)
// fmt.Println(data)
}
package main
import (
"bufio"
"fmt"
"io"
"mypro/tcp_stick/proto"
"net"
)
func process(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
// var buf [1024]byte
for {
msg, err := proto.Decode(reader)
// n, err := reader.Read(buf[:])
if err == io.EOF {
break
}
if err != nil {
fmt.Println("read from client failed, err:",err)
break
}
// recvStr := string(buf[:n])
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 process(conn)
}
}
package main
import (
"fmt"
"mypro/tcp_stick/proto"
"net"
)
func main() {
conn, err := net.Dial("tcp","127.0.0.1:30000")
if err != nil{
fmt.Println("dial failed,err",err)
return
}
defer conn.Close()
for i:=0;i<20;i++ {
msg := "hello,hi my name is Tom"
data, err := proto.Encode(msg)
if err != nil {
fmt.Println("encode msg failed, err:",err)
}
conn.Write(data)
}
}
|
请发表评论