编程语言
首页 > 编程语言> > 即时通讯源码(基于websocket即时通讯源码uniapp)+视频搭建教程

即时通讯源码(基于websocket即时通讯源码uniapp)+视频搭建教程

作者:互联网

  即时通讯系统源码服务器端构架目录:

       仓库源码:im.jstxym.top

  1、构建基本服务器
  2、用户在线功能
  3、用户消息广播机制
  4、用户业务层封装
  5、在线用户查询
  6、修改用户名
  7、超时推送功能
  8、私聊功能
  即时通讯系统源码客户端构架目录:
  1、客户端类型定义和链接
  2、解析命令行
  3、菜单显示
  4、更新用户名
  5、公共聊天模式
  6、私聊模式
  即时通讯系统 - 服务器
  项目架构图:

 


  1、构建基本服务器
  其中包括以下内容:
  定义服务器结构,包括IP和端口字段
  NewServer(ip string, port int)创建服务器对象的方法
  (s *Server) Start()启动服务器服务的方法
  (s *Server) Handler(conn net.Conn)处理连接服务

package main

import (
    "fmt"
    "net"
)

type Server struct {
    Ip   string
    Port int
}

//Create a server interface
func NewServer(ip string, port int) *Server {
    server := &Server{
        Ip:   ip,
        Port: port,
    }
    return server
}

func (s *Server) Handler(conn net.Conn) {
    //Currently connected services
    fmt. Println ("connection established successfully!")
}

//Start the interface of the server
func (s *Server) Start() {
    // socket listen
    listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Ip, s.Port))
    if err != nil {
        fmt.Println("net.Listen err: ", err)
        return
    }
    // close listen socket
    defer listener.Close()

    for {
        // accpet
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener accept err: ", err)
            continue
        }
        // do handler
        go s.Handler(conn)
    }

}

        启动我们写的服务器:

package main

func main() {
    server := NewServer("127.0.0.1", 8888)
    server.Start()
}

  以下命令在Linux或MacOS下运行,与windows略有不同
  同时编译的两个文件:go build -o server main.go server.go
  然后运行编译后的文件:./server
  收听我们使用命令构建的服务:nc 127.0.0.1 8888
  2、用户在线功能
  NewUser(conn net.Conn) *User创建用户对象
  (u *User) ListenMessage()收听用户对应的频道消息

  添加了在线地图和消息属性
  在处理客户端的处理程序中创建和添加用户
  新的广播消息方法
  收听广播消息的新频道方法
  使用 goroutine 分别监听消息

type Server struct {
    Ip   string
    Port int

    //List of online users
    OnlineMap map[string]*User
    mapLock   sync.RWMutex

    //Message broadcast channel
    Message chan string
}

//Create a server interface
func NewServer(ip string, port int) *Server {
    server := &Server{
        Ip:        ip,
        Port:      port,
        OnlineMap: make(map[string]*User),
        Message:   make(chan string),
    }
    return server
}

//Monitor the goroutine of the channel of the message broadcast message. Once there is a message, it will be sent to all online users
func (s *Server) ListenMessager() {
    for {
        msg := <-s.Message
        //Send msg to all online users
        s.mapLock.Lock()
        for _, cli := range s.OnlineMap {
            cli.C <- msg
        }
        s.mapLock.Unlock()
    }
}

//Method of broadcasting message
func (s *Server) BroadCast(user *User, msg string) {
    sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg

    s.Message <- sendMsg
}

func (s *Server) Handler(conn net.Conn) {
    //Currently connected services
    // fmt. Println ("connection established successfully!")

    user := NewUser(conn)

    //The user goes online and adds the user to the onlinemap
    s.mapLock.Lock()
    s.OnlineMap[user.Name] = user
    s.mapLock.Unlock()

    //Broadcast the online message of the current user
    s. Broadcast (user, "online")

    //Current handler blocked
    select {}
}

//Start the interface of the server
func (s *Server) Start() {
    // socket listen
    listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Ip, s.Port))
    if err != nil {
        fmt.Println("net.Listen err: ", err)
        return
    }
    // close listen socket
    defer listener.Close()

    //Start goroutine for monitoring message
    go s.ListenMessager()

    for {
        // accpet
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener accept err: ", err)
            continue
        }
        // do handler
        go s.Handler(conn)
    }

  学到的编程思想:
  结构中的channels基本上都需要开一个循环来监听它们的变化(尽量获取值并发给其他channels)
  3、用户消息广播机制
  服务器。go:改进句柄处理业务方法,为当前客户端启动一个读例程

  4、用户业务层封装
  为用户类型添加服务器关联
  添加线上、线下和外卖方式

