first commit
This commit is contained in:
commit
7b1c126acd
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# TCP CHAT
|
||||
|
||||
+ `/nick <name>` 设置自己的昵称
|
||||
+ `/join <room>` 加入聊天房间
|
||||
+ `/rooms` 获取聊天房间列表
|
||||
+ `/msg <msg>` 发送消息
|
||||
+ `/quit` 退出房间
|
||||
71
client.go
Normal file
71
client.go
Normal file
@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
conn net.Conn
|
||||
nick string
|
||||
room *room
|
||||
commands chan<- command
|
||||
}
|
||||
|
||||
func (c *client) readInput() {
|
||||
for {
|
||||
msg, err := bufio.NewReader(c.conn).ReadString('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
msg = strings.Trim(msg, "\r\n")
|
||||
args := strings.Split(msg, " ")
|
||||
cmd := strings.TrimSpace(args[0])
|
||||
|
||||
switch cmd {
|
||||
case "/nick":
|
||||
c.commands <- command{
|
||||
id: CMD_NICK,
|
||||
client: c,
|
||||
args: args,
|
||||
}
|
||||
case "/join":
|
||||
c.commands <- command{
|
||||
id: CMD_JOIN,
|
||||
client: c,
|
||||
args: args,
|
||||
}
|
||||
case "/rooms":
|
||||
c.commands <- command{
|
||||
id: CMD_ROOMS,
|
||||
client: c,
|
||||
args: args,
|
||||
}
|
||||
case "/msg":
|
||||
c.commands <- command{
|
||||
id: CMD_MSG,
|
||||
client: c,
|
||||
args: args,
|
||||
}
|
||||
case "/quit":
|
||||
c.commands <- command{
|
||||
id: CMD_QUIT,
|
||||
client: c,
|
||||
args: args,
|
||||
}
|
||||
default:
|
||||
c.err(fmt.Errorf("unknown command %s", cmd))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) err(err error) {
|
||||
c.conn.Write([]byte("ERR: " + err.Error() + "\n"))
|
||||
}
|
||||
|
||||
func (c *client) msg(msg string) {
|
||||
c.conn.Write([]byte("> " + msg + "\n"))
|
||||
}
|
||||
17
command.go
Normal file
17
command.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type commandId int
|
||||
|
||||
const (
|
||||
CMD_NICK = iota
|
||||
CMD_JOIN
|
||||
CMD_ROOMS
|
||||
CMD_MSG
|
||||
CMD_QUIT
|
||||
)
|
||||
|
||||
type command struct {
|
||||
id commandId
|
||||
client *client
|
||||
args []string
|
||||
}
|
||||
28
main.go
Normal file
28
main.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := newServer()
|
||||
go s.run()
|
||||
|
||||
linstener, err := net.Listen("tcp", ":8888")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to start server: %v", err)
|
||||
}
|
||||
defer linstener.Close()
|
||||
log.Println("started server on :8888")
|
||||
|
||||
for {
|
||||
conn, err := linstener.Accept()
|
||||
if err != nil {
|
||||
log.Printf("failed to accept connection: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.newClient(conn)
|
||||
}
|
||||
}
|
||||
16
room.go
Normal file
16
room.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "net"
|
||||
|
||||
type room struct {
|
||||
name string
|
||||
members map[net.Addr]*client
|
||||
}
|
||||
|
||||
func (r *room) broadcast(c *client, msg string) {
|
||||
for addr, m := range r.members {
|
||||
if addr != c.conn.RemoteAddr() {
|
||||
m.msg(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
109
server.go
Normal file
109
server.go
Normal file
@ -0,0 +1,109 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type server struct {
|
||||
rooms map[string]*room
|
||||
commands chan command
|
||||
}
|
||||
|
||||
func newServer() *server {
|
||||
return &server{
|
||||
rooms: make(map[string]*room),
|
||||
commands: make(chan command),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) run() {
|
||||
for cmd := range s.commands {
|
||||
switch cmd.id {
|
||||
case CMD_NICK:
|
||||
s.nick(cmd.client, cmd.args)
|
||||
case CMD_JOIN:
|
||||
s.join(cmd.client, cmd.args)
|
||||
case CMD_ROOMS:
|
||||
s.listRooms(cmd.client, cmd.args)
|
||||
case CMD_MSG:
|
||||
s.msg(cmd.client, cmd.args)
|
||||
case CMD_QUIT:
|
||||
s.quit(cmd.client, cmd.args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) newClient(conn net.Conn) {
|
||||
log.Printf("new client has connected: %s", conn.RemoteAddr().String())
|
||||
|
||||
c := &client{
|
||||
conn: conn,
|
||||
nick: "anonymous",
|
||||
commands: s.commands,
|
||||
}
|
||||
|
||||
c.readInput()
|
||||
}
|
||||
|
||||
func (s *server) nick(c *client, args []string) {
|
||||
c.nick = args[1]
|
||||
c.msg(fmt.Sprintf("all right, I will call you %s", c.nick))
|
||||
}
|
||||
|
||||
func (s *server) join(c *client, args []string) {
|
||||
roomName := args[1]
|
||||
|
||||
r, ok := s.rooms[roomName]
|
||||
if !ok {
|
||||
r = &room{
|
||||
name: roomName,
|
||||
members: make(map[net.Addr]*client),
|
||||
}
|
||||
s.rooms[roomName] = r
|
||||
}
|
||||
|
||||
r.members[c.conn.RemoteAddr()] = c
|
||||
|
||||
s.quitCurrentRoom(c)
|
||||
c.room = r
|
||||
|
||||
r.broadcast(c, fmt.Sprintf("%s has joined the room", c.nick))
|
||||
c.msg(fmt.Sprintf("Wecome to %s", r.name))
|
||||
}
|
||||
|
||||
func (s *server) listRooms(c *client, args []string) {
|
||||
var rooms []string
|
||||
for name := range s.rooms {
|
||||
rooms = append(rooms, name)
|
||||
}
|
||||
|
||||
c.msg(fmt.Sprintf("available rooms are: %s", strings.Join(rooms, ", ")))
|
||||
}
|
||||
|
||||
func (s *server) msg(c *client, args []string) {
|
||||
if c.room == nil {
|
||||
c.err(errors.New("you must join the room first"))
|
||||
return
|
||||
}
|
||||
|
||||
c.room.broadcast(c, strings.Join(args[1:], " "))
|
||||
}
|
||||
|
||||
func (s *server) quit(c *client, args []string) {
|
||||
log.Printf("client has disconnection: %s", c.conn.RemoteAddr().String())
|
||||
|
||||
s.quitCurrentRoom(c)
|
||||
c.msg("sad to see you go :(")
|
||||
c.conn.Close()
|
||||
}
|
||||
|
||||
func (s *server) quitCurrentRoom(c *client) {
|
||||
if c.room != nil {
|
||||
delete(c.room.members, c.conn.RemoteAddr())
|
||||
c.room.broadcast(c, fmt.Sprintf("%s has left the room", c.nick))
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user