首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >同时使用Golang服务器和套接字

同时使用Golang服务器和套接字
EN

Stack Overflow用户
提问于 2016-05-24 08:31:50
回答 2查看 6.1K关注 0票数 3

我试着制作套接字和我的客户沟通。

在对我的API进行一些请求之后,将创建一个套接字。这意味着,客户端只通过请求连接自己,但随后,他加入了聊天,因此创建了一个套接字,并将其链接到好的通道。

我已经使用了套接字,所以我理解它是如何工作的(C,C++,C#,Java),但是我想做什么,用我在网上看到的东西,我认为这是可能的,但是我不知道如何用golang来处理它。

我创建了第一个服务器:

代码语言:javascript
复制
func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    r.HandleFunc("/products", ProductsHandler)
    r.HandleFunc("/articles", ArticlesHandler)
    http.Handle("/", r)
}

但是套接字我还需要一个吗?

代码语言:javascript
复制
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 (\n)
        message, _ := bufio.NewReader(conn).ReadString('\n')
        // 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 + "\n"))   
    } 
}

谢谢你帮忙!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-05-24 23:21:03

基于我们的聊天讨论。

包含大量伪码的OVERsimplified示例

代码语言:javascript
复制
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)
}

打电话给ConnectUsersHandler(user1, user2),让他们彼此沟通。

允许用户连接到服务器的known_users_with_tokens[token] = user

您需要为连接到服务器的连接实现异步读取器/写入器。有用的结构,以保持良好的用户。保护服务器结构成员,并提供线程安全访问来更新它。

UDP

看起来json.NewEncoder(connection).Encode(&message)json.NewDecoder(connection).Decode(&message)是异步和线程安全的。这样你就可以同时从不同的峡谷写东西了。不用手动同步,YAY!

票数 2
EN

Stack Overflow用户

发布于 2016-05-24 14:51:02

默认http服务器只接受一个“主机:端口”上的连接

答案取决于您将使用何种协议通过套接字进行通信。

我建议这样做:(大大简化)

  1. 让http.Server单独为您的API服务(它实现了HTTP1.*/2协议,因此您不需要担心它)
  2. 实现您自己的"MultiSocketServer",这样做: 2.1实现GracefulListener (必须实现net.Listener) (当不再需要套接字时,您需要关闭它们,对吗?) 2.2实现MultiSocketServer.Serve(l GracefulListener) (hello http.Server.Serve() )以服务单独的连接(您通过套接字与客户端通信的协议在这里)。像net.textproto这样的东西很容易实现,因为您的GracefulListener.Accept()将返回net.Conn) 2.3将方法MultiSocketServer.ListenAndServe(addr)、MultiSocketServer.StopServe(l GracefulListener)添加到MultiSocketServer中 键入MultiSocketServer struct { GracefulListener[] //或map? //许多其他东西}//看起来很熟悉吗?(http.Server.ListenAndServe) func (s *MultiSocketServer) ListenAndServe(addr字符串){ ln,err := net.Listen("tcp",addr) graceful_listner = &GracefulListener(ln) s.listeners = append(s.listeners,graceful_listner) go s.Serve( graceful_listner )返回graceful_listner} func (s *MultiSocketServer) StopServe(graceful_listner GracefulListener) { graceful_listner.Stop() //伪代码remove_listener_from_slice(s.listeners )graceful_listner) }

当然,您需要添加错误检查和互斥(可能)来保护MultiSocketServer.listeners,使其线程安全。

在main()中启动API http.Server,并初始化MultiSocketServer。现在,从http.Server的http.Handler/http.HandlerFunc中,您应该能够调用MultiSocketServer.ListenAndServe(addr)来侦听和服务套接字连接。

基于问题的更新

然而,我不能肯定地理解“在你的主()”这个部分。如果我理解它很好,您会说我有我的API,并且在启动它之后,我初始化了MultiSocketServer。但是在哪里呢?在我的API开始后?或者您的意思是,我使用您代码的逻辑作为API会更好吗?每一个请求都插在插座上

BTW:更新后的MultiSocketServer.ListenAndServe启动、侦听并返回graceful_listner

代码语言:javascript
复制
func main() {
    //init MultiSocketServer
    multi_socket_server = &MultiSocketServer{} //nil for GracefulListener[] is fine for now, complex initialization will be added later
    // no listners yet, serves nothing

    // create new Handeler for your "socket requests"
    SocketRequestHandler := function(w http.ResponseWriter, r *http.Request) {
       // identify client, assign him an address to connect
       addr_to_listen := parse_request(r) //pseudocode

       listener := multi_socket_server.ListenAndServe(addr_to_listen)
       // TODO: handle errors
       // now your multi_socket_server listen to addr_to_listen and serves it with multi_socket_server.Serve method in its own goroutine
       // as i said MultiSocketServer.Serve method must implement your protocol (plaintext Reader/Writer on listener for now?)

       save_listener_in_context_or_whatever_you_like_to_track_it(listener) //pseudo
   }  

    SocketDisconnectHandler := function(w http.ResponseWriter, r *http.Request) {
      // identify client 
      some_client := parse_request(r) //pseudocode
      // get listener based on info
      listener := get_listener_from_context_or_whatever(some_client) //pseudo

      multi_socket_server.StopServe(listener)
      // TODO: handle errors
    }

    //initialize your API http.Server
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    r.HandleFunc("/products", ProductsHandler)
    r.HandleFunc("/articles", ArticlesHandler)
    r.HandleFunc("/socket_request", SocketRequestHandler) // added
    r.HandleFunc("/socket_disconnect", SocketDisconnectHandler) //added
    http.Handle("/", r)

    // it creates new http.Server with DefaultServeMux as Handler (which is configured with your http.Handle("/", r) call)
   http.ListenAndServe(":8080") // start serving API via HTTP proto
}

实际上,您可以从API服务器中的任何处理程序调用multi_socket_server.ListenAndServe(addr_to_listen)multi_socket_server.StopServe(listener)

每次您调用multi_socket_server.ListenAndServe(addr_to_listen)时,它都会创建新的侦听器并在其上服务,您必须控制它(不要在相同的地址上多听一次(我认为它无论如何都会出错)。

您的MultiSocketServer.Serve看起来可能如下:

代码语言:javascript
复制
func (s *MultiSocketServer) Serve(l net.Listener) {
    defer l.Close()

    for {     
        // will listen for message to process ending in newline (\n)
        message, _ := bufio.NewReader(conn).ReadString('\n')
        // 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 + "\n"))   
    } 
}

可能的GracefulListener 实现 github

还是你想要取得完全不同的成就?

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37408252

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档