type User struct {
    Name string
    Addr string
    C    chan string
    conn net.Conn

    server *Server
}

//Create a user API
func NewUser(conn net.Conn, server *Server) *User {
    userAddr := conn.RemoteAddr().String()

    user := &User{
        Name:   userAddr,
        Addr:   userAddr,
        C:      make(chan string),
        conn:   conn,
        server: server,
    }

    //Start goroutine to listen to the current user channel message
    go user.ListenMessage()

    return user
}

//Online business of users
func (u *User) Online() {
    //When the user goes online, add the user to the onlinemap
    u.server.mapLock.Lock()
    u.server.OnlineMap[u.Name] = u
    u.server.mapLock.Unlock()

    //Broadcast the online message of the current user
    u.server. Broadcast (U, "online")
}

//User's offline business
func (u *User) Offline() {
    //When the user goes offline, delete the user from the onlinemap
    u.server.mapLock.Lock()
    delete(u.server.OnlineMap, u.Name)
    u.server.mapLock.Unlock()

    //Broadcast the offline message of the current user
    u.server. Broadcast (U, "offline")
}

//Service for users to process messages
func (u *User) DoMessage(msg string) {
    u.server.BroadCast(u, msg)
}

//The method of listening to the current user channel. Once there is a message, it will be sent directly to the client
func (u *User) ListenMessage() {
    for {
        msg := <-u.C
        u.conn.Write([]byte(msg + "\n"))
    }
}

  server.go:
  将之前的代码替换为用户封装的业务

func (s *Server) Handler(conn net.Conn) {
    //Currently connected services
    // fmt. Println ("connection established successfully!")

    user := NewUser(conn, s)

  //User online
    user.Online()

    //Accept messages sent by clients
    go func() {
        buf := make([]byte, 4096)
        for {
            n, err := conn.Read(buf)
            if n == 0 {
        //User offline
                user.Offline()
                return
            }
            if err != nil && err != io.EOF {
                fmt.Println("Conn Read err:", err)
                return
            }

            //Extract user's message (remove '\ n')
            msg := string(buf[:n-1])

            //Broadcast the received message
            user.DoMessage(msg)
        }
    }()

    //Current handler blocked
    select {}
}

  5、在线用户查询
  如果用户输入的消息是who则查询当前在线用户列表。
  用户.go:
  为 sendmsg 提供 API 以向对象客户端发送消息

  func (u *User) SendMsg(msg string) {
  u.conn.Write([]byte(msg))
  }

        在domessage()方法中,增加了“who”指令的处理,返回在线用户信息

func (u *User) DoMessage(msg string) {
    if msg == "who" {
        //Query the current online users
        u.server.mapLock.Lock()
        for _, user := range u.server.OnlineMap {
            onlineMsg := "[" + user.Addr + "]" + user. Name + ":" + "online... \ n"
            u.SendMsg(onlineMsg)
        }
        u.server.mapLock.Unlock()
    } else {
        u.server.BroadCast(u, msg)
    }
}

  6、修改用户名
  如果用户输入的消息是Rename Zhang SanChange your name to Zhang San。
  用户.go:
  在指令中添加“doame”

func (u *User) DoMessage(msg string) {
    if msg == "who" {
        //Query the current online users
        u.server.mapLock.Lock()
        for _, user := range u.server.OnlineMap {
            onlineMsg := "[" + user.Addr + "]" + user. Name + ":" + "online... \ n"
            u.SendMsg(onlineMsg)
        }
        u.server.mapLock.Unlock()
    } else if len(msg) > 7 && msg[:7] == "rename|" {
        //Message format: Rename | Zhang San
        newName := strings.Split(msg, "|")[1]
        //Determine whether name exists
        _, ok := u.server.OnlineMap[newName]
        if ok {
            u. Sendmsg ("current user name is in use \ n")
        } else {
            u.server.mapLock.Lock()
            delete(u.server.OnlineMap, newName)
            u.server.OnlineMap[newName] = u
            u.server.mapLock.Unlock()

            u.Name = newName
            u. Sendmsg ("you have updated your user name:" + u.name + "\ n")
        }
    } else {
        u.server.BroadCast(u, msg)
    }
}

  7、超时推送功能
  来自用户的任何消息都表明该用户处于活动状态。如果用户长时间不发送消息,则视为超时,然后强制关闭用户连接。
  server.go:

