github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/modules/jsock/jsock.go (about) 1 package jsock 2 3 import ( 4 "fmt" 5 "log" 6 "net/url" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/insionng/yougam/helper" 12 "github.com/insionng/yougam/libraries/igm/sockjs-go.v2/sockjs" 13 "github.com/insionng/yougam/models" 14 "github.com/insionng/yougam/modules/setting" 15 "github.com/insionng/yougam/routers" 16 17 "github.com/insionng/makross" 18 ) 19 20 //Client 客户端 21 type Client struct { 22 session sockjs.Session 23 clientIP string 24 userid int64 25 } 26 27 //Box 盒子 28 type Box struct { 29 sync.RWMutex 30 key string 31 box []*Client 32 } 33 34 //Boxes 盒子集合 35 type Boxes struct { 36 sync.RWMutex 37 boxes []*Box 38 } 39 40 var boxes = newBoxes() 41 42 func (b *Box) appendClient(client *Client) { 43 b.Lock() 44 defer b.Unlock() 45 b.box = append(b.box, client) 46 /* 47 for _, c := range b.box { 48 if c != client { 49 log.Println(client.clientIP, "进入聊天~") 50 } 51 } 52 */ 53 54 } 55 56 func (b *Box) removeClient(client *Client) { 57 b.Lock() 58 defer b.Unlock() 59 60 for index, c := range b.box { 61 if c == client { 62 b.box = append(b.box[:index], b.box[(index+1):]...) 63 if err := c.session.Send(`<div class="alert alert-warning alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button><h4 class="alert-heading">警告</h4>` + `你的账号在别处连接,当前通信已经失效,如非本人操作请立即修改账号密码..` + `</div>`); err != nil { 64 return 65 } 66 } else { 67 if err := client.session.Send(`<div class="alert alert-warning alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button><h4 class="alert-heading">警告</h4>` + `你的账号已经重新连接,原连接已经失效,系统禁止同一账号在多客户端同时连接..` + `</div>`); err != nil { 68 return 69 } 70 } 71 } 72 } 73 74 func (b *Box) removeUser(client *Client) { 75 b.Lock() 76 defer b.Unlock() 77 78 for index, c := range b.box { 79 if c.userid == client.userid { 80 b.box = append(b.box[:index], b.box[(index+1):]...) 81 if err := c.session.Send(`<div class="alert alert-warning alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button><h4 class="alert-heading">警告</h4>` + `你的账号在别处连接,当前通信已经失效,如非本人操作请立即修改账号密码..` + `</div>`); err != nil { 82 return 83 } 84 } else { 85 if err := client.session.Send(`<div class="alert alert-warning alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button><h4 class="alert-heading">警告</h4>` + `你的账号已经重新连接,原连接已经失效,系统禁止同一账号在多客户端同时连接..` + `</div>`); err != nil { 86 return 87 } 88 } 89 } 90 } 91 92 func tpl(align, username, avatar, datetime, message string) string { 93 94 var bg = "b-light" 95 if align == "right" { 96 bg = "bg-light" 97 } 98 99 if len(avatar) == 0 { 100 avatar = "/identicon/" + username + "/48/default.png" 101 } 102 103 var tpl = ` 104 <article class="chat-item ` + align + `"> 105 <a href="/user/` + username + `/" class="pull-` + align + ` thumb-sm avatar"> 106 <img src="` + avatar + `" alt="` + username + `"/> 107 <div class="text-ellipsis text-center">` + username + `</div> 108 </a> 109 <section class="chat-body"> 110 <div class="panel ` + bg + ` text-sm m-b-none"> 111 <div class="panel-body"> 112 <span class="arrow ` + align + `"></span> 113 <div class="m-b-none">` + message + `</div> 114 <div class="clear"></div> 115 </div> 116 </div> 117 <small class="text-muted"> <i class="fa fa-ok text-success"></i> 118 ` + datetime + ` 119 </small> 120 </section> 121 </article> 122 ` 123 return tpl 124 } 125 126 func (b *Box) broadcastMessage(currentUserid int64, username, avatar, datetime, message string) { 127 b.Lock() 128 defer b.Unlock() 129 130 var align string 131 for _, client := range b.box { 132 133 if client.userid == currentUserid { 134 align = "right" 135 } else { 136 align = "left" 137 } 138 139 if err := client.session.Send(tpl(align, username, avatar, datetime, message)); err != nil { 140 return 141 } 142 } 143 } 144 145 func (b *Box) broadcastMessageTo(isMyself bool, currentUserid int64, username, avatar, datetime, message string) { 146 b.Lock() 147 defer b.Unlock() 148 149 var align string 150 for _, client := range b.box { 151 152 if client.userid == currentUserid { 153 align = "right" 154 } else { 155 align = "left" 156 } 157 158 if isMyself { 159 if client.userid == currentUserid { 160 if err := client.session.Send(tpl(align, username, avatar, datetime, message)); err != nil { 161 return 162 } 163 } 164 } else { 165 if client.userid != currentUserid { 166 if err := client.session.Send(tpl(align, username, avatar, datetime, message)); err != nil { 167 return 168 } 169 } 170 } 171 172 } 173 } 174 175 func (b *Boxes) getBox(key string) *Box { 176 b.Lock() 177 defer b.Unlock() 178 179 for _, Box := range b.boxes { 180 if Box.key == key { 181 return Box 182 } 183 } 184 185 box := &Box{sync.RWMutex{}, key, make([]*Client, 0)} 186 b.boxes = append(b.boxes, box) 187 return box 188 } 189 190 func newBoxes() *Boxes { 191 return &Boxes{sync.RWMutex{}, make([]*Box, 0)} 192 } 193 194 func sockHandler(session sockjs.Session) { 195 l, err := url.Parse(session.Request().RequestURI) 196 if err != nil { 197 session.Close(1008, "Bad Request") 198 return 199 } 200 201 sender := l.Query()["sender"][0] 202 receiver := l.Query()["receiver"][0] 203 token := l.Query()["token"][0] 204 205 if (sender == receiver) || (len(sender) == 0) || (len(receiver) == 0) || (len(token) != 48) { 206 session.Close(1008, "Bad Request") 207 return 208 } 209 210 if len(receiver) > 0 { 211 recipient, err := models.GetUserByUsername(receiver) 212 if (err != nil) || (recipient == nil) { 213 log.Println(err) 214 session.Close(1008, "Bad Request") 215 return 216 } 217 218 if key := sender + ":" + helper.AesKey + ":" + receiver; !helper.ValidateHash(token, key) { 219 session.Close(1008, "Bad Request") 220 return 221 } 222 223 var me *models.User 224 item := setting.Cache.Get(token) 225 if item == nil { 226 session.Close(1008, "Bad Request") 227 return 228 } 229 230 if user, okay := item.Value().(*models.User); okay { 231 232 usr, e := models.GetUserByUsername(sender) 233 if (e != nil) || (usr == nil) { 234 session.Close(1011, "Unauthorized") 235 return 236 } 237 238 if (usr.Password != user.Password) || (usr.Username != user.Username) { 239 session.Close(1008, "Bad Request") 240 return 241 } 242 243 if !models.IsFriend(usr.Id, recipient.Id) { 244 session.Close(1008, "Bad Request") 245 return 246 } 247 248 setting.Cache.Delete(token) //用完即弃 249 me = usr 250 251 } else { 252 session.Close(1008, "Bad Request") 253 return 254 } 255 256 rAddr := session.Request().RemoteAddr 257 sockCli := &Client{session, rAddr, me.Id} 258 orderKey := helper.OrderKey(me.Id, recipient.Id) 259 box := boxes.getBox(orderKey) 260 261 if len(box.box) < 2 { 262 box.appendClient(sockCli) 263 } else { 264 uid := fmt.Sprintf("%v", me.Id) 265 //如果是原来的用户则更新连接地址,即禁止用户同时登录2个以上客户端 266 if s := strings.Split(box.key, ":"); (s[0] == uid) || (s[1] == uid) { 267 box.removeUser(sockCli) 268 box.appendClient(sockCli) 269 } else { 270 session.Close(1008, "Bad Request") 271 return 272 } 273 274 } 275 276 //读取并发送离线消息 277 messages, e := models.GetMessagesViaReceiverWithSender(0, 0, me.Username, receiver, "asc") 278 if (e == nil) && (messages != nil) { 279 for _, v := range *messages { 280 models.DelMessage(v.Id) //阅后即焚 281 box.broadcastMessageTo(false, v.Uid, v.Sender, v.Avatar, helper.TimeSince(v.Created), v.Content) 282 } 283 } 284 285 for { 286 287 //若果双方好友关系已经解除 288 if !models.IsFriend(me.Id, recipient.Id) { 289 session.Close(1008, "Bad Request") 290 return 291 } 292 293 message, err := session.Recv() 294 if err != nil { 295 box.removeClient(sockCli) 296 session.Close(1000, "Closed Request") 297 return 298 } 299 300 policy := helper.ObjPolicy() 301 body := policy.Sanitize(message) 302 now := time.Now() 303 box.broadcastMessage(me.Id, me.Username, me.AvatarMedium, now.Format("2006-01-02 03:04"), body) 304 go func() { 305 //若果对方没有连线 306 if box := boxes.getBox(orderKey); len(box.box) == 1 { 307 m := new(models.Message) 308 m.Key = orderKey 309 m.Uid = me.Id 310 m.Sender = me.Username 311 m.Avatar = me.AvatarMedium 312 m.Receiver = receiver 313 m.Content = body 314 m.Created = now.Unix() 315 models.PostMessage(m) 316 } 317 318 //always save the history message 319 m := new(models.HistoryMessage) 320 m.Key = orderKey 321 m.Uid = me.Id 322 m.Sender = me.Username 323 m.Avatar = me.AvatarMedium 324 m.Receiver = receiver 325 m.Content = body 326 m.Created = now.Unix() 327 models.PostHistoryMessage(m) 328 }() 329 330 } 331 } else { 332 session.Close(1008, "Bad Request") 333 return 334 } 335 336 } 337 338 //JSockHandler 对象 339 var JSockHandler = sockjs.NewHandler("/sock", sockjs.Options{ 340 Websocket: true, 341 JSessionID: nil, 342 SockJSURL: fmt.Sprintf("http://%s/%s", helper.Domain, "/libs/sockjs-client-1.1.0/sockjs.min.js"), 343 HeartbeatDelay: 25 * time.Second, 344 DisconnectDelay: 5 * time.Second, 345 ResponseLimit: 128 * 1024, 346 }, sockHandler) 347 348 //JSock 路由 349 func JSock(m *makross.Makross) { 350 m.Get("/contact/", routers.GetContactHandler) 351 m.Get("/contact/search/", routers.GetContactHandler) 352 m.Get("/connect/<name:([\\x{4e00}-\\x{9fa5}A-Z0-9a-z_-]+)>/", routers.GetConnectHandler) 353 }