first commit

This commit is contained in:
kenneth 2023-12-24 00:10:41 +08:00
commit 7b1c126acd
7 changed files with 251 additions and 0 deletions

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# TCP CHAT
+ `/nick <name>` 设置自己的昵称
+ `/join <room>` 加入聊天房间
+ `/rooms` 获取聊天房间列表
+ `/msg <msg>` 发送消息
+ `/quit` 退出房间

71
client.go Normal file
View 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
View 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
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/zhang2092/tcp-chat
go 1.21.4

28
main.go Normal file
View 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
View 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
View 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))
}
}