网络知识基础以及简单的服务端和客户端编写|青训营

88 阅读5分钟

为了实现一个数据的传输,我们将网络的传输分为了4层:链路层,网络层,传输层,应用层。

链路层:每个设备要实现数据的传输,需要有一个网卡,每个网卡有一个mac地址用于标识每个人的设备。

但是如果仅有这一个层就有一个问题,在第一次发送数据的时候由于目标的mac地址未知,我们无法确定自己具体要发送给哪一台主机。但是有一个简单粗暴的方法就是对网络内的计算机全部发送一边自己要发的信息,就是广播,但是当网络内的计算机多的时候,如果发送数据的不止你一个就会导致“广播风暴”的产生。

为了解决广播风暴的问题,我们还需要有1层协议来解决,网络层提供了一个新地址:ip地址,用来给各个地方的主机来划分区域,他将一个网络分割为了多个小网络,这时候我们的机器将只会在需要找到的主机mac地址的目标的ip地址上广播信息,减少了广播风暴的发生。
现在我们有一个找到目标主机的方法:通过ip找mac:先往目标机器所在的ip地址广播信息,然后再由目标机器接受到我们发的消息,但是还有一个问题,一台设备上面可能有多个

应用程序可以接受我们发送的信息,这就需要传输层了

传输层提供了一个名为端口号的东西,一般是一个4位数字,用来甄别你发送的信息需要被哪个应用接收

应用层的作用就是用来规定你的数据应该以哪种格式被接收,用于将网络报文的格式转化成我们便于理解的格式

 

下面来实现一个能实现收发信息的服务端/客户端

首先我们先安装一个netcat然后配置环境变量

下载地址:eternallybored.org/misc/netcat…

安装教程:Netcat介绍及安装使用_netcat安装_JSON_L的博客-CSDN博客

了解以下net包我们待会要用到的函数:
net.listen(network,address)

该函数是用于创建监听器监听某个特定端口是否发来信息,

参数:

network是指使用的协议:tcp或者udp协议

Address是指监听的ip地址已经监听的端口比如127.0.0.1:8080

返回值:

Listener对象,就是监听器对象

Error,错误对象,用于产生异常用于排查

 

listen.Close()

用于关闭监听器,释放资源

 

Listen.Accept()

调用该方法后进程阻塞以等待客户端链接,有客户端连接时将返回一个connect对象与客户端通信

返回值:

Connect对象,用于与客户端进行通讯

Error,产生异常排查

 

Make([]type,len int)

产生某个数据类型的切片,len表示产生切片时的长度

参数是定义某数据类型的切片,加int参数表示切片大小

返回某个类型的数组

 

connect.Read([]byte)

用于接收客户端发来的信息,并将它封装在参数的切片中

参数:

字节数组切片,这个切片接受客户端发来的信息

返回:
整形i,用于记录客户端发送信息的长度

Error,产生异常用于排查

 

net.Dial(network,address)

该函数是用于建立 TCP 连接的函数调用。它将尝试连接到指定的地址并返回一个代表该连接的 net.Conn 对象,以及一个可能的错误。

参数:

network是指使用的协议:tcp或者udp协议

Address是指想要建立链接的地址以及端口比如127.0.0.1:8080

返回值:

connect对象,就是监听器对象

Error,错误对象,产生异常用于排查

 

connect.Write([]byte)

用于发送信息,发送的信息在参数里面

参数:

字节数组切片,这个切片存着需要发送的信息

 

代码:

服务端

创建监听器监听指定的地址

listen, err := net.Listen("tcp", "127.0.0.1:9999") //监听从127.0.0.1:9999中的链接 if err != nil {    fmt.Println(err)    **return**** **} **defer** listen.Close() //通过使用 defer listen.Close(),我们确保无论程序结束还是函数退出,都会关闭监听器并释放资源。

让服务端等待客户端的信息直到客户端信息到达

conn, err2 := listen.Accept() //程序会阻塞并等待客户端的连接请求。当有客户端连接时,Accept() 方法会返回一个 conn 对象用于与客户端进行通信。 if err2 != nil {    fmt.Println(err2)    continue

创建对象接受客户端信息然后打印他

buf := make([]byte, 1024) //创建一个1kb的切片 i, err3 := conn.Read(buf) //Read() 方法也会阻塞,直到满足读取条件(例如有足够的数据可用或者连接关闭)。 //因此,你可能需要在并发的环境中使用 Read() 方法,在单独的 goroutine 中进行读取操作,以避免阻塞主程序的执行。 if err3 != nil {    fmt.Println(err3)    continue } fmt.Println(string(buf[:i]))

服务端代码

`package main

import (
"fmt"
"net"
)

func main() {
listen, err := net.Listen("tcp", "127.0.0.1:9999") //监听从127.0.0.1:9999中的链接
if err != nil {
fmt.Println(err)
return
}
defer listen.Close() //通过使用 defer listen.Close(),我们确保无论程序结束还是函数退出,都会关闭监听器并释放资源。
for {
conn, err2 := listen.Accept() //程序会阻塞并等待客户端的连接请求。当有客户端连接时,Accept() 方法会返回一个 conn 对象用于与客户端进行通信。
if err2 != nil {
fmt.Println(err2)
continue
}
buf := make([]byte, 1024) //创建一个1kb的切片
i, err3 := conn.Read(buf)
//Read() 方法也会阻塞,直到满足读取条件(例如有足够的数据可用或者连接关闭)。
//因此,你可能需要在并发的环境中使用 Read() 方法,在单独的 goroutine 中进行读取操作,以避免阻塞主程序的执行。
if err3 != nil {
fmt.Println(err3)
continue
}
fmt.Println(string(buf[:i]))
}
}`

客户端

创建链接然后用write发送

`package main

import (
"fmt"
"net"
)

func main() {
conn, err := net.Dial("tcp", "127.0.0.1:9999")
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
str := make([]byte, 1024)
fmt.Scanf("%s\n", &str)
conn.Write([]byte(str))

}`