Skip to content

Commit

Permalink
首次提交
Browse files Browse the repository at this point in the history
  • Loading branch information
shiguanghuxian committed Dec 30, 2017
1 parent 9174344 commit 141d567
Show file tree
Hide file tree
Showing 13 changed files with 942 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@

# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/

examples/chat/client/client*
examples/chat/server/server*
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
# tcplibrary
golang 版的tcp通讯库,让tcp开发更简单
# golang的tcp通讯库

使用此库你只需要创建一个server或client的结构体,实现接口,再写两句话就可以搭建起tcp通讯

## 备注
自定义协议的小伙伴如果需要websocket通讯时,请参考default_packet.go中的`GetPayload()`函数,包内容问题

当需要获取连接列表时 可以调用`GetClients()`方法获取连接`*sync.Map`
89 changes: 89 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* @Author: 时光弧线
* @Date: 2017-12-30 11:54:57
* @Last Modified by: 时光弧线
* @Last Modified time: 2017-12-30 13:11:34
*/
package tcplibrary

import (
"errors"
"net"
)

/* tcp golang客户端 */

// TCPClient tcp客户端
type TCPClient struct {
*TCPLibrary
conn *Conn // 连接对象
}

// NewTCPClient 创建一个tcp客户端
func NewTCPClient(debug bool, socket Socket, packets ...Packet) (*TCPClient, error) {
if socket == nil {
return nil, errors.New("Socket参数不能是nil")
}
// 封包解包对象
var packet Packet
if len(packets) == 0 {
packet = new(DefaultPacket)
} else {
packet = packets[0]
}
// 标记为客户端
isServer = false

return &TCPClient{
TCPLibrary: &TCPLibrary{
packet: packet,
socket: socket,
readDeadline: DefaultReadDeadline,
readBufferSize: DefaultBufferSize,
},
}, nil
}

// DialAndStart 连接到服务器,并开始读取信息
func (c *TCPClient) DialAndStart(address string) error {
addr, err := net.ResolveTCPAddr("tcp", address)
if err != nil {
globalLogger.Errorf(err.Error())
return err
}
conn, err := net.DialTCP("tcp", nil, addr)
if err != nil {
globalLogger.Errorf(err.Error())
return err
}
// 判断是否设置读超时
if c.readDeadline == 0 {
c.readDeadline = DefaultReadDeadline
}
// 赋值给当前连接对象
c.conn = &Conn{
Conn: conn,
connType: TCPSocketType,
packet: c.packet,
}
// 通知建立连接
err = c.socket.OnConnect(c.conn)
if err != nil {
globalLogger.Errorf(err.Error())
// 如果建立连接函数返回false,则关闭连接
c.socket.OnClose(c.conn, err) // 通知关闭
err = conn.Close() // 关闭连接
if err != nil {
globalLogger.Errorf(err.Error())
}
return err
}
// 开启一个协程处理数据接收
go c.handleConn(c.conn)
return nil
}

// GetConn 获取连接对象
func (c *TCPClient) GetConn() *Conn {
return c.conn
}
81 changes: 81 additions & 0 deletions conn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* @Author: 时光弧线
* @Date: 2017-12-30 11:55:02
* @Last Modified by: 时光弧线
* @Last Modified time: 2017-12-30 13:09:56
*/
package tcplibrary

import (
"fmt"
"net"
)

/* tcp 连接定义 */

// TCPType tcp连接类型
type TCPType = int

const (
// TCPSocketType tcp连接
TCPSocketType = iota
// WebSocketType WebSocket连接
WebSocketType
)

// Conn 自定义连接对象结构体,可以存储tcp或webSocket连接对象
type Conn struct {
net.Conn
connType TCPType // 连接对象类型
clientID string // 客户端id
packet Packet // 封闭解包对象
}

// SendMessage 发送消息,参数为自己报文结构体
func (c *Conn) SendMessage(v interface{}) (int, error) {
// 判断是tcp还是websocket
if c.connType == TCPSocketType { // 二进制协议
// 先封包,再发送数据
data, err := c.packet.Marshal(v)
if err != nil {
globalLogger.Errorf(err.Error())
return 0, err
}
return c.Write(data)
} else if c.connType == WebSocketType { // json方式
data, err := c.packet.MarshalToJSON(v)
if err != nil {
globalLogger.Errorf(err.Error())
return 0, err
}
c.Write(data)
} else {
globalLogger.Errorf("不支持的连接方式")
}
return 0, nil
}

// GetClientID 获取当前连接id
func (c *Conn) GetClientID() string {
return c.clientID
}

// GetConnType 获取连接类型
func (c *Conn) GetConnType() TCPType {
return c.connType
}

// CloseForClientID 根据clientID关闭连接
func CloseForClientID(clientID string) error {
// log.Println(clientID)
connInterface, ok := clients.Load(clientID)
if ok == false {
return fmt.Errorf("踢人失败,没有这样的连接1:%s", clientID)
}
if conn, ok := connInterface.(*Conn); ok == true {
conn.Close()
} else {
return fmt.Errorf("踢人失败,没有这样的连接2:%s", clientID)
}
return nil
}
108 changes: 108 additions & 0 deletions convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* @Author: 时光弧线
* @Date: 2017-12-30 11:55:06
* @Last Modified by: 时光弧线
* @Last Modified time: 2017-12-30 11:55:06
*/
package tcplibrary

import (
"encoding/binary"
"math"
)

func ByteToBool(i byte) bool {
if i == 1 {
return true
}
return false
}

