-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9174344
commit 141d567
Showing
13 changed files
with
942 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.