Guest User

Untitled

a guest
Jul 6th, 2015
259
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.80 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4. "bufio"
  5. "flag"
  6. "fmt"
  7. "log"
  8. "net"
  9. "strings"
  10. "sync"
  11. )
  12.  
  13. func main() {
  14. log.SetPrefix("chat server: ")
  15. addr := flag.String("addr", ":4000", "listen address")
  16. flag.Parse()
  17. server := new(server)
  18. log.Fatal(server.listenAndServe(*addr))
  19. }
  20.  
  21. type server struct {
  22. addToChannel chan *client
  23. addUsername chan *client
  24. remUsername chan *client
  25. remFromChannel chan *client
  26. changeChannel chan *client
  27. remChannel chan bool
  28. usernameList map[string]*client
  29. channelList map[string]*channel
  30. }
  31.  
  32. type channel struct {
  33. addClient chan *client
  34. remClient chan *client
  35. broadcast chan string
  36. name string
  37. server *server
  38. }
  39.  
  40. type client struct {
  41. username string
  42. conn *net.Conn
  43. currentChannel *channel
  44. newChannel string
  45. reader *bufio.Reader
  46. message chan string
  47. server *server
  48. id string
  49. }
  50.  
  51. func (server *server) listenAndServe(addr string) error {
  52. ln, err := net.Listen("tcp", addr)
  53. if err != nil {
  54. return err
  55. }
  56. defer ln.Close()
  57. go server.manageServer()
  58. log.Printf("listening on %s\n", addr)
  59. for {
  60. conn, err := ln.Accept()
  61. if err != nil {
  62. return err
  63. }
  64. log.Printf("new connection from %s\n", conn.RemoteAddr().String())
  65. go initializeClient(&conn, server)
  66. }
  67. }
  68.  
  69. func initializeClient(conn *net.Conn, server *server) {
  70. log.Printf("initalizing %s\n", (*conn).RemoteAddr().String())
  71. client := client{conn: conn, reader: bufio.NewReader(*conn), server: server, id: "@" + (*conn).RemoteAddr().String(), message: make(chan string)}
  72. _, err := fmt.Fprint(*client.conn, "welcome to the chat server\n")
  73. if err != nil {
  74. client.shutdown()
  75. return
  76. }
  77. client.getUsername()
  78. }
  79.  
  80. func (client *client) shutdown() {
  81. if client.currentChannel != nil {
  82. client.server.remFromChannel <- client
  83. }
  84. if client.username != "" {
  85. client.server.remUsername <- client
  86. }
  87. log.Printf("%s shutting down", client.id)
  88. (*client.conn).Close()
  89. log.Printf("%s shut down", client.id)
  90. }
  91.  
  92. func getTrimmed(client *client, what string) (fromUser string, err error) {
  93. _, err = fmt.Fprint(*client.conn, what+": ")
  94. if err != nil {
  95. client.shutdown()
  96. return
  97. }
  98. fromUser, err = client.reader.ReadString('\n')
  99. if err != nil {
  100. client.shutdown()
  101. return
  102. }
  103. return strings.TrimSpace(fromUser), err
  104. }
  105.  
  106. func (client *client) getUsername() {
  107. var err error
  108. log.Printf("getting username for %s\n", client.id)
  109. client.username, err = getTrimmed(client, "username")
  110. if err != nil {
  111. return
  112. }
  113. client.server.addUsername <- client
  114. }
  115.  
  116. func (client *client) joinChannel(channelName string) {
  117. log.Printf("getting channel for %s\n", client.id)
  118. if channelName == "" {
  119. var err error
  120. channelName, err = getTrimmed(client, "channel name")
  121. if err != nil {
  122. return
  123. }
  124. }
  125. client.currentChannel = &channel{name: channelName, server: client.server}
  126. client.server.addToChannel <- client
  127. }
  128.  
  129. func (server *server) manageServer() {
  130. server.addUsername = make(chan *client)
  131. server.addToChannel = make(chan *client)
  132. server.remUsername = make(chan *client)
  133. server.remFromChannel = make(chan *client)
  134. server.remChannel = make(chan bool)
  135. server.changeChannel = make(chan *client)
  136. server.usernameList = make(map[string]*client)
  137. server.channelList = make(map[string]*channel)
  138.  
  139. remFromChannel := func(client *client) {
  140. client.currentChannel.remClient <- client
  141. if true, _ := <-server.remChannel; true {
  142. log.Printf("channel %s shutting down", client.currentChannel.name)
  143. delete(server.channelList, client.currentChannel.name)
  144. }
  145. }
  146.  
  147. for {
  148. select {
  149. case c := <-server.addUsername:
  150. if _, used := server.usernameList[c.username]; used {
  151. _, err := fmt.Fprintf(*c.conn, "username %s is not available\n", c.username)
  152. if err != nil {
  153. c.shutdown()
  154. break
  155. }
  156. go c.getUsername()
  157. break
  158. }
  159. server.usernameList[c.username] = c
  160. c.id = c.username + c.id
  161. log.Printf("got username for %s", c.id)
  162. go c.joinChannel("")
  163. case c := <-server.addToChannel:
  164. log.Printf("got channel %s for %s\n", c.currentChannel.name, c.id)
  165. if _, exists := server.channelList[c.currentChannel.name]; exists {
  166. c.currentChannel = server.channelList[c.currentChannel.name]
  167. c.currentChannel.addClient <- c
  168. break
  169. }
  170. log.Printf("creating channel %s", c.currentChannel.name)
  171. server.channelList[c.currentChannel.name] = c.currentChannel
  172. c.currentChannel.addClient = make(chan *client)
  173. go c.currentChannel.manageChannel()
  174. c.currentChannel.addClient <- c
  175. log.Printf("created channel %s", c.currentChannel.name)
  176. case c := <-server.remUsername:
  177. delete(server.usernameList, c.username)
  178. case c := <-server.remFromChannel:
  179. remFromChannel(c)
  180. case c := <-server.changeChannel:
  181. remFromChannel(c)
  182. go c.joinChannel(c.newChannel)
  183. }
  184. }
  185. }
  186.  
  187. type clientList struct {
  188. m (map[string]*client)
  189. sync.RWMutex
  190. }
  191.  
  192. func (channel *channel) manageChannel() {
  193. channel.broadcast = make(chan string)
  194. channel.remClient = make(chan *client)
  195. clientList := clientList{m: make(map[string]*client)}
  196. broadcastLoop := func() {
  197. for {
  198. message := <-channel.broadcast
  199. clientList.RLock()
  200. for _, c := range clientList.m {
  201. c.message <- message
  202. }
  203. clientList.RUnlock()
  204. }
  205. }
  206. go broadcastLoop()
  207. for {
  208. select {
  209. case client := <-channel.addClient:
  210. fmt.Fprintf(*client.conn, "joining channel %s\n", channel.name)
  211. client.currentChannel = channel
  212. clientList.m[client.username] = client
  213. log.Printf("%s has joined channel %s", client.id, channel.name)
  214. go client.writeLoop()
  215. channel.broadcast <- "+++ "+client.username+" has joined the channel\n"
  216. go client.readLoop()
  217. case client := <-channel.remClient:
  218. fmt.Fprintf(*client.conn, "leaving channel %s\n", channel.name)
  219. delete(clientList.m, client.username)
  220. log.Printf("%s has left channel %s", client.id, channel.name)
  221. channel.broadcast <- "--- "+client.username+" has left the channel\n"
  222. if len(clientList.m) == 0 {
  223. channel.server.remChannel <- true
  224. } else {
  225. channel.server.remChannel <- false
  226. }
  227. }
  228. }
  229. }
  230.  
  231. func (client *client) readLoop() {
  232. for {
  233. payload, err := client.reader.ReadString('\n')
  234. if err != nil {
  235. client.shutdown()
  236. return
  237. }
  238. if strings.Contains(payload, "/chch") {
  239. log.Printf("changing channel for %s", client.id)
  240. client.newChannel = strings.TrimSpace(payload[6:])
  241. client.server.changeChannel <- client
  242. return
  243. }
  244. log.Printf("broadcast: >>> %s: %s", client.id, payload)
  245. client.currentChannel.broadcast <- ">>> " + client.username + ": " + payload
  246. }
  247. }
  248.  
  249. func (client *client) writeLoop() {
  250. for {
  251. message := <-client.message
  252. _, err := fmt.Fprintf(*client.conn, message)
  253. if err != nil {
  254. client.shutdown()
  255. return
  256. }
  257. }
  258. }
Advertisement
Add Comment
Please, Sign In to add comment