├── .travis.yml ├── LICENSE ├── tcp_server_test.go ├── readme.md └── tcp_server.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.7 4 | script: 5 | - go test -v ./... 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 firstrow@gmail.com 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. 22 | -------------------------------------------------------------------------------- /tcp_server_test.go: -------------------------------------------------------------------------------- 1 | package tcp_server 2 | 3 | import ( 4 | . "github.com/smartystreets/goconvey/convey" 5 | "net" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func buildTestServer() *server { 11 | return New("localhost:9999") 12 | } 13 | 14 | func Test_accepting_new_client_callback(t *testing.T) { 15 | server := buildTestServer() 16 | 17 | var messageReceived bool 18 | var messageText string 19 | var newClient bool 20 | var connectinClosed bool 21 | 22 | server.OnNewClient(func(c *Client) { 23 | newClient = true 24 | }) 25 | server.OnNewMessage(func(c *Client, message string) { 26 | messageReceived = true 27 | messageText = message 28 | }) 29 | server.OnClientConnectionClosed(func(c *Client, err error) { 30 | connectinClosed = true 31 | }) 32 | go server.Listen() 33 | 34 | // Wait for server 35 | // If test fails - increase this value 36 | time.Sleep(10 * time.Millisecond) 37 | 38 | conn, err := net.Dial("tcp", "localhost:9999") 39 | if err != nil { 40 | t.Fatal("Failed to connect to test server") 41 | } 42 | conn.Write([]byte("Test message\n")) 43 | conn.Close() 44 | 45 | // Wait for server 46 | time.Sleep(10 * time.Millisecond) 47 | 48 | Convey("Messages should be equal", t, func() { 49 | So(messageText, ShouldEqual, "Test message\n") 50 | }) 51 | Convey("It should receive new client callback", t, func() { 52 | So(newClient, ShouldEqual, true) 53 | }) 54 | Convey("It should receive message callback", t, func() { 55 | So(messageReceived, ShouldEqual, true) 56 | }) 57 | Convey("It should receive connection closed callback", t, func() { 58 | So(connectinClosed, ShouldEqual, true) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/firstrow/tcp_server.svg?branch=master)](https://travis-ci.org/firstrow/tcp_server) 2 | 3 | # TCPServer 4 | Package tcp_server created to help build TCP servers faster. 5 | 6 | ### Install package 7 | 8 | ``` bash 9 | go get -u github.com/firstrow/tcp_server 10 | ``` 11 | 12 | ### Usage: 13 | 14 | NOTICE: `OnNewMessage` callback will receive new message only if it's ending with `\n` 15 | 16 | ``` go 17 | package main 18 | 19 | import "github.com/firstrow/tcp_server" 20 | 21 | func main() { 22 | server := tcp_server.New("localhost:9999") 23 | 24 | server.OnNewClient(func(c *tcp_server.Client) { 25 | // new client connected 26 | // lets send some message 27 | c.Send("Hello") 28 | }) 29 | server.OnNewMessage(func(c *tcp_server.Client, message string) { 30 | // new message received 31 | }) 32 | server.OnClientConnectionClosed(func(c *tcp_server.Client, err error) { 33 | // connection with client lost 34 | }) 35 | 36 | server.Listen() 37 | } 38 | ``` 39 | 40 | # Contributing 41 | 42 | To hack on this project: 43 | 44 | 1. Install as usual (`go get -u github.com/firstrow/tcp_server`) 45 | 2. Create your feature branch (`git checkout -b my-new-feature`) 46 | 3. Ensure everything works and the tests pass (`go test`) 47 | 4. Commit your changes (`git commit -am 'Add some feature'`) 48 | 49 | Contribute upstream: 50 | 51 | 1. Fork it on GitHub 52 | 2. Add your remote (`git remote add fork git@github.com:firstrow/tcp_server.git`) 53 | 3. Push to the branch (`git push fork my-new-feature`) 54 | 4. Create a new Pull Request on GitHub 55 | 56 | Notice: Always use the original import path by installing with `go get`. 57 | -------------------------------------------------------------------------------- /tcp_server.go: -------------------------------------------------------------------------------- 1 | package tcp_server 2 | 3 | import ( 4 | "bufio" 5 | "log" 6 | "net" 7 | ) 8 | 9 | // Client holds info about connection 10 | type Client struct { 11 | conn net.Conn 12 | Server *server 13 | } 14 | 15 | // TCP server 16 | type server struct { 17 | address string // Address to open connection: localhost:9999 18 | onNewClientCallback func(c *Client) 19 | onClientConnectionClosed func(c *Client, err error) 20 | onNewMessage func(c *Client, message string) 21 | } 22 | 23 | // Read client data from channel 24 | func (c *Client) listen() { 25 | c.Server.onNewClientCallback(c) 26 | reader := bufio.NewReader(c.conn) 27 | for { 28 | message, err := reader.ReadString('\n') 29 | if err != nil { 30 | c.conn.Close() 31 | c.Server.onClientConnectionClosed(c, err) 32 | return 33 | } 34 | c.Server.onNewMessage(c, message) 35 | } 36 | } 37 | 38 | // Send text message to client 39 | func (c *Client) Send(message string) error { 40 | _, err := c.conn.Write([]byte(message)) 41 | return err 42 | } 43 | 44 | // Send bytes to client 45 | func (c *Client) SendBytes(b []byte) error { 46 | _, err := c.conn.Write(b) 47 | return err 48 | } 49 | 50 | func (c *Client) Conn() net.Conn { 51 | return c.conn 52 | } 53 | 54 | func (c *Client) Close() error { 55 | return c.conn.Close() 56 | } 57 | 58 | // Called right after server starts listening new client 59 | func (s *server) OnNewClient(callback func(c *Client)) { 60 | s.onNewClientCallback = callback 61 | } 62 | 63 | // Called right after connection closed 64 | func (s *server) OnClientConnectionClosed(callback func(c *Client, err error)) { 65 | s.onClientConnectionClosed = callback 66 | } 67 | 68 | // Called when Client receives new message 69 | func (s *server) OnNewMessage(callback func(c *Client, message string)) { 70 | s.onNewMessage = callback 71 | } 72 | 73 | // Start network server 74 | func (s *server) Listen() { 75 | listener, err := net.Listen("tcp", s.address) 76 | if err != nil { 77 | log.Fatal("Error starting TCP server.") 78 | } 79 | defer listener.Close() 80 | 81 | for { 82 | conn, _ := listener.Accept() 83 | client := &Client{ 84 | conn: conn, 85 | Server: s, 86 | } 87 | go client.listen() 88 | } 89 | } 90 | 91 | // Creates new tcp server instance 92 | func New(address string) *server { 93 | log.Println("Creating server with address", address) 94 | server := &server{ 95 | address: address, 96 | } 97 | 98 | server.OnNewClient(func(c *Client) {}) 99 | server.OnNewMessage(func(c *Client, message string) {}) 100 | server.OnClientConnectionClosed(func(c *Client, err error) {}) 101 | 102 | return server 103 | } 104 | --------------------------------------------------------------------------------