go language implementation chat server sample code
- 2020-06-15 09:17:18
- OfStack
After two days of reading go, it was time to get some practice.
go's routine (routines) and chan (channels) are artifacts, and implementing multithreading (multiple routines in go, to be precise) is not too easy.
So began to code a stupid version of the black box chat.
server side:
Listen for the TCP connection; Support custom client commands; Support message distribution; Theoretically support broadcasting; .
package main
import (
"fmt"
"net"
"io"
"strconv"
"time"
"strings"
)
const (
NORMAL_MESSAGE = iota
LIST_MESSAGE
)
var clientSenders = make(map[string] chan string)
func send (addr string, conn *net.Conn){
senderChan := clientSenders[addr]
for s := range senderChan{
(*conn).Write([]byte(s))
}
}
func sendUsersInfo(addr string){
senderChan := clientSenders[addr]
if nil != senderChan{
ls := strconv.Itoa(LIST_MESSAGE)
cs := strconv.Itoa(NORMAL_MESSAGE) + " List of logged in clients: \n"
i := 1
for k := range clientSenders{
a := ""
if k == addr {
a = " (I) "
}
cs = cs + strconv.Itoa(i) + " ) " + k + a + "\n"
ls += k + "\n"
i ++
}
cs += " Send a message that can be used 1<- This is for 1 The number of client messages \n (Please use English for the best experience) \n"
senderChan <- cs
time.Sleep(time.Millisecond * 300)
senderChan <- ls
// Send a formatted list
fmt.Println(" "Login User Information" sent ", addr)
} else{
fmt.Println(" The client acceptance channel does not exist ", addr)
}
}
func serve (conn *net.Conn){
connect := *conn
addr := connect.RemoteAddr().String()
fmt.Println(addr, " Access to the service ")
senderChan := make(chan string, 3)
clientSenders[addr] = senderChan
// Start sending
go send(addr, conn)
// Send current user information
go sendUsersInfo(addr)
buff := make([]byte, 10240)
for {
n, err := connect.Read(buff)
if err != nil {
if err == io.EOF {
fmt.Println(" The client disconnects, ", addr)
delete(clientSenders, addr)
return
} else{
fmt.Println(err)
}
}
msg := string(buff[:n])
// Refresh the client list
if msg == "ls\n" {
go sendUsersInfo(addr)
continue
}
// Extract the data
msgs := strings.Split(msg, "<-")
if len(msg) < 2{
senderChan <- string(" Incorrect data format, please contact developer ")
continue
}
aimAddr := msgs[0]
aimSender := clientSenders[aimAddr]
if aimSender == nil {
senderChan <- string(" Client is offline and in use ls Command gets the latest client list ")
continue
}
aimSender <- strconv.Itoa(NORMAL_MESSAGE) + "[from:" + addr + "]:" + strings.Join(msgs[1:], "<-")
}
}
func main(){
addr := ":8080"
listener, err := net.Listen("tcp", addr)
if err != nil{
fmt.Println(err)
return
}
// Start the message scheduler
defer listener.Close()
// Start connection listening
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err)
continue
}
go serve(&conn)
}
}
Client:
Support disconnection and reconnection; Supports sending messages to specific other clients
package main
import (
"net"
"fmt"
"io"
"os"
"bufio"
"sync"
"time"
"strings"
"strconv"
)
var conn *net.Conn
var addrs []string
const (
NORMAL_MESSAGE = iota
LIST_MESSAGE
)
func read(conn2 *net.Conn){
defer func() {
fmt.Println(" Try to reconnect to ")
go connectServer()
}()
connect := *conn2
buff := make([]byte, 20140)
for {
n, err := connect.Read(buff)
if err != nil {
if err == io.EOF{
fmt.Println(" The end of the ")
(*conn2).Close()
conn = nil
return
} else{
fmt.Println(err)
}
}
msg := string(buff[:n])
t, err := strconv.Atoi(string(msg[0]))
msg = msg[1:]
switch t {
case NORMAL_MESSAGE:
fmt.Print(msg)
break
case LIST_MESSAGE:
// Parse client list data
addrs = strings.Split(msg, "\n")
fmt.Println(" Received client list. \n")
break
default:
fmt.Print(msg)
break
}
}
}
func connectServer(){
addr := "192.168.99.236:8080"
fmt.Println(" Wait for the server to start ")
conn2, err := net.Dial("tcp", addr)
if err != nil {
fmt.Print(err)
fmt.Println(" Connection failed, 10s After trying to ")
time.Sleep(10 * time.Second)
go connectServer()
return
}
fmt.Println(" The connected ")
conn = &conn2
go read(&conn2)
}
func send (){
inputReader := bufio.NewReader(os.Stdout)
for {
input, err := inputReader.ReadString('\n')
if err != nil {
if err == io.EOF{
return
} else{
fmt.Println(err)
}
}
if input == "ls\n" {
(*conn).Write([]byte(input))
continue
}
msgs := strings.Split(input, "<-")
if len(msgs) < 2 {
fmt.Println(" The sending posture is not correct. It should look like this 1<- to 1 No. Send message \n")
continue
}
index, err := strconv.Atoi(msgs[0])
if err != nil {
fmt.Println(" The sending posture is not correct. It should look like this 1<- to 1 No. Send message \n")
continue
}
if len(addrs) <= index {
fmt.Println(" There is no first " + strconv.Itoa(index) + " A client \n")
continue
}
addr := addrs[index-1]
input = addr + "<-" + strings.Join(msgs[1:], "<-")
if nil != conn {
(*conn).Write([]byte(input))
}
}
}
func main (){
var wg sync.WaitGroup
wg.Add(2)
go connectServer()
go send()
wg.Wait()
defer func() {
if nil != conn {
(*conn).Close()
}
}()
}