func (s *Server) Handler(conn net.Conn) {
    //Currently connected services
    // fmt. Println ("connection established successfully!")

    user := NewUser(conn, s)

    user.Online()

    //Monitor whether the user is active in the channel
    isLive := make(chan bool)

    //Accept messages sent by clients
    go func() {
        buf := make([]byte, 4096)
        for {
            n, err := conn.Read(buf)
            if n == 0 {
                user.Offline()
                return
            }
            if err != nil && err != io.EOF {
                fmt.Println("Conn Read err:", err)
                return
            }

            //Extract user's message (remove '\ n')
            msg := string(buf[:n-1])

            //The user processes messages for MSG
            user.DoMessage(msg)

            //Any message of the user, indicating that the current user is active
            isLive <- true
        }
    }()

    //Current handler blocked
    for {
        select {
        case <-isLive:
            //The current user is active and the timer should be reset
            //Do nothing. In order to activate select, update the timer below
        case <-time. After (time. Second * 10): // trigger the timer after 10s
            //Has timed out
            //Force the current user to close
            user. Sendmsg ("you got kicked.")

            //Destruction of resources
            close(user.C)

            //Close connection
            conn.Close()

            //Exit current handler
            // runtime.Goexit()
            return
        }
    }
}

  8、私聊功能
  留言格式:Hello, I'm
  在domessage()方法中,添加对“三问好”指令的处理:

func (this *User) DoMessage(msg string) {
    if msg == "who" {
        //Query the current online users

        this.server.mapLock.Lock()
        for _, user := range this.server.OnlineMap {
            onlineMsg := "[" + user.Addr + "]" + user. Name + ":" + "online... \ n"
            this.SendMsg(onlineMsg)
        }
        this.server.mapLock.Unlock()

    } else if len(msg) > 7 && msg[:7] == "rename|" {
        //Message format: Rename | Zhang San
        newName := strings.Split(msg, "|")[1]

        //Determine whether name exists
        _, ok := this.server.OnlineMap[newName]
        if ok {
            this. Sendmsg ("current user name is in use \ n")
        } else {
            this.server.mapLock.Lock()
            delete(this.server.OnlineMap, this.Name)
            this.server.OnlineMap[newName] = this
            this.server.mapLock.Unlock()

            this.Name = newName
            this. Sendmsg ("you have updated your user name:" + this. Name + "\ n")
        }

    } else {
        this.server.BroadCast(this, msg)
    }
}

  即时通讯系统——客户端
  客户端类型定义和链接

type Client struct {
    ServerIp   string
    ServerPort int
    Name       string
    conn       net.Conn
}

func NewClient(serverIp string, serverPort int) *Client {
    //Create client object
    client := &Client{
        ServerIp:   serverIp,
        ServerPort: serverPort,
    }
    //Connect to server
    conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", serverIp, serverPort))
    if err != nil {
        fmt.Println("net.Dial error:", err)
        return nil
    }
    client.conn = conn
    //Return object
    return client
}

func main() {
    client := NewClient("127.0.0.1", 8888)
    if client == nil {
        fmt. Println ("> > > failed to connect to the server")
        return
    }
    fmt. Println ("> > > successfully connected to the server")

    //Start client service
    select {}
}

  编译说明:go build -o client client.go
  运行编译后的文件:./client
  解析命令行
  在init函数中初始化命令行参数并解析:

var serverIp string
var serverPort int

func init() {
    flag. Stringvar (& ServerIP, "IP", "127.0.0.1", "set server IP address (default is 127.0.0.1)")
    flag. Intvar (& serverport, "port", 8888, "set server port (default is 8888)")

    //Command line parsing
    flag.Parse()
}

  然后,在运行客户端的时候,可以通过命令行传递参数来运行:
  ./client -ip 127.0.0.1 -port 8888
  菜单显示
  向客户端添加标志属性:

type Client struct {
    ServerIp   string
    ServerPort int
    Name       string
    conn       net.Conn
    Flag int // current client mode
}
添加menu()方法获取用户输入的模式:

