Server 端
go run main.go
package main
import (
"encoding/json"
"log"
"net"
"sync"
)
type User struct {
Uid string
Name string
conn *ConnInfo
}
type ConnInfo struct {
conn net.Conn
user *User
}
type SessionManager struct {
sync.RWMutex
users map[string]*User
}
func NewSessionManager() *SessionManager {
return &SessionManager{
users: make(map[string]*User),
}
}
func (s *SessionManager) User(uid string) (*User, bool) {
s.RLock()
defer s.RUnlock()
u, ok := s.users[uid]
return u, ok
}
func (s *SessionManager) AddUser(u *User) {
s.Lock()
s.users[u.Uid] = u
s.Unlock()
}
func (s *SessionManager) RemoveUser(uid string) {
s.Lock()
delete(s.users, uid)
s.Unlock()
}
var manager = NewSessionManager()
func Start() {
listener, err := net.Listen("tcp", ":8099")
if err != nil {
panic(err)
}
for {
conn, err := listener.Accept()
if err != nil {
break
}
log.Printf("remote conn:%s", conn.RemoteAddr())
go handleConn(conn)
}
}
type LoginProto struct {
Uid string `json:"uid"`
Name string `json:"name"`
}
type MessageDataProto struct {
To string `json:"to"`
Message string `json:"message"`
}
type CommandProto struct {
Command string `json:"command"`
Content string `json:"content"`
}
func handleConn(conn net.Conn) {
connInfo := &ConnInfo{
conn: conn,
}
buf := make([]byte, 1024)
for {
n, err := conn.Read(buf)
if err != nil {
log.Printf("conn %s read err:%s", conn.RemoteAddr(), err.Error())
if connInfo.user != nil {
manager.RemoveUser(connInfo.user.Uid)
log.Printf("uid %s is offline", connInfo.user.Uid)
}
break
}
var cmd CommandProto
if err := json.Unmarshal(buf[:n], &cmd); err != nil {
log.Printf("failed to unmarshal buf data:%s", buf[:n])
continue
}
switch cmd.Command {
case "Login":
var login LoginProto
if err := json.Unmarshal([]byte(cmd.Content), &login); err != nil {
log.Printf("failed to unmarshal content data:%s", cmd.Content)
continue
}
u := &User{
Uid: login.Uid,
Name: login.Name,
conn: connInfo,
}
connInfo.user = u
manager.AddUser(u)
log.Printf("uid %s,name:%s login successfully", u.Uid, u.Name)
case "Send":
if connInfo.user == nil {
log.Println("user doesn't login")
continue
}
var msg MessageDataProto
if err := json.Unmarshal([]byte(cmd.Content), &msg); err != nil {
log.Printf("failed to unmarshal content data:%s", cmd.Content)
continue
}
toUser, exist := manager.User(msg.To)
if !exist {
log.Printf("peer user %s is offline", msg.To)
continue
}
data := struct {
From string
Message string
}{
From: connInfo.user.Uid,
Message: msg.Message,
}
if err := json.NewEncoder(toUser.conn.conn).Encode(data); err != nil {
log.Printf("send data from %s to %s err:%s", connInfo.user.Uid, msg.To, err.Error())
continue
}
default:
log.Printf("unknown command:%s", cmd.Command)
}
}
}
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
Start()
}
client 端
通过telnet
连接Server
telnet localhost 8099
然后再发送登录uid:001指令
{
"command":"Login",
"content":" { \"uid\":\"001\", \"name\":\"Tom\" } "
}
再起一个窗口,也通过telnet
连接Server
telnet localhost 8099
然后再发送登录uid:002指令
{
"command":"Login",
"content":" { \"uid\":\"002\", \"name\":\"Jack\" } "
}
然后在之前的uid:001窗口给对端uid:002发送消息指令
{
"command":"Send",
"content":" { \"to\":\"002\", \"message\":\"Hello Jack\" } "
}
可以看到,uid:002 收到了uid:001 发过来的
Hello Jack
消息了。