Golang学习笔记之简易聊天系统服务器的搭建

栏目: Go · 发布时间: 6年前

内容简介:个人端代码

下面先列举一下程序使用到的函数,省的大家去找,直接拷贝官方api的解释吧。

func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)

DialTCP在网络协议net上连接本地地址laddr和远端地址raddr。

net必须是"tcp"、"tcp4"、"tcp6";如果laddr不是nil,将使用它作为本地地址,否则自动选择一个本地地址。

func ResolveTCPAddr(net, addr string) (*TCPAddr, error)

ResolveTCPAddr将addr作为TCP地址解析并返回。

参数addr格式为"host:port"或"[ipv6-host%zone]:port",解析得到网络名和端口名;net必须是"tcp"、"tcp4"或"tcp6"。

func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error)

ListenTCP在本地TCP地址laddr上声明并返回一个*TCPListener,

net参数必须是"tcp"、"tcp4"、"tcp6",如果laddr的端口字段为0,函数将选择一个当前可用的端口,可以用Listener的Addr方法获得该端口。TCPListener代表一个TCP网络的监听者。使用者应尽量使用Listener接口而不是假设(网络连接为)TCP。

func (l *TCPListener) AcceptTCP() (*TCPConn, error)
//AcceptTCP接收下一个呼叫,并返回一个新的*TCPConn。
//TCPConn代表一个TCP网络连接,实现了Conn接口。
Write(b []byte) (n int, err error)

Write从连接中写入数据

Write方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真

Read(b []byte) (n int, err error)

Read从连接中读取数据

Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真

var Args []string

Args保管了命令行参数,第一个是程序名。

服务端代码

server.go

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net"
)

func main() {
    //端口号
    StartServer("8080")
}

//结构体
type person struct {
    news string
    ip   string
}

//StartServer 启动服务器
func StartServer(s string) {

    // 获取tcp地址
    tcpAddr, err := net.ResolveTCPAddr("tcp4", ":"+s)
    if err != nil {
        log.Printf("resolve tcp addr failed: %v\n", err)
        return
    }
    //连接池,用来保存所有人的数据
    conns := make(map[string]net.Conn)
    //消息信道,为有缓冲信道,当然缓冲内存也可以增大
    messages := make(chan person, 10)

    // 监听
    listener, err := net.ListenTCP("tcp", tcpAddr)
    if err != nil {
        log.Printf("listen tcp port failed: %v\n", err)
        return
    }
    //开启发送消息的协程
    go BroadCastMessage(conns, messages)

    //时刻监测有没有新的消息发送过来
    for {
        /*
            func (l *TCPListener) AcceptTCP() (*TCPConn, error)

            AcceptTCP接收下一个呼叫,并返回一个新的*TCPConn。
            TCPConn代表一个TCP网络连接,实现了Conn接口。
        */
        conn, err := listener.AcceptTCP()
        if err != nil {
            fmt.Println("链接失败")
            continue
        }
        conns[conn.RemoteAddr().String()] = conn
        go HandlerMessage(conn, conns, messages)
    }
}

//HandlerMessage 检查发送来的消息
/*
    //conn:返回的新的*TCPConn
    conns:连接池
    messages:消息通道
*/
func HandlerMessage(conn net.Conn, conns map[string]net.Conn, messages chan person) {
    //切片用来暂存消息
    buf := make([]byte, 1024)
    for {
        //从链接里面读取数据,写给buf
        len, err := conn.Read(buf)
        if err != nil {
            conn.Close()
            delete(conns, conn.RemoteAddr().String())
            break
        }
        //消息写入结构体,并且把发送者的ip一块写入
        p := person{
            news: string(buf[:len]),
            ip:   conn.RemoteAddr().String(),
        }
        //发送给信道
        messages <- p
        fmt.Println(string(buf[:len]))
    }
}

//BroadCastMessage 发送消息
/*
    conns:连接池
    messages:消息通道
*/
func BroadCastMessage(conns map[string]net.Conn, messages chan person) {
    for {
        //读取信道里面的消息
        mess := <-messages
        //发送给所有人
        for k, v := range conns {
            //不发送给自己
            if k != mess.ip {
                var m map[string]interface{}
                //将发送过来消息的news序列化为map
                json.Unmarshal([]byte(mess.news), &m)
                //拼接字符串
                msg := m["time"].(string) + "\n" + m["userID"].(string) + ":" + m["message"].(string)
                //发送
                _, err := v.Write([]byte(msg))
                //如果失败关闭v所属人的协程,继续监测
                if err != nil {
                    delete(conns, k)
                    v.Close()
                    continue
                }
            }
        }
    }
}

个人端代码

net.go

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net"
    "os"
    "time"
)

func main() {
    /*
        var Args []string
        Args保管了命令行参数,第一个是程序名。
    */
    tcpAddr, _ := net.ResolveTCPAddr("tcp", os.Args[1])
    //拨号
    conn, _ := net.DialTCP("tcp", nil, tcpAddr)
    var p = make(map[string]interface{})
    p["userID"] = os.Args[2]
    go HandlerMessage(conn, p)
    ReceivesMessage(conn)
}

//HandlerMessage 向服务器发送数据
func HandlerMessage(conn net.Conn, p map[string]interface{}) {
    for {
        var input string

        // 接收输入消息,放到input变量中
        fmt.Scanln(&input)

        //用户端退出啊
        if input == "/q" || input == "/quit" {
            fmt.Println("Byebye ...")
            conn.Close()
            os.Exit(0)
        }
        //发送的信息
        p["message"] = input
        //时间戳
        p["time"] = time.Now()
        // 只处理有内容的消息
        if len(input) > 0 {
            //序列化为json
            msg, err := json.Marshal(p)
            if err != nil {
                //如果不成功,返回错误
                fmt.Println("错误", err)
            } else {
                //发送数据
                _, err := conn.Write([]byte(string(msg)))
                //没有发送成功
                if err != nil {
                    conn.Close()
                    break
                }
            }
        }
    }
}

//ReceivesMessage 接收服务器消息
func ReceivesMessage(conn net.Conn) {
    // 接收来自服务器端的广播消息
    buf := make([]byte, 1024)
    for {
        length, err := conn.Read(buf)
        if err != nil {
            log.Printf("recv server msg failed: %v\n", err)
            conn.Close()
            os.Exit(0)
            break
        }

        fmt.Println(string(buf[0:length]))
    }
}

测试步骤,只用本地测试一下,懒的上传云服务器了

一、启动服务器

Golang学习笔记之简易聊天系统服务器的搭建

二、启动个人端,当然这里我们启动两个,两个以上才能看出来效果不是吗

Golang学习笔记之简易聊天系统服务器的搭建
Golang学习笔记之简易聊天系统服务器的搭建

三、发送消息

Golang学习笔记之简易聊天系统服务器的搭建

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Lean Analytics

Lean Analytics

Alistair Croll、Benjamin Yoskovitz / O'Reilly Media / 2013-3-18 / USD 29.99

If you're involved with a startup, analytics help you find your way to the right product and market before the money runs out. But with a flood of information available, where do you start? This book ......一起来看看 《Lean Analytics》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具