Skip to content

Commit b440449

Browse files
committed
Initial commit (upload)
0 parents  commit b440449

File tree

7 files changed

+275
-0
lines changed

7 files changed

+275
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
vendor
2+
.idea

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 GreenWix
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
## packetize
2+
3+
A simple library for creating packetized TCP server using Go.
4+
5+
## Installation
6+
7+
```
8+
go get github.com/GreenWix/packetize
9+
```
10+
11+
## Packet Format
12+
13+
Packet is a byte array with maximum length of 65,535 bytes.
14+
Packets are sent in this format:
15+
16+
```<packet length : uint16> <packet data : bytes>```
17+
18+
## Example
19+
20+
```go
21+
package main
22+
23+
import (
24+
"fmt"
25+
pkize "github.com/GreenWix/packetize"
26+
"net"
27+
)
28+
29+
func main() {
30+
listener, _ := pkize.Listen("tcp4", "localhost:1337") // open listening socket
31+
for {
32+
conn, err := listener.Accept() // accept TCP connection from client and create a new socket
33+
if err != nil {
34+
continue
35+
}
36+
go handleClient(conn) // handle client connection in a separate goroutine
37+
}
38+
}
39+
40+
func handleClient(conn net.Conn) {
41+
defer conn.Close() // close socket after done
42+
43+
buf := make([]byte, 32) // buffer for reading data from client
44+
for {
45+
conn.Write([]byte("Hello world!")) // write to the client
46+
47+
readLen, err := conn.Read(buf) // reading from the socket
48+
if err != nil {
49+
fmt.Println("error occured during client handling: ", err)
50+
break
51+
}
52+
53+
fmt.Println(string(buf[:readLen]))
54+
}
55+
}
56+
```

conn.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package packetize
2+
3+
import (
4+
"context"
5+
"encoding/binary"
6+
"errors"
7+
bufpool "github.com/valyala/bytebufferpool"
8+
"io"
9+
"net"
10+
"time"
11+
)
12+
13+
type Conn struct {
14+
conn net.Conn
15+
readDeadline <-chan time.Time
16+
packetChan chan []byte
17+
18+
closeCtx context.Context
19+
close context.CancelFunc
20+
}
21+
22+
func (conn *Conn) Read(b []byte) (n int, err error) {
23+
select {
24+
case packet := <-conn.packetChan:
25+
if len(b) < len(packet) {
26+
err = errors.New("message sent on a socket was larger than the buffer used to receive the message into")
27+
}
28+
return copy(b, packet), err
29+
case <-conn.closeCtx.Done():
30+
return 0, errors.New("error reading from conn: connection closed")
31+
case <-conn.readDeadline:
32+
return 0, errors.New("error reading from conn: read timeout")
33+
}
34+
}
35+
36+
func (conn *Conn) Write(b []byte) (n int, err error) {
37+
if conn.closeCtx.Err() != nil {
38+
return 0, errors.New("error writing to conn: connection closed")
39+
}
40+
41+
pk := bufpool.Get()
42+
defer bufpool.Put(pk)
43+
44+
err = binary.Write(pk, binary.BigEndian, uint16(len(b)))
45+
if err != nil {
46+
return
47+
}
48+
_, err = pk.Write(b)
49+
if err != nil {
50+
return
51+
}
52+
53+
_, err = conn.conn.Write(pk.B)
54+
return
55+
}
56+
57+
func (conn *Conn) Close() error {
58+
if conn.closeCtx.Err() != nil {
59+
return errors.New("conn is already closed")
60+
}
61+
62+
conn.close()
63+
return conn.conn.Close()
64+
}
65+
66+
func (conn *Conn) LocalAddr() net.Addr {
67+
return conn.conn.LocalAddr()
68+
}
69+
70+
func (conn *Conn) RemoteAddr() net.Addr {
71+
return conn.conn.RemoteAddr()
72+
}
73+
74+
func (conn *Conn) SetDeadline(t time.Time) error {
75+
return conn.SetReadDeadline(t)
76+
}
77+
78+
func (conn *Conn) SetReadDeadline(t time.Time) error {
79+
if t.IsZero() {
80+
conn.readDeadline = make(chan time.Time)
81+
return nil
82+
}
83+
if t.Before(time.Now()) {
84+
return errors.New("read deadline cannot be before now")
85+
}
86+
conn.readDeadline = time.After(t.Sub(time.Now()))
87+
return nil
88+
}
89+
90+
func (conn *Conn) SetWriteDeadline(time.Time) error {
91+
return nil
92+
}
93+
94+
func (conn *Conn) process() {
95+
lenBuf := make([]byte, 2)
96+
97+
for conn.closeCtx.Err() == nil {
98+
length, err := io.ReadFull(conn.conn, lenBuf)
99+
if err != nil || length != 2 {
100+
_ = conn.Close()
101+
break
102+
}
103+
104+
pkLen := int(binary.BigEndian.Uint16(lenBuf))
105+
106+
pk := make([]byte, pkLen)
107+
108+
length, err = io.ReadFull(conn.conn, pk)
109+
if err != nil || length != pkLen {
110+
_ = conn.Close()
111+
break
112+
}
113+
114+
conn.packetChan <- pk
115+
}
116+
}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/GreenWix/packetize
2+
3+
go 1.14
4+
5+
require github.com/valyala/bytebufferpool v1.0.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
2+
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=

listener.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package packetize
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net"
7+
"time"
8+
)
9+
10+
type Listener struct {
11+
listener net.Listener
12+
connChan chan *Conn
13+
14+
closeCtx context.Context
15+
close context.CancelFunc
16+
}
17+
18+
func Listen(network string, addr string) (*Listener, error) {
19+
listener, err := net.Listen(network, addr)
20+
if err != nil {
21+
return nil, err
22+
}
23+
24+
closeCtx, closeFunc := context.WithCancel(context.Background())
25+
lst := &Listener{
26+
listener: listener,
27+
connChan: make(chan *Conn),
28+
closeCtx: closeCtx,
29+
close: closeFunc,
30+
}
31+
32+
go func() {
33+
for lst.closeCtx.Err() == nil {
34+
conn, err := lst.listener.Accept()
35+
if err != nil {
36+
continue
37+
}
38+
39+
cloneCtx, closeFunc := context.WithCancel(context.Background())
40+
pkConn := &Conn{
41+
conn: conn,
42+
readDeadline: make(chan time.Time),
43+
packetChan: make(chan []byte, 32),
44+
45+
closeCtx: cloneCtx,
46+
close: closeFunc,
47+
}
48+
49+
go pkConn.process()
50+
51+
lst.connChan <- pkConn
52+
}
53+
}()
54+
return lst, nil
55+
}
56+
57+
func (lst *Listener) Accept() (net.Conn, error) {
58+
select {
59+
case conn := <-lst.connChan:
60+
return conn, nil
61+
case <-lst.closeCtx.Done():
62+
return nil, errors.New("accept: listener closed")
63+
}
64+
}
65+
66+
func (lst *Listener) Close() error {
67+
lst.close()
68+
return lst.listener.Close()
69+
}
70+
71+
func (lst *Listener) Addr() net.Addr {
72+
return lst.listener.Addr()
73+
}

0 commit comments

Comments
 (0)