//Menu
func (client *Client) menu() bool {
    var flag int

    fmt. Println ("1. Public chat mode")
    fmt. Println ("2. Private chat mode")
    fmt. Println ("3. Update user name")
    fmt. Println ("0. Exit")

    fmt.Scanln(&flag)

    if flag >= 0 && flag <= 3 {
        client.flag = flag
        return true
    } else {
        fmt. Println ("> > > > please enter the number within the legal range < < <)
        return false
    }
}
添加一个run()主业务循环:

func (client *Client) Run() {
    for client.flag != 0 {
        for !client.menu() {
        }

        //Handle different businesses according to different modes
        switch client.flag {
        case 1:
            //Public chat mode
            fmt. Println ("public chat mode")
        case 2:
            //Private chat mode
            fmt. Println ("private chat mode")
        case 3:
            //Update user name
            fmt. Println ("update user name")
        }
    }
    fmt. Println ("exit!")
}

  更新用户名
  新的 updatename() 用户名​​:

func (client *Client) UpdateName() bool {
    fmt. Println ("> > > > please enter user name:")
    fmt.Scanln(&client.Name)

    sendMsg := "rename|" + client. Name + "\ n" // encapsulation protocol
    _, err := client.conn.Write([]byte(sendMsg))
    if err != nil {
        fmt.Println("conn.Write err: ", err)
        return false
    }

    return true
}
添加服务器回执消息方法 dealresponse()

//Process the messages responded by the server and display them directly to the standard output
func (client *Client) DealResponse() {
    //Once client Conn has data, which is directly copied to stdout standard output to permanently block listening
    io.Copy(os.Stdout, client.conn)
}
在 main 中打开一个 goroutine 来托管 dealresponse() 进程:

func main() {
    client := NewClient(serverIp, serverPort)
    if client == nil {
        fmt. Println ("> > > failed to connect to the server")
        return
    }
    fmt. Println ("> > > successfully connected to the server")

    //Open a goroutine separately to process the receipt message of the server
    go client.DealResponse()

    //Start client service
    client.Run()
}

  公共聊天模式
  添加publicchat()公共聊天模式:

func (client *Client) PublicChat() {
    //Prompt the user for a message
    var chatMsg string

    fmt. Println ("> > > > please enter the chat content and exit.")
    fmt.Scanln(&chatMsg)

    for chatMsg != "exit" {
        //Send to server
        //The message is not empty. Send it now
        if len(chatMsg) != 0 {
            sendMsg := chatMsg + "\n"
            _, err := client.conn.Write([]byte(sendMsg))
            if err != nil {
                fmt.Println("conn Write err: ", err)
                break
            }
        }
        chatMsg = ""
        fmt. Println ("> > > > please enter the chat content and exit.")
        fmt.Scanln(&chatMsg)
    }
}

  私聊模式
  查询当前有哪些用户在线:

func (client *Client) SelectUsers() {
    sendMsg := "who\n"
    _, err := client.conn.Write([]byte(sendMsg))
    if err != nil {
        fmt.Println("conn Write err: ", err)
        return
    }
}
新的私聊业务:

func (client *Client) PrivateChat() {
    var remoteName string
    var chatMsg string

    client.SelectUsers()
    fmt. Println ("> > > > please enter the [user name] of the chat object and exit:")
    fmt.Scanln(&remoteName)

    for remoteName != "exit" {
        fmt. Println ("> > > > please enter the message content, exit:")
        fmt.Scanln(&chatMsg)

        for chatMsg != "exit" {
            //Send if the message is not empty
            if len(chatMsg) != 0 {
                sendMsg := "to|" + remoteName + "|" + chatMsg + "\n\n"
                _, err := client.conn.Write([]byte(sendMsg))
                if err != nil {
                    fmt.Println("conn Write err: ", err)
                    break
                }
            }
            chatMsg = ""
            fmt. Println ("> > > > please enter the message content, exit:")
            fmt.Scanln(&chatMsg)
        }

        client.SelectUsers()
        fmt. Println ("> > > > please enter the [user name] of the chat object and exit:")
        fmt.Scanln(&remoteName)

    }

}

 

标签:uniapp,client,err,fmt,即时通讯,server,源码,user,conn
来源: https://www.cnblogs.com/zerobeauty/p/16619214.html