Giter Site home page Giter Site logo

ws-examples's Introduction

ws examples

website

Example applications written in Go with github.com/gobwas/ws inside.

Applications

  • Chat
  • Chat CLI
  • Twitter hashtag watcher

Notes

Commands

Currently these commands are developed:

  • bin/chat the chat application, which is listening raw tcp socket and handles [jsonrpc]-like messages.
  • bin/proxy proxy that used for two purposes. First of all, to serve static files for chat ui. Second and technical one is to proxy /ws requests to running chat app. This is done only for running on heroku, where only one port is able to be exported.

Building

All commands can be built by make * or by just make.

The directory structure is convinient for gb vendoring tool. But instead of using gb git submodules are used to vendor dependencies. Thus, make vendor will update existing submodules.

Also, gb directory structure is here to signal the heroku buildpack to use appropriate build logic.

Chat application deployed here.

ws-examples's People

Contributors

antoniomo avatar binkoni avatar gobwas avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ws-examples's Issues

ASK: Request as string and writer

// Broadcast sends message to all alive users.
func (c *Chat) Broadcast(method string, params Object) error {
	var buf bytes.Buffer

	w := wsutil.NewWriter(&buf, ws.StateServerSide, ws.OpText)
	encoder := json.NewEncoder(w)

	r := Request{Method: method, Params: params}
	if err := encoder.Encode(r); err != nil {
		return err
	}
	if err := w.Flush(); err != nil {
		return err
	}

	c.out <- buf.Bytes()

	return nil
}

How I can achieve when Request is plain string so I dont need to JSON encoding. how can I write into buffer?

thank you in advance!

import gopool can not be found

The two import references to gopool (main.go and chat.go) could not be found. I had to update them to:
"github.com/gobwas/ws-examples/src/gopool"
To get the code to compile.

Cheers
F

Upgrade error on Mac

Hello,

I am trying the chat and proxy examples on Mac. When I run the script, it returns:

2019/07/24 17:23:34 upstream "chat" ok
2019/07/24 17:23:34 proxy is listening on ":"
2019/07/24 17:23:34 127.0.0.1:3333 > 127.0.0.1:56968: upgrade error: EOF

Chat starts fine. No errors there. Any ideas why I am seeing this error? The OS is x86_64-apple-darwin18 / darwin18, AKA MacOS Mojave v10.14.5. Has the ws library been tested on Mac and/or Windows? Am I doing something wrong?

should not use netpoll with gobwas

as I have said here:
gobwas/ws#121 (comment)

both gobwas's upgrade and wsutil/user.readRequest are blocking mode, if many connections send bytes one-by-one or send a half ws upgrade message or send only part of ws message, it will make all your gopool's task blocked, then your service not available.

try this client example:

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/url"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gorilla/websocket"
)

var (
	connectedNum int64 = 0

	tunnelMux = sync.Mutex{}
	tunnels   = map[string]time.Duration{}
)

type Object map[string]interface{}

type Request struct {
	ID     int    `json:"id"`
	Method string `json:"method"`
	Params Object `json:"params"`
}

func client(id int, addr string, needLog bool, needDelay bool) {
	u := url.URL{Scheme: "ws", Host: addr, Path: "/ws"}
	if needLog {
		log.Printf("connecting to %s", u.String())
	}

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Print("dial:", err)
		return
	}
	defer c.Close()
	if needLog {
		log.Printf("connected to %s, connectedNum: %v", u.String(), atomic.AddInt64(&connectedNum, 1))
	}

	tunnelMux.Lock()
	tunnels[c.RemoteAddr().String()] = time.Second
	tunnelMux.Unlock()
	defer func() {
		tunnelMux.Lock()
		delete(tunnels, c.RemoteAddr().String())
		tunnelMux.Unlock()
	}()

	go func() {
		for {
			_, message, err := c.ReadMessage()
			if err != nil {
				if needLog {
					log.Println("read:", err)
				}
				return
			}
			if needLog {
				log.Println("read :", string(message))
			}
		}
	}()

	for {
		data, _ := json.Marshal(&Request{
			ID:     id,
			Method: "publish",
			Params: map[string]interface{}{
				"timestamp": time.Now().Unix(),
			},
		})
		err := c.WriteMessage(websocket.TextMessage, data)
		if err != nil {
			log.Printf("write: %v", err)
			return
		}
		if needLog {
			log.Println("write:", string(data))
		}

		time.Sleep(time.Second)
	}
}

func main() {
	go runProxy("localhost:3000", "localhost:3333")
	time.Sleep(time.Second / 10)

	// larger than your chat server's pool size
	num := 128
	for i := 1; i <= num; i++ {
		go client(i, "localhost:3000", false, true)
	}

	client(num+1, "localhost:3333", true, false)
}

