1. 服务端

package main

import (
"bufio"
"fmt"
"net"
"os"
"strings"
) // tcp server端
func processConn(conn net.Conn) {
defer conn.Close()
// 3. 与客户端通信
var tmp [128]byte
reader := bufio.NewReader(os.Stdin)
for {
n, err := conn.Read(tmp[:])
if err != nil {
fmt.Println("read from conn failed,err:", err)
return
}
fmt.Println(string(tmp[:n]))
fmt.Print("请回复:")
msg, _ := reader.ReadString('\n') // 读到换行
msg = strings.TrimSpace(msg)
if msg == "exit" {
break
}
conn.Write([]byte(msg))
}
} func main() {
// 1. 本地端口启动服务
listener, err := net.Listen("tcp", "127.0.0.1:20000")
if err != nil {
fmt.Println("start tcp server on 127.0.0.1:20000 failed, err:", err)
return
}
// 2. 等待别人来跟我建立连接
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("accept failed,err:", err)
return
}
go processConn(conn)
} }

2. 客户端 

package main

import (
"bufio"
"fmt"
"net"
"os"
"strings"
) // tcp client func main() {
// 1. 与server端建立连接
conn, err := net.Dial("tcp", "127.0.0.1:20000")
if err != nil {
fmt.Println("dial 127.0.0.1:20000 failed,err:", err)
return
}
// 2. 发送数据
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("请说话:")
msg, _ := reader.ReadString('\n') // 读到换行
msg = strings.TrimSpace(msg)
if msg == "exit" {
break
}
conn.Write([]byte(msg))
}
conn.Close()
}

3. 黏包

为什么会出现粘包

主要原因就是tcp数据传递模式是流模式,在保持长连接的时候可以进行多次的收和发。

“粘包”可发生在发送端也可发生在接收端:

  1. 由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法。简单来说就是当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去。
  2. 接收端接收不及时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后通知应用层取数据。当应用层由于某些原因不能及时的把TCP的数据取出来,就会造成TCP缓冲区中存放了几段数据。

解决办法

出现”粘包”的关键在于接收方不确定将要传输的数据包的大小,因此我们可以对数据包进行封包和拆包的操作。

封包:封包就是给一段数据加上包头,这样一来数据包就分为包头和包体两部分内容了(过滤非法包时封包会加入”包尾”内容)。包头部分的长度是固定的,并且它存储了包体的长度,根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完整的数据包。

我们可以自己定义一个协议,比如数据包的前4个字节为包头,里面存储的是发送的数据的长度。

package main

import (
"bufio"
"fmt"
"io"
"net" proto "code.oldboyedu.com/day08/09nianbao_jiejue/protocol"
) // socket_stick/server/main.go func process(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
recvStr, err := proto.Decode(reader)
if err == io.EOF {
return
}
if err != nil {
fmt.Println("decode failed,err:", err)
return
}
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" proto "code.oldboyedu.com/day08/09nianbao_jiejue/protocol"
) // 黏包 client // socket_stick/client/main.go 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, Hello. How are you?`
// 调用协议编码数据
b, err := proto.Encode(msg)
if err != nil {
fmt.Println("encode failed,err:", err)
return
}
conn.Write(b)
// time.Sleep(time.Second)
}
}
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) //Buffer缓冲区
// 写入消息头
err := binary.Write(pkg, binary.LittleEndian, length)
if err != nil {
return nil, err
}
// 写入消息实体
err = binary.Write(pkg, binary.LittleEndian, []byte(message)) //binary.LittleEndian小段(低位在最右边)
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, 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
}

