Golang tcp socket编程案例

Golang tcp socket编程案例

      • 简单的介绍
      • 步骤代码实现
        • 1、定义一个 server(服务端) 结构体以及获取它的函数(服务端)
        • 2、编写服务端的启动方法(服务端)
        • 3、定义一个 client(客户端) 结构体以及获取它的函数(客户端)
        • 4、编写客户端启动方法(客户端)
        • 5、服务端启动,客户端是否能连接上?(小测试)
          • 5.1、服务器端启动
          • 5.2、服务器端启动的效果
          • 5.3、客户端启动
          • 5.4、客户端启动的效果
          • 5.5、测试客户端链接服务器端的效果
        • 6、编写服务端处理客户端消息代码(服务端)
          • 6.1、在 StartServer() 方法中 加入 go process(conn),并且加上时间打印的效果
          • 6.2、process函数
          • 6.3、StartServer方法完整的代码
        • 7、编写客户端发送消息代码(客户端)
          • 7.1、**注意**这里的客户端有了小改动,包括结构体
          • 7.2 客户端发送消息的方法编写
        • 8、客户端链接服务器端(客户端)
        • 9、重新编译使客户端与服务器端进行交互
          • 9.1、服务器端启动效果
          • 9.2、客户端A向服务器端发送消息
          • 9.3、服务器端接收到客户端的消息
      • 以上示例完整的服务器端实现代码
      • 以上示例完整的客户端实现代码
      • 客户端改造
        • 效果如下
      • 代码地址

简单的介绍

  1. 项目代码基于 Go mod 方式
  2. 纯Golang编写
  3. 基于 golang net 包
  4. 代码请以最终整理的代码为主
  5. 本人菜鸡一枚,代码写得不好,望各位大佬口下留情~

步骤代码实现

1、定义一个 server(服务端) 结构体以及获取它的函数(服务端)

package server

type server struct {
	// 网络协议类型
	networkType string
	// ip地址 + 端口
	ipAddress string
}

// NewServer 通过此函数可以得到一个 server 结构体
func NewServer(ipAddress string) server {
	if ipAddress == "" {
		ipAddress = "127.0.0.1:8088"
	}
	return server{
		// 默认值 -- 方便开发
		networkType: "tcp",
		ipAddress: ipAddress,
	}
}

2、编写服务端的启动方法(服务端)

// StartServer 启动服务端的方法
func (s server) StartServer() {

	// 1. 表示使用的网络协议:tcp
	// 2. 对本机进行监听 ip为 xxx.xxx.xxx.xxx + 端口为 xxx
	listener, err := net.Listen(s.networkType, s.ipAddress)
	if err != nil {
		panic(fmt.Sprintf("服务器启动失败, err = %v \n", err))
	}

	fmt.Printf("服务器端启动成功, %v \n", listener.Addr().String())

	// 服务器不断的接收客户端的信息
	for {
		conn, err := listener.Accept()
		// 客户端的信息
		remoteAddr := conn.RemoteAddr()
		if err != nil {
			fmt.Printf("客户端: %v 链接服务器端出现异常, err = %v \n", remoteAddr.String(), err)
			// 跳过此客户端链接不在往下执行
			continue
		}
		fmt.Printf("客户端: %v 链接服务器端成功 \n", remoteAddr.String())
		// 处理客户端的消息..
	}
}

3、定义一个 client(客户端) 结构体以及获取它的函数(客户端)

package client

import "net"

type client struct {
	// 网络协议类型
	networkType string
	// ip地址 + 端口
	ipAddress string
}

// NewClient 通过此函数可以得到一个 client 结构体
func NewClient(ipAddress string) client {
	return client{
		networkType: "tcp",
		ipAddress:   ipAddress,
	}
}

4、编写客户端启动方法(客户端)

// StartClient 客户端启动方法
func (c client) StartClient() {
	conn, err := net.Dial(c.networkType, c.ipAddress)
	if err != nil {
		fmt.Printf("链接服务器端: %v , 出现异常 err = %v\n", c.ipAddress, err)
		return
	}
	fmt.Println("链接服务器端成功", conn)
}

5、服务端启动,客户端是否能连接上?(小测试)

5.1、服务器端启动
package main

import "tcp_socket_demo/server"

func main() {

	// 得到一个服务器端的结构体
	server := server.NewServer("")
	// 调用启动的方法
	server.StartServer()
	
}
5.2、服务器端启动的效果

Golang tcp socket编程案例_第1张图片

5.3、客户端启动
package main

import (
	"tcp_socket_demo/client"
)

func main() {
	client := client.NewClient("127.0.0.1:8088")
	client.StartClient()
}
5.4、客户端启动的效果

Golang tcp socket编程案例_第2张图片

5.5、测试客户端链接服务器端的效果

Golang tcp socket编程案例_第3张图片

6、编写服务端处理客户端消息代码(服务端)

