我试着制作套接字和我的客户沟通。
在对我的API进行一些请求之后,将创建一个套接字。这意味着,客户端只通过请求连接自己,但随后,他加入了聊天,因此创建了一个套接字,并将其链接到好的通道。
我已经使用了套接字,所以我理解它是如何工作的(C,C++,C#,Java),但是我想做什么,用我在网上看到的东西,我认为这是可能的,但是我不知道如何用golang来处理它。
我创建了第一个服务器:
func main() {
r := mux.NewRouter()
r.HandleFunc("/", HomeHandler)
r.HandleFunc("/products", ProductsHandler)
r.HandleFunc("/articles", ArticlesHandler)
http.Handle("/", r)
}但是套接字我还需要一个吗?
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"))
}
}谢谢你帮忙!
发布于 2016-05-24 23:21:03
基于我们的聊天讨论。
包含大量伪码的OVERsimplified示例
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!
发布于 2016-05-24 14:51:02
默认http服务器只接受一个“主机:端口”上的连接
答案取决于您将使用何种协议通过套接字进行通信。
我建议这样做:(大大简化)
当然,您需要添加错误检查和互斥(可能)来保护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
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看起来可能如下:
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
还是你想要取得完全不同的成就?
https://stackoverflow.com/questions/37408252
复制相似问题