Go网络编程

一、概述

在计算机世界,两个或多个计算机之间的通信是基于OSI(Open System Interconnection)开放式系统互联协议进行网络通信。该通信协议分为7层,然而实际上在现实世界中OSI并没有大规模使用,现实世界常用的是tcp/ip协议,而tcp/ip协议可划分为4或5层。

  • 应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
  • 应用层,传输层,网络层,数据链路层,物理层
  • 应用层,传输层,网络层,网络接口层

关系如图:
在这里插入图片描述
可以看出4层模型是由5层合并得到,5层模型是由7层归纳合并得到。
在5层模型结构中:
应用层(http,ftp协议,一些自定义协议:数据的加密,规定应用程序的数据格式)
传输层(tcp,udp协议:建立port到port的通信协议)
网络层(ip协议:包装本,目标网ip信息协议)
数据链路层(Ethernet以太网协议:包装了本地MAC和目标MAC地址相关信息协议)
物理层(将以上各层的数据打包层二进制发送给目标网络,目标网络反向逐层解析数据)

二、Go中的网络编程

在上述的模型中,在应用层和传输层中,基于传输层抽象了一些列接口而此接口即用于我们的实际开发中为socket(套接字)。
socket也叫套接字, 是为了方便程序员进行网络开发而被设计出来的编程接口. socket在七层模型中是属于传输层(TCP,UDP协议)之上 的一个抽象接口.
Go语言的标准库net包里对socket封装了一些列实现网络通信的api

1、基于tcp

tcp协议:两网络通信之前需要建立链接,其处于OSI 7层模型中的传输层。其建立端到端的通信协议,其链接和断开有3次握手和4次挥手的特点。
相关api:

server端:
  socket,_ := net.Listen("tcp","127.0.0.1:8080")
  socket.Accept()
  conn.Read([]byte) 
  conn.Write([]byte)
  defer -> socket.Close() conn.Close() 
client端:
  conn,_ := net.Dial("tcp","127.0.0.1:8080")
  conn.Read([]byte) 
  conn.Write(data)
  defer -> conn.Close()

server端通过net.Listen方法获取到socket对象,通过此socket的Accept()阻塞等待一个client端的一次链接。当链接建立成功后拿到conn通道对象,通过其Read(),Write()方法即可向该链接通道里读写数据,继而实现网络通信。在方法结束或对方结束通信后应该关闭socket和链接conn通道。
client端通过net.Dai()方法建立一个通信链接conn,通过其Read(),Write()方法实现其个server之间的通信。
注意当通道conn里没有数据时Read()方法为一阻塞状态,故可以通过for循环来实现不断的获取对方发送来的数据。

server:

package main

import (
	"fmt"
	"io"
	"net"
	"strings"
)
/**
结论:
0.listener.Accept()在client未Dial请求链接前为阻塞
1.无论server还是clien端只要conn.Close()关闭了,自己的conn.Read()方法就会在循环里不断调用,读取的字节大小n为0,err为EOF.故要做判断。
2.conn.Read()方法在conn链接未关闭前,没接收到数据是阻塞的。
3.conn.Write()方法向对方发送二进制流数据。
 */
//服务端
func main(){
	socket,err := net.Listen("tcp","127.0.0.1:8080")
	checkError(err)
	defer socket.Close()
	fmt.Println("socket server start ...")
	
	for{
		conn,err := socket.Accept()//阻塞于此,等待client端的链接请求
		go handlerConn(conn,err)//开启协程独立处理每条client的链接请求与响应
	}
}
func checkError(err error){
	if err!=nil {
		panic(err)
	}
}
func handlerConn(conn net.Conn,err error){
	defer conn.Close()
	checkError(err)
	for{
		//读取客户端发来的信息
		receiveData := make([]byte,4)
		n,resErr:=conn.Read(receiveData) //阻塞等待client端发送到conn链接通道数据。n为每次读取到的字节数,将每次读取的数据最大以len(receiveData)字节长度存储到切片中,若超过则再遍历一次进行读取后段数据
		data := receiveData[0:n] //切片切取到实际数据量
		if  strings.ToUpper(strings.TrimSpace(string(data))) == "EXIT" {
			conn.Close()
			fmt.Println("client结束了聊天!")
			return
		}
		if n==0 && resErr == io.EOF {
			conn.Close()
			return
		}
		fmt.Printf("收到client信息:%d %v %s",n,resErr,string(receiveData))
	}
}

client:

package main

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

//客户端
func main(){
	conn,err := net.Dial("tcp","127.0.0.1:8080")
	checkError(err)
	defer conn.Close()//关闭链接

	for{
		//监听输入,将输入发送给服务端
		fmt.Println("input>>")
		r := bufio.NewReader(os.Stdin)
		data,_,_ := r.ReadLine()
		conn.Write(data)
		if strings.ToUpper(string(data)) == "EXIT" {
			break //跳出循环,结束程序
		}
	}
}
func checkError(err error){
	if err!=nil {
		panic(err)
	}
}
  1. 1v1: 以上为server To Client端1v1的方式实现通信
  2. nvn: 可以想到server端在和多个client建立链接conn后我们可以对这些链接进行一些列的处理,将2. client之间发送来到信息进行指定链接通道的写入。从而实现多个client之间的通信。此时的server就相当于一个信息中转站的角色。
2、基于udp

udp协议:是一种不需要建立链接,故存在数据可能会丢失的情况,不可靠。但其速度快,UCP不维护连接状态,也不跟踪这些参数,开销小。空间和时间上都具有优势。UDP也常用于多媒体应用(如IP电话,实时视频会议,直播,流媒体等)数据的可靠传输对他们而言并不重要,TCP的拥塞控制会使他们有较大的延迟,也是不可容忍的。
相关api:

server:
	udpAddr,err := net.ResolveUDPAddr("udp","127.0.0.1:8080") //创建server端地址结构
	socket,err := net.ListenUDP("udp",udpAddr)  //创建socket
	socket.ReadFromUDP(data) //读取client端数据
	socket.WriteToUDP()  // 向client发送数据
	defer -> socket.Close()
client:
	conn,_ := net.Dial("udp","127.0.0.1:8080")
	conn.Read([]byte) 
	conn.Write(data)
	defer -> conn.Close()

server:

package main

import (
	"fmt"
	"io"
	"net"
)

func main(){
	udpAddr,err := net.ResolveUDPAddr("udp","127.0.0.1:8080")
	checkError(err)
	socket,err := net.ListenUDP("udp",udpAddr)
	checkError(err)
	//读取client端信息
	for{
		data := make([]byte,1024)
		n,_,err:=socket.ReadFromUDP(data)
		if n == 0 && err == io.EOF {
			break
		}
		fmt.Println("收到client端信息:",string(data[0:n]))
	}
	//向client写数据
	//udpConn.WriteToUDP()
	defer  socket.Close()
}
func checkError(err error){
	if err!=nil {
		panic(err)
	}
}

client:

package main

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

//客户端
func main(){
	conn,err := net.Dial("udp","127.0.0.1:8080")
	checkError(err)
	defer conn.Close()//关闭链接

	for{
		//监听输入,将输入发送给服务端
		fmt.Println("input>>")
		r := bufio.NewReader(os.Stdin)
		data,_,_ := r.ReadLine()
		conn.Write(data)
		if strings.ToUpper(string(data)) == "EXIT" {
			break //跳出循环,结束程序
		}
	}
}
func checkError(err error){
	if err!=nil {
		panic(err)
	}
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值