func BytesToUint16(data []byte) uint16 {
return binary.LittleEndian.Uint16(data)
}

func BytesToUint32(data []byte) uint32 {
return binary.LittleEndian.Uint32(data)
}

func BytesToUint64(data []byte) uint64 {
return binary.LittleEndian.Uint64(data)
}

func BytesToInt16(data []byte) int16 {
return int16(BytesToUint16(data))
}

func BytesToInt32(data []byte) int32 {
return int32(BytesToUint16(data))
}

func BytesToInt64(data []byte) int64 {
return int64(BytesToUint64(data))
}

func BytesToInt(data []byte) int {
switch len(data) {
case 2:
return int(BytesToUint16(data))
case 4:
return int(BytesToUint32(data))
case 8:
return int(BytesToUint64(data))
}
return 0
}

//IntToBytes 整形转换成byte数组
func IntToBytes(data interface{}) []byte {
var buf []byte
switch data.(type) {
case int:
buf = make([]byte, 8)
target, _ := data.(int)
binary.LittleEndian.PutUint64(buf, uint64(target))
case int16:
buf = make([]byte, 2)
target, _ := data.(int16)
binary.LittleEndian.PutUint16(buf, uint16(target))
case int32:
buf = make([]byte, 4)
target, _ := data.(int32)
binary.LittleEndian.PutUint32(buf, uint32(target))
case int64:
buf = make([]byte, 8)
target, _ := data.(int64)
binary.LittleEndian.PutUint64(buf, uint64(target))
case uint:
buf = make([]byte, 8)
target, _ := data.(uint)
binary.LittleEndian.PutUint64(buf, uint64(target))
case uint16:
buf = make([]byte, 2)
target, _ := data.(uint16)
binary.LittleEndian.PutUint16(buf, target)
case uint32:
buf = make([]byte, 4)
target, _ := data.(uint32)
binary.LittleEndian.PutUint32(buf, target)
case uint64:
buf = make([]byte, 8)
target, _ := data.(uint64)
binary.LittleEndian.PutUint64(buf, target)
}
return buf
}

func Float64frombytes(bytes []byte) float64 {
bits := binary.LittleEndian.Uint64(bytes)
float := math.Float64frombits(bits)
return float
}

func Float32bytes(float float32) []byte {
bits := math.Float32bits(float)
bytes := make([]byte, 4)
binary.LittleEndian.PutUint32(bytes, bits)
return bytes
}
100 changes: 100 additions & 0 deletions default_packet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* @Author: 时光弧线
* @Date: 2017-12-30 11:55:15
* @Last Modified by: 时光弧线
* @Last Modified time: 2017-12-30 14:02:48
*/
package tcplibrary

import (
"bytes"
"encoding/json"
"errors"
"fmt"
)

// DefaultPacket 协议包
type DefaultPacket struct {
Length int32 `json:"Length"` // Payload 包长度,4字节
Payload interface{} `json:"Payload"` // 报文内容,n字节
}

// GetPayload 获取包内容
// 之所以有此函数,是为了兼容websocket,websocket使用此库时可直接使用json传输数据
func (dp *DefaultPacket) GetPayload() []byte {
switch dp.Payload.(type) {
case string:
return []byte(dp.Payload.(string))
case []byte:
return dp.Payload.([]byte)
default:
js, err := json.Marshal(dp.Payload)
if err == nil {
return js
}
globalLogger.Errorf("默认包结构获取错误:%v", err)
}
return make([]byte, 0)
}

// Unmarshal 默认解包
func (dp *DefaultPacket) Unmarshal(data []byte, c chan interface{}) (outData []byte, err error) {
// 捕获异常
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%T", r)
globalLogger.Fatalf("默认解包错误:%v", err)
}
}()
// 长度不足4个字节无法获取包长度
if len(data) < 4 {
return data, err
}
// 获取包长度
packetLength := BytesToInt32(data[0:4]) + 4
// 判断是否达到一个包长,没有达到直接返回
if len(data) < int(packetLength) {
return data, err
}
// 截取一个包的长度,解包
packetData := data[:packetLength]
// 解析内容和长度
packet := new(DefaultPacket)
packet.Length = int32(packetLength)
packet.Payload = packetData[4:]
// 写入管道数据,用于通知实际业务逻辑
c <- packet
// 递归调用解包
return dp.Unmarshal(data[packetLength:], c)
}

// Marshal 默认封包
func (dp *DefaultPacket) Marshal(v interface{}) ([]byte, error) {
packet, ok := v.(*DefaultPacket)
if ok == false {
return nil, errors.New("封包参数不是*DefaultPacket")
}
// 获取内容
payload := packet.GetPayload()
packet.Length = int32(len(payload))

/* 创建Buffer对象,写入头和数据 */
packetData := bytes.NewBuffer([]byte{})
lengthByte := IntToBytes(packet.Length) // 长度转byte
packetData.Write(lengthByte)
packetData.Write(payload)

// 返回编码后的字节数组
return packetData.Bytes(), nil
}

// MarshalToJSON 编码到json, 同时将Payload转为字符串
func (dp *DefaultPacket) MarshalToJSON(v interface{}) ([]byte, error) {
packet, ok := v.(*DefaultPacket)
if ok == false {
return nil, errors.New("封包参数不是*DefaultPacket")
}
packet.Payload = string(packet.GetPayload())
// 直接转json返回
return json.Marshal(packet)
}
Loading

0 comments on commit 141d567

Please sign in to comment.