235 lines
5.0 KiB
Go
235 lines
5.0 KiB
Go
package main
|
||
|
||
// Start Commond eg: ./client 1 1000 localhost:3101
|
||
// first parameter:beginning userId
|
||
// second parameter: amount of clients
|
||
// third parameter: comet tcp-server addr
|
||
|
||
import (
|
||
"bufio"
|
||
"encoding/binary"
|
||
"flag"
|
||
"fmt"
|
||
"log"
|
||
"math/rand"
|
||
"net"
|
||
"os"
|
||
"runtime"
|
||
"strconv"
|
||
"sync/atomic"
|
||
"time"
|
||
|
||
"github.com/golang/protobuf/proto"
|
||
comet "gitlab.33.cn/chat/im/api/comet/grpc"
|
||
)
|
||
|
||
const (
|
||
rawHeaderLen = uint16(16)
|
||
heart = 5 * time.Second
|
||
)
|
||
|
||
type Proto struct {
|
||
PackLen int32 // package length
|
||
HeaderLen int16 // header length
|
||
Ver int16 // protocol version
|
||
Operation int32 // operation for request
|
||
Seq int32 // sequence number chosen by client
|
||
Body []byte // body
|
||
}
|
||
|
||
var (
|
||
countDown int64
|
||
aliveCount int64
|
||
)
|
||
|
||
func main() {
|
||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||
flag.Parse()
|
||
begin, err := strconv.Atoi(os.Args[1])
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
num, err := strconv.Atoi(os.Args[2])
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
go result()
|
||
for i := begin; i < begin+num; i++ {
|
||
go client(int64(i))
|
||
}
|
||
// signal
|
||
var exit chan bool
|
||
<-exit
|
||
}
|
||
|
||
func result() {
|
||
var (
|
||
lastTimes int64
|
||
interval = int64(5)
|
||
)
|
||
for {
|
||
nowCount := atomic.LoadInt64(&countDown)
|
||
nowAlive := atomic.LoadInt64(&aliveCount)
|
||
diff := nowCount - lastTimes
|
||
lastTimes = nowCount
|
||
fmt.Println(fmt.Sprintf("%s alive:%d down:%d down/s:%d", time.Now().Format("2006-01-02 15:04:05"), nowAlive, nowCount, diff/interval))
|
||
time.Sleep(time.Second * time.Duration(interval))
|
||
}
|
||
}
|
||
|
||
func client(mid int64) {
|
||
for {
|
||
startClient(mid)
|
||
time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
|
||
}
|
||
}
|
||
|
||
func startClient(key int64) {
|
||
time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
|
||
atomic.AddInt64(&aliveCount, 1)
|
||
quit := make(chan bool, 1)
|
||
defer func() {
|
||
close(quit)
|
||
atomic.AddInt64(&aliveCount, -1)
|
||
}()
|
||
// connnect to server
|
||
conn, err := net.Dial("tcp", os.Args[3])
|
||
if err != nil {
|
||
log.Printf("net.Dial(%s) error(%v)", os.Args[3], err)
|
||
return
|
||
}
|
||
seq := int32(0)
|
||
wr := bufio.NewWriter(conn)
|
||
rd := bufio.NewReader(conn)
|
||
authMsg := &comet.AuthMsg{
|
||
AppId: "echo",
|
||
Token: "fdasfdsaf",
|
||
}
|
||
p := new(Proto)
|
||
p.Ver = 1
|
||
p.Operation = int32(comet.Op_Auth)
|
||
p.Seq = seq
|
||
p.Body, _ = proto.Marshal(authMsg)
|
||
|
||
if err = tcpWriteProto(wr, p); err != nil {
|
||
log.Printf("tcpWriteProto() error(%v)", err)
|
||
return
|
||
}
|
||
if err = tcpReadProto(rd, p); err != nil {
|
||
log.Printf("tcpReadProto() error(%v)", err)
|
||
return
|
||
}
|
||
log.Printf("key:%d auth ok, proto: %v", key, p)
|
||
seq++
|
||
// writer
|
||
go func() {
|
||
hbProto := new(Proto)
|
||
for {
|
||
// heartbeat
|
||
hbProto.Operation = int32(comet.Op_Heartbeat)
|
||
hbProto.Seq = seq
|
||
hbProto.Body = nil
|
||
if err = tcpWriteProto(wr, hbProto); err != nil {
|
||
log.Printf("key:%d tcpWriteProto() error(%v)", key, err)
|
||
return
|
||
}
|
||
log.Printf("key:%d Write heartbeat", key)
|
||
time.Sleep(heart)
|
||
seq++
|
||
select {
|
||
case <-quit:
|
||
return
|
||
default:
|
||
}
|
||
}
|
||
}()
|
||
// reader
|
||
for {
|
||
if err = tcpReadProto(rd, p); err != nil {
|
||
log.Printf("key:%d tcpReadProto() error(%v)", key, err)
|
||
quit <- true
|
||
return
|
||
}
|
||
if p.Operation == int32(comet.Op_AuthReply) {
|
||
log.Printf("key:%d auth success", key)
|
||
} else if p.Operation == int32(comet.Op_HeartbeatReply) {
|
||
log.Printf("key:%d receive heartbeat reply", key)
|
||
if err = conn.SetReadDeadline(time.Now().Add(heart + 60*time.Second)); err != nil {
|
||
log.Printf("conn.SetReadDeadline() error(%v)", err)
|
||
quit <- true
|
||
return
|
||
}
|
||
} else {
|
||
log.Printf("key:%d op:%d msg: %s", key, p.Operation, string(p.Body))
|
||
atomic.AddInt64(&countDown, 1)
|
||
}
|
||
}
|
||
}
|
||
|
||
func tcpWriteProto(wr *bufio.Writer, proto *Proto) (err error) {
|
||
// write
|
||
if err = binary.Write(wr, binary.BigEndian, uint32(rawHeaderLen)+uint32(len(proto.Body))); err != nil {
|
||
return
|
||
}
|
||
if err = binary.Write(wr, binary.BigEndian, rawHeaderLen); err != nil {
|
||
return
|
||
}
|
||
if err = binary.Write(wr, binary.BigEndian, proto.Ver); err != nil {
|
||
return
|
||
}
|
||
if err = binary.Write(wr, binary.BigEndian, proto.Operation); err != nil {
|
||
return
|
||
}
|
||
if err = binary.Write(wr, binary.BigEndian, proto.Seq); err != nil {
|
||
return
|
||
}
|
||
if proto.Body != nil {
|
||
if err = binary.Write(wr, binary.BigEndian, proto.Body); err != nil {
|
||
return
|
||
}
|
||
}
|
||
err = wr.Flush()
|
||
return
|
||
}
|
||
|
||
func tcpReadProto(rd *bufio.Reader, proto *Proto) (err error) {
|
||
var (
|
||
packLen int32
|
||
headerLen int16
|
||
)
|
||
// read
|
||
if err = binary.Read(rd, binary.BigEndian, &packLen); err != nil {
|
||
return
|
||
}
|
||
if err = binary.Read(rd, binary.BigEndian, &headerLen); err != nil {
|
||
return
|
||
}
|
||
if err = binary.Read(rd, binary.BigEndian, &proto.Ver); err != nil {
|
||
return
|
||
}
|
||
if err = binary.Read(rd, binary.BigEndian, &proto.Operation); err != nil {
|
||
return
|
||
}
|
||
if err = binary.Read(rd, binary.BigEndian, &proto.Seq); err != nil {
|
||
return
|
||
}
|
||
var (
|
||
n, t int
|
||
bodyLen = int(packLen - int32(headerLen))
|
||
)
|
||
if bodyLen > 0 {
|
||
proto.Body = make([]byte, bodyLen)
|
||
for {
|
||
if t, err = rd.Read(proto.Body[n:]); err != nil {
|
||
return
|
||
}
|
||
if n += t; n == bodyLen {
|
||
break
|
||
}
|
||
}
|
||
} else {
|
||
proto.Body = nil
|
||
}
|
||
return
|
||
}
|