Go网络编程TCP的更多相关文章

  1. GO语言练习:网络编程 TCP 示例

    1.代码 2.编译及运行 1.网络编程 TCP 示例 simplehttp.go 代码 package main import ( "net" "os" &qu ...

  2. 网络编程TCP协议-聊天室

    网络编程TCP协议-聊天室(客户端与服务端的交互); <span style="font-size:18px;">1.客户端发数据到服务端.</span> ...

  3. C#网络编程TCP通信实例程序简单设计

    C#网络编程TCP通信实例程序简单设计 采用自带 TcpClient和TcpListener设计一个Tcp通信的例子 只实现了TCP通信 通信程序截图: 压力测试服务端截图: 俩个客户端链接服务端测试 ...

  4. Socket网络编程(TCP/IP/端口/类)和实例

    Socket网络编程(TCP/IP/端口/类)和实例 原文:C# Socket网络编程精华篇 转自:微冷的雨 我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念: TCP/IP层次 ...

  5. 网络编程——TCP协议、UDP协议、socket套接字、粘包问题以及解决方法

    网络编程--TCP协议.UDP协议.socket套接字.粘包问题以及解决方法 TCP协议(流式协议) ​ 当应用程序想通过TCP协议实现远程通信时,彼此之间必须先建立双向通信通道,基于该双向通道实现数 ...

  6. Socket网络编程-TCP编程

    Socket网络编程-TCP编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.socket介绍 1>.TCP/IP协议 2>.跨网络的主机间通讯 在建立通信连接的 ...

  7. 32.网络编程TCP/UDP服务

    网络编程TCP: 服务器端口了解: port:0~65535 web服务:80 邮箱服务:556 0~1024:为服务默认的公认端口,一般我们不能用 套接字:socket socket作用 ip:po ...

  8. 网络编程TCP/IP详解

    网络编程TCP/IP详解 1. 网络通信 中继器:信号放大器 集线器(hub):是中继器的一种形式,区别在于集线器能够提供多端口服务,多口中继器,每个数据包的发送都是以广播的形式进行的,容易阻塞网络. ...

  9. java 网络编程-tcp/udp

    --转自:http://blog.csdn.net/nyzhl/article/details/1705039 直接把代码写在这里,解释看这里吧:http://blog.csdn.net/nyzhl/ ...

  10. 网络编程TCP/IP实现客户端与客户端聊天

    一.TCP/IP协议 既然是网络编程,涉及几个系统之间的交互,那么首先要考虑的是如何准确的定位到网络上的一台或几台主机,另一个是如何进行可靠高效的数据传输.这里就要使用到TCP/IP协议. TCP/I ...

随机推荐

  1. ASP.NET常用内置对象(三)Server

    Server对象是HttpServerUtility的一个实例,也是上下文对象HttpContext的一个属性,提供用于处理Web请求的Helper方法. Server.MapPath("& ...

  2. 微信小程序报错TypeError: this.setData is not a function

    今天在练习小程序的时候,遇到小程序报错 对于处于小白阶段的我,遇到这种报错,真还不知道是错从何来,只有一脸蒙逼,后来通过查询,终于知道了问题所在,下面对这一问题做一记录 小程序默认中是这么写的 onL ...

  3. First Kernel-pwn

    Kernel pwn-极简题目的操作模式 完全参照M4x师傅的指导,用 hacklu的baby kernel迈了第一步 题目附带文件说明 一般题目会给出bzImage,.cpio, .sh文件 sh文 ...

  4. Mysql 出现许多问号的问题

    建数据库的时候,已经选择了编码格式为UTF-8 但是用PDM生成的脚本导进去的时候却奇怪的发现表和表的字段的编码格式却是GBK,一个一个却又觉得麻烦,在网上找了一下办法 一个是修改表的编码格式的 AL ...

  5. Nginx+uWSGI部署flask项目

    uwsgi配置 uwsgi安装 安装uwsgi pip install uwsgi 启动uwsgi uwsgin --ini uwsgi.ini # 后台启动 nohup uwsgi --ini uw ...

  6. 使用U盘装Windows10系统

    一.装备工作 使用U盘装系统需要准备以下工具: 8G左右的U盘一个.由于制作启动盘会删除U盘的所有数据,所以重要资料请提前备份. 系统的镜像文件.这里我推荐MSDN, 我告诉你.这里下载的镜像和官方的 ...

  7. Ubuntu OS 打开端口命令

    直接执行命令:ufw allow 80 再去修改对应功能配置文件的端口号

  8. POJ 2096 Collecting Bugs (概率DP,求期望)

    Ivan is fond of collecting. Unlike other people who collect post stamps, coins or other material stu ...

  9. 题解【AcWing95】费解的开关

    题面 一道非常好的递推练习题. 我们考虑每次枚举第一行的操作,由上一行的状态递推出下一行的状态,最后判断最后一行是否全部为 \(1\) 即可. 实现代码时要注意一些细节问题. #include < ...

  10. 转载:arm neon intrinsic

    转自:https://blog.csdn.net/hemmingway/article/details/44828303/ https://blog.csdn.net/chshplp_liaoping ...