6.1、在 StartServer() 方法中 加入 go process(conn),并且加上时间打印的效果
// 获取当前运行的时间
func nowTime() string {
	return time.Now().Format("2006-01-02 15:04:05")
}
6.2、process函数
// 用于处理客户端消息的函数
func process(conn net.Conn) {
	// 客户端断开链接后要及时关闭
	defer conn.Close()
	connRemoteAddr := conn.RemoteAddr().String()
	// 循环读取客户端的消息
	for {
		// 创建一个1024长度的 byte 切片用于存储客户端的消息
		bytes := make([]byte, 1024)
		// 等待客户端通过 conn 发送消息
		// 如果客户端一直没有发送,那么此协程就阻塞在这里
		readLen, err := conn.Read(bytes)
		if err != nil {
			errStr := err.Error()
			contains := strings.Contains(errStr, "An existing connection was forcibly closed by the remote host")
			if contains || errStr == "EOF" {
				fmt.Printf("客户端[%v]: %v 断开链接\n", nowTime(), connRemoteAddr)
				return
			} else {
				fmt.Printf("服务器读取客户端[%v]: %v 消息出现异常, err = %v \n", nowTime(), connRemoteAddr, err)
				return
			}
		}
		// 服务器端显示客户端的消息
		// bytes[:readLen] ==> 只打印有效的数据长度
		fmt.Printf("server[%v]-客户端[%v]说: %v\n", nowTime(), connRemoteAddr, string(bytes[:readLen]))
	}
}
6.3、StartServer方法完整的代码
// StartServer 启动服务端的方法
func (s server) StartServer() {

	// 1. 表示使用的网络协议:tcp
	// 2. 对本机进行监听 ip为 xxx.xxx.xxx.xxx + 端口为 xxx
	listener, err := net.Listen(s.networkType, s.ipAddress)
	if err != nil {
		panic(fmt.Sprintf("服务器启动失败, err = %v \n", err))
	}

	fmt.Printf("服务器端启动成功, %v , time: %v \n", listener.Addr().String(), nowTime())

	// 服务器不断的接收客户端的信息
	for {
		conn, err := listener.Accept()
		// 客户端的信息
		remoteAddr := conn.RemoteAddr()
		if err != nil {
			fmt.Printf("客户端[%v]: %v 链接服务器端出现异常, err = %v \n", nowTime(), remoteAddr.String(), err)
			// 跳过此客户端链接不在往下执行
			continue
		}
		fmt.Printf("客户端[%v]: %v 链接服务器端成功 \n", nowTime(), remoteAddr.String())
		// 处理客户端的消息..
		// 有多个客户端链接,就有多少个协程
		go process(conn)
	}
}

7、编写客户端发送消息代码(客户端)

7.1、注意这里的客户端有了小改动,包括结构体

代码如下

package client

import (
	"fmt"
	"net"
	"time"
)

type client struct {
	// 网络协议类型
	networkType string
	// ip地址 + 端口
	ipAddress string
	// 当前的链接
	conn net.Conn
}

// NewClient 通过此函数可以得到一个 client 结构体
func NewClient(ipAddress string) client {
	return client{
		networkType: "tcp",
		ipAddress:   ipAddress,
	}
}

// 获取当前运行的时间
func nowTime() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

// StartClient 客户端启动方法
func (c *client) StartClient() {
	conn, err := net.Dial(c.networkType, c.ipAddress)
	if err != nil {
		fmt.Printf("链接服务器端: %v ,time = %v ,出现异常 err = %v\n", c.ipAddress, nowTime(), err)
		return
	}
	fmt.Printf("[%v]链接服务器端:[%v]成功\n", nowTime(), conn)
	c.conn = conn
}
7.2 客户端发送消息的方法编写
// SendMessage 发送消息
func (c *client) SendMessage(msg string) {
	writeLen, err := c.conn.Write([]byte(msg))
	if err != nil {
		fmt.Printf("[%v]客户端发送消息至服务器失败, err = %v\n", nowTime(), err)
		return
	}
	fmt.Printf("[%v]客户发送长度为[%d]-[%v]消息成功\n", nowTime(), writeLen, msg)
}

8、客户端链接服务器端(客户端)

package main

import (
	"tcp_socket_demo/client"
)

func main() {
	client := client.NewClient("127.0.0.1:8088")
	client.StartClient()
	client.SendMessage("你好啊,服务器,我是A客户端")
}

9、重新编译使客户端与服务器端进行交互

9.1、服务器端启动效果

Golang tcp socket编程案例_第4张图片

9.2、客户端A向服务器端发送消息

Golang tcp socket编程案例_第5张图片

9.3、服务器端接收到客户端的消息

Golang tcp socket编程案例_第6张图片

以上示例完整的服务器端实现代码

package server

import (
	"fmt"
	"net"
	"strings"
	"time"
)

type server struct {
	// 网络协议类型
	networkType string
	// ip地址 + 端口
	ipAddress string
}

// NewServer 通过此函数可以得到一个 server 结构体
func NewServer(ipAddress string) server {
	if ipAddress == "" {
		ipAddress = "127.0.0.1:8088"
	}
	return server{
		// 默认值 -- 方便开发
		networkType: "tcp",
		ipAddress:   ipAddress,
	}
}

