网络编程
最后更新于
最后更新于
Socket
是应用层与TCP/IP协议族通信的中间软件抽象层。在设计模式中,Socket
其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket
后面,对用户来说只需要调用Socket规定的相关函数,让Socket
去组织符合指定的协议数据然后进行通信。
### tcp协议
TCP/IP(Transmission Control Protocol/Internet Protocol) 即传输控制协议/网间协议,是一种面向连接(连接导向)的、可靠的、基于字节流的传输层(Transport layer)通信协议,因为是面向连接的协议,数据像水流一样传输,会存在黏包问题。
服务端
/**
* Createby GoLand
* User xzw jsjxzw@163.com
* Date 2021/8/8
* Time 上午10:27
*/
package main
import (
bufio
fmt
io
log
net
imooc.com/ccmouse/learngo/net/protocol
)
func main() {
listen, err := net.Listen(tcp, 127.0.0.1:8080) // 监听端口
if err != nil {
log.Fatal(listen failed, err:, err)
}
for {
conn, err := listen.Accept() // 建立连接
if err != nil {
log.Println(accept failed, err:, err)
continue
}
go process(conn) // 启动一个 goroutine 处理连接
}
}
func process(conn net.Conn) {
defer conn.Close()
for {
reader := bufio.NewReader(conn)
msg, err := protocol.Decode(reader)
if err == io.EOF {
return
}
if err != nil {
log.Println(decode msg failed, err:, err)
return
}
fmt.Println(收到client发来的数据:, msg)
data, err := protocol.Encode(msg)
if err != nil {
log.Println(encode msg failed, err:, err)
return
}
conn.Write(data)
}
}
客户端
/**
* Createby GoLand
* User xzw jsjxzw@163.com
* Date 2021/8/8
* Time 上午10:35
*/
package main
import (
bufio
fmt
io
log
net
os
strings
imooc.com/ccmouse/learngo/net/protocol
)
func main() {
conn, err := net.Dial(tcp, 127.0.0.1:8080)
if err != nil {
log.Fatal(err:, err)
}
defer conn.Close()
inputReader := bufio.NewReader(os.Stdin)
for {
input, _ := inputReader.ReadString('\n') // 读取用户输入
inputInfo := strings.Trim(input, \r\n)
if strings.ToUpper(inputInfo) == Q { // 如果输入q就推出
return
}
data, err := protocol.Encode(inputInfo)
if err != nil {
panic(err)
}
_, err = conn.Write(data) // 发送数据
if err != nil {
return
}
reader := bufio.NewReader(conn)
msg, err := protocol.Decode(reader)
if err == io.EOF {
return
}
if err != nil {
log.Println(decode msg failed, err:, err)
return
}
fmt.Println(msg)
}
}
粘包拆包
/**
* Createby GoLand
* User xzw jsjxzw@163.com
* Date 2021/8/8
* Time 上午10:48
*/
package protocol
import (
bufio
bytes
encoding/binary
)
// 将消息编码
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) // binary.LittleEndian 小端序,低地址位存放低位字节
if err != nil {
return nil, err
}
// 写入消息实体
err = binary.Write(pkg, binary.LittleEndian, []byte(message))
if err != nil {
return nil, err
}
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
}
用户数据报协议(User Datagram Protocol,缩写为UDP),又称用户数据报文协议,是一个简单的面向数据报(package-oriented)的传输层协议。UDP只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以UDP有时候也被认为是不可靠的数据报协议)。UDP在IP数据报的头部仅仅加入了复用和数据校验。
服务端
/**
* Createby GoLand
* User xzw jsjxzw@163.com
* Date 2021/8/8
* Time 上午11:36
*/
package main
import (
log
net
)
func main() {
listen, err := net.ListenUDP(udp, &net.UDPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: 30000,
})
if err != nil {
log.Println(listen failed, err:, err)
return
}
defer listen.Close()
for {
var data [1024]byte
n, addr, err := listen.ReadFromUDP(data[:]) // 接收数据
if err != nil {
log.Println(read udp failed, err:, err)
continue
}
log.Printf(data:%v addr:%v count:%v\n, string(data[:n]), addr, n)
_, err = listen.WriteToUDP(data[:n], addr) // 发送数据
if err != nil {
log.Println(write to udp failed, err:, err)
continue
}
}
}
客户端
/**
* Createby GoLand
* User xzw jsjxzw@163.com
* Date 2021/8/8
* Time 上午11:43
*/
package main
import (
fmt
log
net
)
func main() {
socket, err := net.DialUDP(udp, nil, &net.UDPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: 30000,
})
if err != nil {
log.Println(连接服务器失败,err:, err)
return
}
defer socket.Close()
sendData := []byte(Hello server)
_, err = socket.Write(sendData) // 发送数据
if err != nil {
log.Println(发送数据失败,err:, err)
return
}
data := make([]byte, 4096)
n, remoteAddr, err := socket.ReadFromUDP(data) // 接收数据
if err != nil {
log.Println(接收数据失败,err:, err)
return
}
fmt.Printf(recv:%v addr:%v count:%v\n, string(data[:n]), remoteAddr, n)
}