2 回答

TA貢獻(xiàn)1825條經(jīng)驗(yàn) 獲得超4個(gè)贊
默認(rèn) http 服務(wù)器僅接受一個(gè)“主機(jī):端口”上的連接
答案取決于您將使用什么協(xié)議通過(guò)套接字進(jìn)行通信。
我建議這樣做:(非常簡(jiǎn)化)
讓 http.Server 單獨(dú)為您的 API 提供服務(wù)(它實(shí)現(xiàn)了 HTTP 1.*/2 協(xié)議,因此您無(wú)需擔(dān)心)
實(shí)現(xiàn)你自己的“MultiSocketServer”,這樣做:
2.1 實(shí)現(xiàn) GracefulListener(必須實(shí)現(xiàn) net.Listener)(當(dāng)你不再需要它們時(shí),你需要關(guān)閉你的套接字,對(duì)吧?)
2.2 實(shí)現(xiàn) MultiSocketServer.Serve(l GracefulListener) (hello http.Server.Serve() ) 來(lái)服務(wù)單個(gè)連接(你通過(guò)套接字與客戶(hù)端通信的協(xié)議在這里。像 net.textproto 這樣的東西很容易實(shí)現(xiàn),因?yàn)槟?GracefulListener.Accept () 將返回 net.Conn)
2.3 添加方法MultiSocketServer.ListenAndServe(addr), MultiSocketServer.StopServe(l GracefulListener) 到你的MultiSocketServer
type MultiSocketServer struct {
listeners GracefulListener[] //or map?
// lots of other stuff
}
// looks familiar? (http.Server.ListenAndServe)
func (s *MultiSocketServer) ListenAndServe(addr string) {
ln, err := net.Listen("tcp", addr)
graceful_listner = &GracefulListener(ln)
s.listeners = append(s.listeners, graceful_listner)
go s.Serve(graceful_listner)
return graceful_listner
}
func (s *MultiSocketServer) StopServe(graceful_listner GracefulListener) {
graceful_listner.Stop()
//pseudocode
remove_listener_from_slice(s.listeners, graceful_listner)
}
當(dāng)然,您需要添加錯(cuò)誤檢查和互斥鎖(可能)來(lái)保護(hù) MultiSocketServer.listeners 以使其線(xiàn)程安全。
在您的 main() 中啟動(dòng)您的 API http.Server,并初始化您的 MultiSocketServer?,F(xiàn)在,從 http.Server 的 http.Handler/http.HandlerFunc 中,您應(yīng)該能夠調(diào)用 MultiSocketServer.ListenAndServe(addr) 來(lái)監(jiān)聽(tīng)和服務(wù)您的套接字連接。
根據(jù)問(wèn)題更新
但是,我不確定是否理解“在您的 main() 中”部分。如果我理解得很好,你說(shuō)我有我的API,啟動(dòng)它后,我初始化MultiSocketServer。但是哪里?在我的 API 啟動(dòng)之后?或者您的意思是我將您的代碼邏輯用作 API 會(huì)更好?每個(gè)請(qǐng)求都通過(guò)一個(gè)套接字
順便說(shuō)一句:更新 MultiSocketServer.ListenAndServe 以啟動(dòng) Listen 并返回 graceful_listner
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
}
實(shí)際上,您可以調(diào)用multi_socket_server.ListenAndServe(addr_to_listen)和multi_socket_server.StopServe(listener)從您的 API 服務(wù)器中的任何處理程序。
每次調(diào)用multi_socket_server.ListenAndServe(addr_to_listen)它都會(huì)創(chuàng)建新的偵聽(tīng)器并為其提供服務(wù),您必須控制它(不要在同一個(gè)地址上偵聽(tīng)一次以上(我認(rèn)為它無(wú)論如何都會(huì)出錯(cuò)))
您的 MultiSocketServer.Serve 可能如下所示:
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實(shí)現(xiàn) github
還是您想實(shí)現(xiàn)完全不同的目標(biāo)?=)

TA貢獻(xiàn)2012條經(jīng)驗(yàn) 獲得超12個(gè)贊
基于我們的聊天討論。
OVER帶有大量偽代碼的簡(jiǎn)化示例
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)
}
打電話(huà)ConnectUsersHandler(user1, user2)讓他們互相交流。
known_users_with_tokens[token] = user 允許用戶(hù)連接到服務(wù)器
您需要實(shí)現(xiàn)異步讀取器/寫(xiě)入器以連接到您的服務(wù)器。保持良好用戶(hù)的有用結(jié)構(gòu)。保護(hù)服務(wù)器結(jié)構(gòu)成員并提供線(xiàn)程安全訪(fǎng)問(wèn)來(lái)更新它。
UDP
看起來(lái)像json.NewEncoder(connection).Encode(&message)并且json.NewDecoder(connection).Decode(&message)是異步和線(xiàn)程安全的。因此,您可以從不同的 goroutine 同時(shí)編寫(xiě)代碼。無(wú)需手動(dòng)同步,耶!
- 2 回答
- 0 關(guān)注
- 525 瀏覽
添加回答
舉報(bào)