// StartServer 启动服务端的方法
func (s server) StartServer() {

	// 1. 表示使用的网络协议:tcp
	// 2. 对本机进行监听 ip为 xxx.xxx.xxx.xxx + 端口为 xxx
	listener, err := net.Listen(s.networkType, s.ipAddress)
	if err != nil {
		panic(fmt.Sprintf("服务器启动失败, err = %v \n", err))
	}

	fmt.Printf("服务器端启动成功, %v , time: %v \n", listener.Addr().String(), nowTime())

	// 服务器不断的接收客户端的信息
	for {
		conn, err := listener.Accept()
		// 客户端的信息
		remoteAddr := conn.RemoteAddr()
		if err != nil {
			fmt.Printf("客户端[%v]: %v 链接服务器端出现异常, err = %v \n", nowTime(), remoteAddr.String(), err)
			// 跳过此客户端链接不在往下执行
			continue
		}
		fmt.Printf("客户端[%v]: %v 链接服务器端成功 \n", nowTime(), remoteAddr.String())
		// 处理客户端的消息..
		// 有多个客户端链接,就有多少个协程
		go process(conn)
	}
}

// 获取当前运行的时间
func nowTime() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

// 用于处理客户端消息的函数
func process(conn net.Conn) {
	// 客户端断开链接后要及时关闭
	defer conn.Close()
	connRemoteAddr := conn.RemoteAddr().String()
	// 循环读取客户端的消息
	for {
		// 创建一个1024长度的 byte 切片用于存储客户端的消息
		bytes := make([]byte, 1024)
		// 等待客户端通过 conn 发送消息
		// 如果客户端一直没有发送,那么此协程就阻塞在这里
		readLen, err := conn.Read(bytes)
		if err != nil {
			errStr := err.Error()
			contains := strings.Contains(errStr, "An existing connection was forcibly closed by the remote host")
			if contains || errStr == "EOF" {
				fmt.Printf("客户端[%v]: %v 断开链接\n", nowTime(), connRemoteAddr)
				return
			} else {
				fmt.Printf("服务器读取客户端[%v]: %v 消息出现异常, err = %v \n", nowTime(), connRemoteAddr, err)
				return
			}
		}
		// 服务器端显示客户端的消息
		// bytes[:readLen] ==> 只打印有效的数据长度
		fmt.Printf("server[%v]-客户端[%v]说: %v\n", nowTime(), connRemoteAddr, string(bytes[:readLen]))
	}
}

以上示例完整的客户端实现代码

package client

import (
	"fmt"
	"net"
	"time"
)

type client struct {
	// 网络协议类型
	networkType string
	// ip地址 + 端口
	ipAddress string
	// 当前的链接
	conn net.Conn
}

// NewClient 通过此函数可以得到一个 client 结构体
func NewClient(ipAddress string) client {
	return client{
		networkType: "tcp",
		ipAddress:   ipAddress,
	}
}

// 获取当前运行的时间
func nowTime() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

// StartClient 客户端启动方法
func (c *client) StartClient() {
	conn, err := net.Dial(c.networkType, c.ipAddress)
	if err != nil {
		fmt.Printf("链接服务器端: %v ,time = %v ,出现异常 err = %v\n", c.ipAddress, nowTime(), err)
		return
	}
	fmt.Printf("[%v]链接服务器端:[%v]成功\n", nowTime(), conn.RemoteAddr().String())
	c.conn = conn
}

// SendMessage 发送消息
func (c *client) SendMessage(msg string) {
	writeLen, err := c.conn.Write([]byte(msg))
	if err != nil {
		fmt.Printf("[%v]客户端发送消息至服务器失败, err = %v\n", nowTime(), err)
		return
	}
	fmt.Printf("[%v]客户发送长度为[%d]-[%v]消息成功\n", nowTime(), writeLen, msg)
}

客户端改造

客户端保持链接,可以不断发送消息,直到自已选择退出客户端

func main() {
	client := client.NewClient("127.0.0.1:8088")
	client.StartClient()
	for {
		fmt.Printf("请输入要发送的内容(回车即可发送,退出请输入exit): ")
		// 标准输入
		reader := bufio.NewReader(os.Stdin)
		// 回车代表输入结束
		readString, err := reader.ReadString('\n')
		if err != nil {
			fmt.Printf("终端输入出现错误,请重新输入, err = %v\n", err)
			continue
		}
		if strings.TrimSpace(readString) == "exit" {
			break
		}
		client.SendMessage(readString)
	}
}

效果如下

两个客户端
Golang tcp socket编程案例_第7张图片
服务端
Golang tcp socket编程案例_第8张图片
两个客户端进行交互
Golang tcp socket编程案例_第9张图片
客户端均下线,服务端的效果
在这里插入图片描述

代码地址

https://github.com/oukele/go_tcp_socket_demo

你可能感兴趣的:(Golang,网络编程,golang,后端)