实现简单服务器与客户端

有些理论知识还是必须要知道的,不然只是参考别人的代码,终究是别人玩剩下来的东西。

首先是服务端的制作
因为要制作的是简单的服务端,所以服务端只需要有2个函数,一个函数作为程序的入口,也即main函数,一个函数对连接进行处理。这个对连接进行处理的函数很重要,之后再细讲。

先贴上服务端代码
package main

import (
“bufio”
“fmt”
“net”
“os”
“strings”
)

func process(conn net.Conn){
defer conn.Close()
reader:=bufio.NewReader(os.Stdin) //读取命令框里的数据
for{
var buf [1024]byte
n,err:=conn.Read(buf[:]) //n为数据的大小
if err!=nil{
fmt.Println(“从客户端读取数据失败”,err)
break
}
fmt.Println(“收到客户端数据”,string(buf[:n]))
fmt.Println(“请回复”)
msg,_:=reader.ReadString(‘\n’) //读取到换行符
msg=strings.TrimSpace(msg) //把msg前后的空白都去掉
if msg==”q”{
break //退出阻塞状态
}
conn.Write([]byte(msg)) //向连接中写入数据.
}
}

func main(){
listen,err:=net.Listen(“tcp”,”127.0.0.1:8888”)
if err!=nil{
fmt.Println(“listen faild”,err)
return
}
for{
conn,err:=listen.Accept()
if err!=nil{
fmt.Println(“accept failed,err:”,err)
continue
}
go process(conn)
}
}

对服务端代码进行分析

Conn接口代表通用的面向流的网络连接。多个线程可能会同时调用同一个Conn的方法。

func process(conn net.Conn){

}
process函数传参 net.Conn 其实就是调用 net包里的Conn接口
Conn接口 通过查询 golang中文库 可以知道它的方法有
// Read从连接中读取数据
// Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
Read(b []byte) (n int, err error)
// Write从连接中写入数据
// Write方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
Write(b []byte) (n int, err error)
// Close方法关闭该连接
// 并会导致任何阻塞中的Read或Write方法不再阻塞并返回错误
Close() error
// 返回本地网络地址
LocalAddr() Addr
// 返回远端网络地址
RemoteAddr() Addr
// 设定该连接的读写deadline,等价于同时调用SetReadDeadline和SetWriteDeadline
// deadline是一个绝对时间,超过该时间后I/O操作就会直接因超时失败返回而不会阻塞
// deadline对之后的所有I/O操作都起效,而不仅仅是下一次的读或写操作
// 参数t为零值表示不设置期限
SetDeadline(t time.Time) error
// 设定该连接的读操作deadline,参数t为零值表示不设置期限
SetReadDeadline(t time.Time) error
// 设定该连接的写操作deadline,参数t为零值表示不设置期限
// 即使写入超时,返回值n也可能>0,说明成功写入了部分数据
SetWriteDeadline(t time.Time) error

因为是最简单的服务端与客户端,所以我们不设置读写超时时间,不设置读取本地和远程地址,也就是说我们只需要用到 read 和 write即可

reader:=bufio.NewReader(os.Stdin) //这里用到bufio包里的NewReader方法去读取os.Stdin的数据 os.Stdin其实就是键盘输入的数据
这里可以理解为创建一个读取器 读取键盘数据

var buf [1024]byte
n,err:=conn.Read(buf[:])
这里是声明了一个1024字节的数组 通过 conn接口的Read方法去读取缓冲区里的数据 n为返回数据大小,err为报错信息,这里缓冲区的数据其实就是客户端发送过来的数据。

msg,:=reader.ReadString(‘\n’) //读取到换行符
msg=strings.TrimSpace(msg) //把msg前后的空白都去掉
if msg==”q”{
break //退出阻塞状态
}
conn.Write([]byte(msg))
用if判断报错就不分析了,没必要reader读取器的ReadString方法实现的是读取字符串的作用,msg,:=reader.ReadString(‘\n’) 指的是 读取到换行符的数据 保存在msg里,而且还是以字符串形式保存,
strings.TrimSpace(msg) 是调用strings包里的TrimSpace方法去除字符串两边的空格,个人觉得可有可无。。

conn.Write([]byte(msg)) 是将我们输入的数据写入到连接中

服务端main方法研究

listen,err:=net.Listen(“tcp”,”127.0.0.1:8888”)
if err!=nil{
fmt.Println(“listen faild”,err)
return
}
通过调用net的Listen方法 设置tcp连接 和 通信的ip地址与端口

for{
conn,err:=listen.Accept()
if err!=nil{
fmt.Println(“accept failed,err:”,err)
continue
}
go process(conn)
}
注意这里用到了一个阻塞状态,而且里面调用 listen.Accept() 意味着,可以不断收到信息,而不是收到了一条信息就停止了。 通过listen.Accept()获取连接的对象,go process(conn)是实现并发处理请求,这里客户端发起的请求如果没失败 就可以认为是一个conn。

客户端主要功能就是向服务器发起请求建立连接,然后输入数据,等待服务器响应
贴上代码,所以一个main函数即可。

package main

import (
“bufio”
“fmt”
“net”
“os”
)

func main(){
conn,err:=net.Dial(“tcp”,”127.0.0.1:8888”) //向127.0.0.1:8888发起tcp连接
if err!=nil{
fmt.Println(“err: “,err)
return
}
defer conn.Close()
reader:=bufio.NewReader(os.Stdin) //阅读器 读取输入的数据
for {
fmt.Println(“请发送:”)
msga,_:=reader.ReadString(‘\n’)
if msga==”q”{
break
}
conn.Write([]byte(msga))
var buf [1024]byte
n,err:=conn.Read(buf[:])
if err!=nil{
fmt.Println(“read from server faild”,err)
break
}
fmt.Println(“收到服务器数据”,string(buf[:n]))
}

}

conn,err:=net.Dial(“tcp”,”127.0.0.1:8888”) //向127.0.0.1:8888发起tcp连接
if err!=nil{
fmt.Println(“err: “,err)
return
}

通过net包的Dial方法 设置 请求方式,请求地址,conn是通过请求建立的连接对象。

defer conn.Close()
主函数执行完毕后关闭连接

reader:=bufio.NewReader(os.Stdin)
设置阅读器,用来读取客户端传过来的数据

for {
fmt.Println(“请发送:”)
msga,:=reader.ReadString(‘\n’)
if msga==”q”{
break
}
conn.Write([]byte(msga))
var buf [1024]byte
n,err:=conn.Read(buf[:])
if err!=nil{
fmt.Println(“read from server faild”,err)
break
}
fmt.Println(“收到服务器数据”,string(buf[:n]))
}
客户端开启阻塞状态是为了能够持续发送数据, msga,:=reader.ReadString(‘\n’) 通过这种方式可简单的将输入的数据保存在msga里面 conn.Write写入msga保存的数据. 设置缓冲字节数组,通过这个数组 可以读取 服务端回复的数据,然后打印到界面上。

欢迎关注我的私人博客: https://arg1nt.gitee.io/2021/03/01/go/#more
原文地址:https://www.cnblogs.com/Arg1nt/p/14490531.html