首页 > 编程语言 >12_网络编程

12_网络编程

时间:2023-10-08 13:23:19浏览次数:46  
标签:12 nil err fmt 编程 网络 Println Hello conn

一、TCP编程

​ 使用 Go 语言的 net 包实现 TCP服务端和 TCP客户端。

image-20220924154349507

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()
}

运行测试:

image-20220924154453086 image-20220924154521619

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)
	}
}

运行结果:

image-20220924171346520

二、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)
}

运行测试:

image-20220924161213353image-20220924161224713

image-20220924161239460

三、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
		}
	}
}

结果响应:

image-20220924174548712 image-20220924174614106

标签:12,nil,err,fmt,编程,网络,Println,Hello,conn
From: https://www.cnblogs.com/istitches/p/17748648.html

相关文章

  • 14_Http编程
    2、性能更高的第三方库HttpRouterhttps://github.com/julienschmidt/httprouterhttps://pkg.go.dev/github.com/julienschmidt/httprouter2.1HttpRouter与net/http比较HttpRouter相较于Go语言官方库net/http性能更高,它支持URL中携带访问参数、支持多种访问类型(G......
  • Python入门示例系列12 数据类型转换
    Python入门示例系列12数据类型转换 type()函数使用type()函数可以查看数据类型。示例:>>>type(123)<class'int'>>>>type(12.3)<class'float'>>>>type("abc")<class'str'>>>>type([1,2,3])......
  • Debian12安装podman
    目录前奏更换国内源安装podman安装方法1安装方法2安装后设置示例1示例2关于docker镜像列表(非podman)前奏更换国内源rambo@debian:~$sudocat/etc/issueDebianGNU/Linux12\n\lrambo@debian:~$cat/etc/apt/sources.listdebhttps://mirrors.aliyun.com/debian/boo......
  • 网络规划设计师真题解析--独立磁盘冗余阵列(二)(容量的计算)
    假如有3块容量是160G的硬盘做RAID5阵列,则这个RADI5的容量是(1);而如果有2块160G的盘和1块80G的盘,此时RAID5的容量是(2)。(1)A.320G    B.160G     C.80G     D.40G(2)A.40G     B.80G     C.160G    D.200G答案:(1)A (2)C解析:常见的RAID......
  • 深度学习---目标检测网络YoloX
    一、网络介绍YoloX由旷视科技开源,以YoloV3(Darknet53作为backbone)作为基线,最大的区别在于DecoupledHead,DataAug,AnchorFree和样本匹配(SimOTA)这几个方面,另外还提供了完善的代码,并很贴心的提供了部署脚本,真的很友好了。PDF:https://arxiv.org/pdf/2107.08430.pdfCode:http......
  • 大型网络拓扑
     syssysnamesw1/sw2/sw3/sw4undoinenvlanbatch10203040inte0/0/1portlink-typeaccessportdefaultvlan10/20/30/40,(sw4inte0/0/2加入vlan40)displayvlan displayipinterfacebriefdisplayiprouting-table displayiprouting-table|include/......
  • 网络性能分析
         pktgen使用: 是什么?高性能的发包工具Linuxkernel的一个模块不是什么?不是用户态的工具软件(所以不能用敲命令行的方式来运行pktgen)不是全能的发包工具,只支持UDP报文安装一般来讲,我们的系统上都有pktgen的模块,只不过没有运行而已.下面......
  • CUDA C编程权威指南:2.1-CUDA编程模型
      本文主要通过例子介绍了CUDA异构编程模型,需要说明的是Grid、Block和Thread都是逻辑结构,不是物理结构。实现例子代码参考文献[2],只需要把相应章节对应的CMakeLists.txt文件拷贝到CMake项目根目录下面即可运行。1.Grid、Block和Thread间的关系  GPU中最重要的2种内存是全局......
  • C# 12 中的新增功能
    新的C#12功能在预览版中已经引入.您可以使用最新的VisualStudio预览版或最新的.NET8预览版SDK来尝试这些功能。以下是一些新引入的功能:主构造函数集合表达式默认Lambda参数任何类型的别名内联数组拦截器使用nameof访问实例成员主构造函数现在可以在任......
  • Java网络编程
    InetAddress端口协议......