douyan2680 2016-05-24 08:31
浏览 78
已采纳

Golang-同时使用API​​服务器和Socket

I try to make sockets to communicate with my clients.

A socket would be created after some requests to my API. It means, a client connects itself (only by request), but then, he joins a chat, so a socket is created and linked to the good channel.

I already used sockets so I understand how it works (C, C++, C#, Java), but what I want to make, with what I saw on the web, I think it's possible, but I don't understand how to handle it with golang.

I create a first server:

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    r.HandleFunc("/products", ProductsHandler)
    r.HandleFunc("/articles", ArticlesHandler)
    http.Handle("/", r)
}

But for socket, I need another one?

package main

import "net"
import "fmt"
import "bufio"
import "strings" // only needed below for sample processing

func main() {

    fmt.Println("Launching server...")

    // listen on all interfaces
    ln, _ := net.Listen("tcp", ":8081")

    // accept connection on port
    conn, _ := ln.Accept()   

    // run loop forever (or until ctrl-c)
    for {     
        // will listen for message to process ending in newline (
)
        message, _ := bufio.NewReader(conn).ReadString('
')
        // output message received     
        fmt.Print("Message Received:", string(message))
        // sample process for string received     
        newmessage := strings.ToUpper(message)
        // send new string back to client     
        conn.Write([]byte(newmessage + "
"))   
    } 
}

Thank for help !

  • 写回答

2条回答 默认 最新

  • duan2477 2016-05-24 23:21
    关注

    Based on our chat discussion.

    OVERsimplified example with lots of pseudocode

    import (
        "net"
        "encoding/json"
        "errors"
    )
    
    type User struct {
        name string
    }
    
    type Message {
        Action string
        Params map[string]string
    }
    
    type Server struct {
        connected_users map[*User]net.Conn
        users_connected_with_each_other map[*User][]*User
        good_users map[string]*User
    }
    
    func (srv *Server) ListenAndServe(addr string) error {
        ln, err := net.Listen("tcp", addr)
        if err != nil {
            return err
        }
        return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
    }
    
    func (srv *Server) Serve(l net.Listener) error {
        defer l.Close()
    
        for {
            rw, e := l.Accept()
            if e != nil {
                return e
            }
    
            // you want to create server_conn here with buffers, channels and stuff
            // to use async thread safe read/write from it
            go srv.serve_conn(rw)
        }
    }
    
    func (srv *Server) serve_conn(rw net.Conn) error {  
        dec := json.NewDecoder(rw)
    
        var message Message
    
        //read 1st message he sent, should be token to connect
        dec.Decode(&message)
    
        token := get_token(Message)
    
        user, ok := srv.good_users[token]
    
        if !ok {
            return errors.New("BAD USER!")
        }
        // store connected user
        srv.connected_users[user] = rw
    
        for {
            // async reader will be nice
            dec.Decode(&message)
    
            switch message.Action {
                case "Message":
                    // find users to send message to
                    if chats_with, err := users_connected_with_each_other[user]; err == nil {                   
                        for user_to_send_message_to := range chats_with {
                            // find connections to send message to
                            if conn, err := srv.connected_users[user_to_send_message_to]; err == nil {
                                // send json encoded message
                                err := json.NewEncoder(conn).Encode(message)
                                //if write failed store message for later
                            }
                        }
                    }
    
                //other cases
    
                default:
                    // log?
            }       
        }
    }
    
    func main() {
        known_users_with_tokens := make(map[string]*User)
    
    
        srv := &Server{
            connected_users: make(map[*User]net.Conn),
            users_connected_with_each_other: make(map[*User][]*User),
            good_users: known_users_with_tokens, // map is reference type, so treat it like pointer
        }
        // start our server
        go srv.ListenAndServe(":54321")
    
    
        ConnRequestHandler := function(w http.ResponseWriter, r *http.Request) {
            user := create_user_based_on_request(r)
            token := create_token(user)
    
            // now user will be able to connect to server with token
            known_users_with_tokens[token] = user
        }
    
        ConnectUsersHandler := function(user1,user2) {
            // you should guard your srv.* members to avoid concurrent read/writes to map
            srv.users_connected_with_each_other[user1] = append(srv.users_connected_with_each_other[user1], user2)
            srv.users_connected_with_each_other[user2] = append(srv.users_connected_with_each_other[user2], user1)
        }
    
        //initialize your API http.Server
        r := mux.NewRouter()
        r.HandleFunc("/", HomeHandler)
        r.HandleFunc("/products", ProductsHandler)
        r.HandleFunc("/articles", ArticlesHandler)
        r.HandleFunc("/connection_request", ConnRequestHandler) // added
        http.Handle("/", r)
    }
    

    Call ConnectUsersHandler(user1, user2) to allow them communicate with each other.

    known_users_with_tokens[token] = user to allow user to connect to server

    You need to implement async reader/writer for connections to your server. Usefull structs to keep good Users. Guard Server struct members and provide thread safe access to update it.

    UDP

    Looks like json.NewEncoder(connection).Encode(&message) and json.NewDecoder(connection).Decode(&message) is async and thread safe. So you can write simultaneously from different goroutines. No need to manual sync, YAY!

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 关于大棚监测的pcb板设计
  • ¥15 stm32开发clion时遇到的编译问题
  • ¥15 lna设计 源简并电感型共源放大器
  • ¥15 如何用Labview在myRIO上做LCD显示?(语言-开发语言)
  • ¥15 Vue3地图和异步函数使用
  • ¥15 C++ yoloV5改写遇到的问题
  • ¥20 win11修改中文用户名路径
  • ¥15 win2012磁盘空间不足,c盘正常,d盘无法写入