func tunnel(clientConn *net.TCPConn, serverAddr string) {
	serverConn, dailErr := net.Dial("tcp", serverAddr)
	log.Printf("+ tunnel: [%v -> %v]\n", clientConn.LocalAddr().String(), serverAddr)
	if dailErr == nil {
		c2sCor := func() {
			defer func() {
				recover()
			}()

			var buf = make([]byte, 4096)
			for i := 0; true; i++ {
				nread, err := clientConn.Read(buf)
				if err != nil {
					clientConn.Close()
					serverConn.Close()
					break
				}

				tmp := buf[:nread]

				tunnelMux.Lock()
				delay := tunnels[clientConn.LocalAddr().String()]
				tunnelMux.Unlock()
				if delay > 0 {
					// fmt.Println("delay:", delay)
					for j := 0; j < len(tmp); j++ {
						_, err := serverConn.Write([]byte{tmp[j]})
						if err != nil {
							clientConn.Close()
							serverConn.Close()
							return
						}
						time.Sleep(delay)
					}
				} else {
					_, err := serverConn.Write(tmp)
					if err != nil {
						clientConn.Close()
						serverConn.Close()
						return
					}
				}
			}
		}

		s2cCor := func() {
			defer func() {
				recover()
			}()

			var buf = make([]byte, 4096)

			for i := 0; true; i++ {
				nread, err := serverConn.Read(buf)
				if err != nil {
					clientConn.Close()
					serverConn.Close()
					break
				}

				tmp := buf[:nread]

				tunnelMux.Lock()
				delay := tunnels[clientConn.LocalAddr().String()]
				tunnelMux.Unlock()
				if delay > 0 {
					// fmt.Println("delay:", delay)
					for j := 0; j < len(tmp); j++ {
						_, err := clientConn.Write([]byte{tmp[j]})
						if err != nil {
							clientConn.Close()
							serverConn.Close()
							return
						}
						time.Sleep(delay)
					}
				} else {
					_, err := clientConn.Write(tmp)
					if err != nil {
						clientConn.Close()
						serverConn.Close()
						return
					}
				}
			}
		}

		go c2sCor()
		go s2cCor()
	} else {
		clientConn.Close()
	}
}

func runProxy(proxyAddr string, serverAddr string) {
	tcpAddr, err := net.ResolveTCPAddr("tcp4", proxyAddr)
	if err != nil {
		fmt.Println("ResolveTCPAddr Error: ", err)
		return
	}

	listener, err2 := net.ListenTCP("tcp", tcpAddr)
	if err2 != nil {
		fmt.Println("ListenTCP Error: ", err2)
		return
	}

	defer listener.Close()

	fmt.Println(fmt.Sprintf("proxy running on: [%s -> %s]", proxyAddr, serverAddr))
	for {
		conn, err := listener.AcceptTCP()

		if err != nil {
			fmt.Println("AcceptTCP Error: ", err2)
		} else {
			go tunnel(conn, serverAddr)
		}
	}
}

how to send ping/pong hearbeat

I send

conn.send(JSON.stringify({
  id:     1,
  method: "ping",
  params: "ping",
}));

and the header.OpCode is ws.OpText(0x1)

how can I make ping/pong heartbeat then?

Find task in pool question

Hi , I read this part fo code
func (p *Pool) worker(task func()) {
defer func() { <-p.sem }()

**task()**

**for task := range p.work {
	**task()**
}**

}
question :

  1. why not just use range p.work do task() ?
  2. Do task() before that("range p.work") is essential?
    Thank you

handshake error: bad "Connection" header

Thank you for publishing this example. I get this error after running bin/chat on Mac OS X terminal, after successfully running Make.

2020/01/21 19:44:11 [::1]:3333 > [::1]:58202: upgrade error: handshake error: bad "Connection" header

Why do we use netpoll event descriptor instead of just a reader goroutine?

I'm trying to understand the example, specifically why do we need to create a netpoll even descriptor for the purpose of reading messages from the connection?

Comparing to most other websocket library examples, I usually see just a dedicated goroutine for reading messages, and usually another separate goroutine for writing messages. Why the seemingly unnecessary complication here?

Would creating a separate reader goroutine for every connection achieve the same thing?

// Create netpoll event descriptor for conn.
// We want to handle only read events of it.
desc := netpoll.Must(netpoll.HandleRead(conn))

Why we need mutex lock and unlock when reading conn

Hi @gobwas, thank you for creating awesome example !
I have a question about your sample code.

https://github.com/gobwas/ws-examples/blob/master/src/chat/chat.go#L79-L80

func (u *User) readRequest() (*Request, error) {
	u.io.Lock()
	defer u.io.Unlock()
	...
}

Why we need lock when read ? if I put the Receive (contain readRequest) method in for-loop. (I am not using poller.Start(desc, func(ev netpoll.Event))

func (s *Socket) Start(conn net.Conn, name string) {
	// Register incoming user in chat.
	user := s.chat.Register(conn)

	go func() {
		retry := 0
		defer conn.Close()
		defer func() {
			if r := recover(); r != nil {
				log.Println(r)
			}
		}()

		for {
			err := user.Receive()
			if err != nil {
				// When receive failed, we can only disconnect broken
				// connection and stop to receive events about it.
				log.Printf("Read user [%s] - client [%s] text failed: %v\n", user.Name, name, err)
				goto disconnect
			}
			log.Println("User receive successful")
		disconnect:
			if retry = retry + 1; retry > 3 {
				log.Printf("Remove user [%s] - client [%s]\n", user.Name, name)
				s.chat.Remove(user)
				break
			}
			delay := time.Millisecond
			log.Printf("Start receive error: %v; retrying in %s", err, delay)
			time.Sleep(delay)
		}
	}()
}

===> its seem block write method such as

https://github.com/gobwas/ws-examples/blob/master/src/chat/chat.go#L135-L136

func (u *User) writeRaw(p []byte) error {
	u.io.Lock()
	defer u.io.Unlock()
	...
}

because readRequest have made u.io locked, and writeRaw can not aquire this lock.

If I remove u.io.Lock() in readRequest method, Have it any problem?

Thank you !

run error

go run main.go:

github.com/mailru/easygo/netpoll

..........\github.com\mailru\easygo\netpoll\handle.go:92: cannot use desc.fd() (type int) as type syscall.Handle in argument to sysc
all.SetNonblock

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.