加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Go socket编程

发布时间:2020-12-16 09:20:09 所属栏目:大数据 来源:网络整理
导读:socket socket 应该是各种语言中网络编程的基础,它介于应用层与传输层之间,只要学会使用它的接口即可。 TCP 以下建立两台机器互相通信。 Server 以下是 Go 语言中通过 socket 和 goroutine 编写的一个非常简单的服务端。 流程如下: 建立与服务端的链接 进

socket

   socket应该是各种语言中网络编程的基础,它介于应用层与传输层之间,只要学会使用它的接口即可。

TCP

   以下建立两台机器互相通信。

Server

   以下是Go语言中通过socketgoroutine编写的一个非常简单的服务端。

   流程如下:

   建立与服务端的链接

   进行数据收发

   关闭链接

// D:GoLeransrcyunya.comTCPServer>

package main

import (
	"bufio"
	"fmt"
	"net"
	"strings"
)

func main(){
	listen,err := net.Listen("tcp","127.0.0.1:9999")
	if err != nil {
		fmt.Println("listent failed,err:",err)
		return
	}
	for {
		conn,err := listen.Accept() // 建立链接
		if err != nil {
			fmt.Println("accept failed,err) // 三次握手失败
			continue
		}
		go process(conn) // 启动多个goroutine来处理回复
	}
}

// 处理请求
func process(conn net.Conn) {
	defer conn.Close() // 关闭链接通道
	for {
		reader := bufio.NewReader(conn)
		var buf [1024]byte
		n,err := reader.Read(buf[:]) // 读取数据 读取的字节数,错误信息
		if err != nil {
			fmt.Print("read form client failed,err)
			break
		}
		recvStr := string(buf[:n])
		fmt.Println("client message:",recvStr)
		var inputMsg string
		fmt.Println("请输入你要发送的信息:")
		fmt.Scanln(&inputMsg)
		inputMsg = strings.Trim(inputMsg,"rn") // 去除空行等,防止阻塞
		conn.Write([]byte(inputMsg))
	}
}

Client

   以下是客户端的代码。

   建立与服务端的链接

   进行数据收发

   关闭链接

D:GoLeransrcyunya.comTCPClient>

package main

import (
	"fmt"
	"net"
	"strings"
)

func main() {
	conn,err := net.Dial("tcp","127.0.0.1:9999") // 绑定服务端地址
	if err != nil {
		fmt.Println("err:",err)
		return
	}
	defer conn.Close() // 关闭双向链接
	for {
		var inputMsg string
		fmt.Println("请输入你要发送的信息:")
		fmt.Scanln(&inputMsg)
		inputMsg = strings.Trim(inputMsg,"rn") // 去除空行等,防止阻塞
		if strings.ToUpper(inputMsg) == "quit" {
			return
		}
		_,err = conn.Write([]byte(inputMsg)) // 发送数据
		if err != nil {
			return
		}
		buf := [512]byte{}
		serverMsg,err := conn.Read(buf[:]) // 服务端返回的信息
		if err != nil {
			fmt.Println("recv failed err:",err)
			return
		}
		fmt.Println("server message:",string(buf[:serverMsg]))
	}
}

UDP

Server

   UDP不用建立双向链接,消息不可靠。因此一般来说使用较少。

// UDP/server/main.go

// UDP server端
func main() {
	listen,err := net.ListenUDP("udp",&net.UDPAddr{
		IP:   net.IPv4(0,0),Port: 30000,})
	if err != nil {
		fmt.Println("listen failed,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)
			continue
		}
		fmt.Printf("data:%v addr:%v count:%vn",string(data[:n]),n)
		_,err = listen.WriteToUDP(data[:n],addr) // 发送数据
		if err != nil {
			fmt.Println("write to udp failed,err)
			continue
		}
	}
}

Client

   客户端代码如下:

// UDP 客户端
func main() {
	socket,err := net.DialUDP("udp",nil,})
	if err != nil {
		fmt.Println("连接服务端失败,err:",err)
		return
	}
	defer socket.Close()
	sendData := []byte("Hello server")
	_,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:%vn",n)
}

TCP粘包

解决方案

   由于TCP是流式传输协议。所以可能会产生粘包现象,我们需要划分每次数据的大小边界,所以可以自定制一个收发消息的协议。如下:

// socket_stick/proto/proto.go
package proto

import (
	"bufio"
	"bytes"
	"encoding/binary"
)

// Encode 将消息编码
func Encode(message string) ([]byte,error) {
	// 读取消息的长度,转换成int32类型(占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,[]byte(message))
	if err != nil {
		return nil,err
	}
	return pkg.Bytes(),nil
}

// Decode 解码消息
func Decode(reader *bufio.Reader) (string,error) {
	// 读取消息的长度
	lengthByte,_ := reader.Peek(4) // 读取前4个字节的数据
	lengthBuff := bytes.NewBuffer(lengthByte)
	var length int32
	err := binary.Read(lengthBuff,&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
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读