go网络编程
1. tcp
package netMode
import (
"fmt"
"net"
)
var (
protocol = "tcp"
addr = "localhost:9999"
)
func TcpServer() {
/**
tcp-server
1. 监听端口
2. 创建客户端链接
3. 接收和发送信息
4. 关闭链接
*/
listener, err := net.Listen(protocol, addr) // 监听端口
defer listener.Close()
if err != nil {
fmt.Println("listen error:", err)
return
}
for {
con, err := listener.Accept() // 建立客户端链接
if err != nil {
fmt.Println("accept error:", err)
continue
}
go func(con net.Conn) {
defer con.Close() // 关闭链接
data := make([]byte, 1024)
n, err := con.Read(data) // 接收数据
if err != nil {
fmt.Println("server-read error:", err)
return
}
recvData := string(data[:n])
fmt.Println("server-read data:", recvData)
n, err = con.Write([]byte(recvData)) // 发送数据
if err != nil {
fmt.Println("server-write error:", err)
return
}
fmt.Println("server-write data:", string(data[:n]))
}(con)
}
}
func TcpClient() {
/**
tcp-client
1. 建立链接
2. 发送和接收数据
3. 关闭链接
*/
dial, err := net.Dial(protocol, addr) // 建立链接
//dial, err := net.DialTimeout(protocol, addr, time.Second*10) // 10s超时
if err != nil {
fmt.Println("dail error:", err)
return
}
defer dial.Close() // 关闭链接
buf := make([]byte, 1024)
sendData := []byte("hello tcp")
n, err := dial.Write(sendData) // 发送数据
if err != nil {
fmt.Println("client-write error:", err)
return
}
fmt.Println("client-write data:", string(sendData))
n, err = dial.Read(buf) // 接收数据
if err != nil {
fmt.Println("client-read error:", err)
return
}
fmt.Println("client-read data:", string(buf[:n]))
}
serverOut:
server-read data: hello tcp
server-write data: hello tcp
clientOut:
client-write data: hello tcp
client-read data: hello tcp
2. udp
package netMode
import (
"fmt"
"net"
)
func UdpServer() {
/**
udp-server:
1. 监听端口
2. 接收和发送信息
3. 关闭链接
*/
listener, err := net.ListenUDP("udp", &net.UDPAddr{
net.IPv4(0, 0, 0, 0),
9999,
"",
})
defer listener.Close()
if err != nil {
fmt.Println("listen error:", err)
return
}
for {
buf := make([]byte, 1024)
n, udpAddr, err := listener.ReadFromUDP(buf) // 接收消息
if err != nil {
fmt.Println("server-read error:", err)
continue
}
fmt.Printf("server-read(client:%s) data:%s\n", udpAddr, string(buf[:n]))
sendData := []byte(udpAddr.String())
n, err = listener.WriteToUDP(sendData, udpAddr) // 发送消息
if err != nil {
fmt.Println("server-write error:", err)
continue
}
fmt.Println("server-write data:", string(sendData))
}
}
func UdpClient() {
/**
udp-client:
1. 建立链接
2. 发送和接收数据
3. 关闭链接
*/
udpAddr, err := net.ResolveUDPAddr("", ":9999") // network默认为udp
if err != nil {
fmt.Println("client-resolve error:", err)
return
}
con, err := net.DialUDP("udp", nil, udpAddr) // 建立链接
if err != nil {
fmt.Println("client-dial error:", err)
return
}
defer con.Close()
sendData := []byte("hello udp")
_, err = con.Write(sendData) // 发送消息
if err != nil {
fmt.Println("client-write error:", err)
return
}
fmt.Println("client-write:", string(sendData))
buf := make([]byte, 1024)
n, addr, err := con.ReadFromUDP(buf) // 接收消息
if err != nil {
fmt.Printf("clien-read(server:%s) error: %s\n", addr, err.Error())
return
}
fmt.Printf("client-read(server:%s) data:%s\n", addr, string(buf[:n]))
}
func UdpBroadCastServer() {
serverAddr, err := net.ResolveUDPAddr("udp4", ":9999")
if err != nil {
fmt.Println("server-resolve error:", err)
return
}
udp, err := net.ListenUDP("udp4", serverAddr)
if err != nil {
fmt.Println("server-listen error:", err)
return
}
broadCastAddr, err := net.ResolveUDPAddr("udp4", "255.255.255.255:9999") // 广播地址
if err != nil {
fmt.Println("server-resolve-broadCast error:", err)
return
}
for {
send := []byte("hello broadcast")
_, err := udp.WriteToUDP(send, broadCastAddr) // 广播形式发送数据
if err != nil {
fmt.Println("server-write error:", err)
return
}
fmt.Println("server-broadcast data:", string(send))
}
}
func UdpBroadCastClient(serverAddr *net.UDPAddr) {
udpAddr, err := net.ResolveUDPAddr("udp4", ":9999")
if err != nil {
fmt.Println("client-resolve error:", err)
return
}
udp, err := net.DialUDP("udp4", udpAddr, serverAddr)
if err != nil {
fmt.Println("client-dial error:", err)
return
}
buf := make([]byte, 1024)
n, addr, err := udp.ReadFromUDP(buf)
if err != nil {
fmt.Println("client-read error:", err)
return
}
fmt.Printf("client-read(server:%s) data:%s\n", addr, string(buf[:n]))
}
serverOut:
server-read(client:127.0.0.1:63379) data:hello udp
server-write-broadcast data: 127.0.0.1:63379
clientOut:
client-write: hello udp
client-read(server:127.0.0.1:9999) data:127.0.0.1:63379
3. http
package netMode
import (
"fmt"
"io"
"net/http"
)
func HttpServer() {
/**
http-server
1. 定义handler(路由函数)
2. 创建http服务端并监听特定端口
*/
http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("请求信息====>")
fmt.Println("addr:", r.RemoteAddr)
fmt.Println("method:", r.Method)
fmt.Println("url:", r.URL.Path)
fmt.Println("header:", r.Header)
fmt.Println("body:", r.Body)
fmt.Println("响应====>")
w.Write([]byte("ok"))
})
http.ListenAndServe(":9999", nil)
}
func HttpClient() {
/**
http-client
1. 确认请求地址
2. 构建参数发起请求
3. 获取结果
*/
resp, err := http.Get("http://localhost:9999/test")
if err != nil {
fmt.Println("error:", err)
}
defer resp.Body.Close()
buffer := make([]byte, 1024)
for {
n, err := resp.Body.Read(buffer)
if err != nil && err != io.EOF {
fmt.Println("error:", err)
break
} else {
fmt.Println("data:", string(buffer[:n]))
break
}
}
}
serverOut:
请求信息====>
addr: 127.0.0.1:52995
method: GET
url: /test
header: map[Accept-Encoding:[gzip] User-Agent:[Go-http-client/1.1]]
body: {}
响应====>
clientOut:
data: ok
4. websocket
go get -u github.com/gorilla/websocket
package netMode
import (
"fmt"
"github.com/gorilla/websocket"
"net/http"
)
func WebSocketServer() {
// websocket配置
upgrader := websocket.Upgrader{
HandshakeTimeout: 0, // 升级websocket握手完成的超时时间
ReadBufferSize: 1024, // io操作读缓冲区大小
WriteBufferSize: 1024, // // io操作写缓冲区大小
WriteBufferPool: nil, // io操作写缓存池大小
Subprotocols: nil, // 服务支持的协议 []string
Error: nil, // 异常响应处理函数 func(w http.ResponseWriter, r *http.Request, status int, reason error)
CheckOrigin: func(r *http.Request) bool {
return true
}, // origin头信息检测
EnableCompression: false, // 是否数据压缩
}
// 创建http服务器
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
// 升级http协议为websocket
con, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println("websocket-upgrade-error:", err)
return
}
defer con.Close()
// 发送消息
sendData := []byte("server-websocket")
err = con.WriteMessage(websocket.TextMessage, sendData)
if err != nil {
fmt.Println("server-write-error:", err)
return
}
fmt.Println("server-write-data:", string(sendData))
// 接收消息
messageType, p, err := con.ReadMessage()
if err != nil {
fmt.Println("server-read-error:", err)
return
}
fmt.Println("server-read-data:", string(p), messageType)
})
// 端口监听
http.ListenAndServe(":9999", nil)
}
func WebSocketClient() {
// 链接websocket服务端(一次握手)
dial, _, err := websocket.DefaultDialer.Dial("ws://localhost:9999/ws", nil)
if err != nil {
fmt.Println("client-connect-error:", err)
return
}
defer dial.Close()
// 发送消息
sendData := []byte("client-websocket")
err = dial.WriteMessage(1, sendData)
if err != nil {
fmt.Println("client-write-error:", err)
return
}
fmt.Println("client-write-data:", string(sendData))
// 接收消息
messageType, p, err := dial.ReadMessage()
if err != nil {
fmt.Println("client-read-error:", err)
return
}
fmt.Println("client-read-data:", string(p), messageType)
}
serverOut:
server-write-data: server-websocket
server-read-data: client-websocket 1
clientOut:
client-write-data: client-websocket
client-read-data: server-websocket 1
5. rpc
https://books.studygolang.com/go-rpc-programming-guide/part1/gorpc.html
package netMode
import (
"fmt"
"net"
"net/http"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
quo.Quo = args.A + args.B
quo.Rem = args.A - args.B
return nil
}
func RpcServer() {
/**
官方例子
1. 定义输入和输出参数的数据结构
2. 定义服务对象 Arith 可以是基本类型、也可以是interface{}
3. 定义2个服务方法 Multiply|Divide
4. 实现rpc服务器
*/
// 生成服务对象
arith := new(Arith)
// 注册服务
rpc.Register(arith)
// 服务业务处理绑定
rpc.HandleHTTP()
// tcp监听
listen, err := net.Listen("tcp", ":9999")
if err != nil {
fmt.Println("listen error:", err)
return
}
// 与http客户端连接 并新建一个新的协程处理
go http.Serve(listen, nil)
select {}
}
func RpcClient() {
// 连接服务端
dial, err := rpc.DialHTTP("tcp", ":9999")
if err != nil {
fmt.Println("client-connect-error:", err)
return
}
defer dial.Close()
args := Args{10, 11}
var reply int
// 同步远程调用
err = dial.Call("Arith.Multiply", args, &reply)
if err != nil {
fmt.Println("client-sync-call-error:", err)
return
}
fmt.Printf("client-sync-call-data(%d*%d):%d\n", args.A, args.B, reply)
// 异步远程调用
quotient := new(Quotient)
call := dial.Go("Arith.Divide", args, quotient, nil)
fmt.Println("client-async-call-method:", call.ServiceMethod)
fmt.Println("client-async-call-args:", call.Args)
fmt.Println("client-async-call-done:", <-call.Done)
fmt.Println("client-async-call-reply:", call.Reply)
}
clientOut:
client-call-data(10*11):110
call-method: Arith.Divide
call-args: {10 11}
call-done: &{Arith.Divide {10 11} 0xc0001a2b80 <nil> 0xc000056540}
call-reply: &{21 -1}
6. grpc
1. grpc参考文档
https://www.topgoer.cn/docs/grpc/grpc-1d2ud3fh1d74h
2. 前置要求
2.1 go环境已添加到环境变量
2.2 protoc编译器下载
windows下载protoc-27.2-win64.zip、linux选择版本下载
https://github.com/protocolbuffers/protobuf/releases
2.3 protobuf文件所需exe应用和grpc服务所需依赖
go get -u google.golang.org/grpc
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
2.4 将下载的exe迁移到go环境/bin目录下
|----bin
|----bin/go.exe
|----bin/protoc.exe
|----bin/protc-gen-go.exe
|----bin/protoc-gen-go-grpc.exe
2.5 编写proto文件
2.6 命令生成当前开发语言适配的源码
protoc --go_out=. --go-grpc_out=. proto/test.proto
2.7 实例
|----netMode
|----netMode/grpcDemo.go
|----netMode/proto/test.proto
|----netMode/proto/test.pb.go
|----netMode/proto/test_grpc.pb.go
// proto文件写法
// 1.syntax: 指定协议版本
syntax="proto3";
// 2.package:指定包名
package <包名>;
// 2.1 go语言包含该选项
option go_package="包名";
// 3.message:指定输入输出信息;驼峰命名message;
message TestReq {
// 内部字段名规范
// 格式: 修饰符 数据类型 字段名 = 序号值 [default=默认值]
// 修饰符: required(必选字段 不传使用类型默认值)|optional(可选-默认)|repeated(多个)
// 数据类型: bool|double|float|int32|uint32|int64|uint64|sint32|sint64|fixed32|fixed64|sfixed32|sfixed64|string|bytes|enum|message
// 字段名: 下划线驼峰 eg: a_b
// 序号值: 范围(1~2^32); 1~15效率最高; 1900~2000不建议使用 protobuf内部使用;
string per_name = 1;
int32 per_age = 2;
Status status = 3;
map<string,string> info = 4;
}
message TestRes {
string msg = 1;
string code = 2;
}
// 3.1数据类型enum介绍;驼峰命名;字段名用大写字母加下划线;字段值必须有个为0;
enum Status {
IS_OK = 0;
IS_FAIL = 1;
}
// 4.service: 指定rpc服务接口;驼峰式命名service
service MyService {
rpc Query (TestReq) returns (TestRes){}
}
// ps: protoc 编译器生成*.proto
protoc --go_out=. --go-grpc_out=. proto/test.proto
syntax="proto3";
option go_package = "/proto";
service TestService {
rpc Query (TestReq) returns (TestRes){}
}
message TestReq {
string name = 1;
int32 age = 2;
enum Sex {
male = 0;
female = 1;
}
Sex sex = 3;
repeated string score = 4;
}
message TestRes {
map<string, string> info = 1;
}
package netMode
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"net"
"strings"
pb "workProject/src/apps/netMode/proto"
)
/**
windows下载protoc-27.2-win64.zip
https://github.com/protocolbuffers/protobuf/releases
go get -u google.golang.org/grpc
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
protoc --go_out=. --go-grpc_out=. po/test.po
*/
type GrpcServer_ struct {
*pb.UnimplementedTestServiceServer
}
func (server *GrpcServer_) Query(ctx context.Context, req *pb.TestReq) (res *pb.TestRes, err error) {
fmt.Println("server-get-req:", req)
resMap := map[string]string{
"name": req.GetName(),
"age": string(req.GetAge()),
"sex": req.GetSex().String(),
"score": fmt.Sprintf(strings.Join(req.GetScore(), ",")),
}
res = &pb.TestRes{
Info: resMap,
}
return res, nil
}
func GrpcServer() {
// 创建tcp连接
listen, err := net.Listen("tcp", ":9999")
if err != nil {
fmt.Println("server-listen-error:", err)
return
}
// 实例grpc服务端
server := grpc.NewServer()
// 服务注册
pb.RegisterTestServiceServer(server, &GrpcServer_{})
defer func() {
server.Stop()
listen.Close()
}()
fmt.Println("server-listen:9999")
// 服务启动
err = server.Serve(listen)
if err != nil {
fmt.Println("server-run-error:", err)
return
}
}
func GrpcClient() {
// 新建连接
conn, err := grpc.NewClient(":9999", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println("client-connect-error:", err)
return
}
defer conn.Close()
// 新建客户端
client := pb.NewTestServiceClient(conn)
// 新建请求参数
req := pb.TestReq{
Name: "张三",
Age: 18,
Sex: 0,
Score: []string{"10", "20", "30"},
}
// 调用接口服务
query, err := client.Query(context.TODO(), &req)
if err != nil {
fmt.Println("client-req-error:", err)
return
}
fmt.Println("client-req-data:", query.GetInfo())
}
serverOut:
server-listen:9999
server-get-req: name:"张三" age:18 score:"10" score:"20" score:"30"
clientOut:
client-req-data: map[age: name:张三 score:10,20,30 sex:male]