我是 Go 语言的新手,正在尝试了解 channel 。为了理清我的理解,我观看了视频教程,阅读了一些书籍,但在使用 Go 编写的 Web 应用程序中进行实际编码和 channel 使用时,我仍然感到困惑。
我想要做的是拥有 2 个 URL:
以下是迄今为止我尝试过的测试代码,它仍然是我试图制定解决方案的困惑代码的精简版本:
//file main.go
package main
import (
"io"
"net/http"
"fmt"
"math/rand"
"log"
"time"
"github.com/gorilla/websocket"
)
func logging(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL.Path)
if r.URL.Path == `/ws` {
log.Println("WebSocket is accessed from ws://localhost:8080/ws")
}
f(w, r)
}
}
type hotcat int
func (c hotcat) ServeHTTP(res http.ResponseWriter, req *http.Request) {
io.WriteString(res, "cat cat cat")
//Some code here who's output I want to pass to websockets url ws://localhost:8080/ws
n := timeConsumingWork(4)
fmt.Println("Random Number Print from cat: ", n)
//Example the value of n I need to pass to ws://localhost:8080/ws, how can I do it?
// Some other example test code just giving some random output from hotcat http handler
// Would like to pass it's output to ws://localhost:8080/ws to print in websocckets output in browser
go func(){
out := make(chan string)
go func(){
for i := 0; ; i++ {
out <- `foo said something`
time.Sleep(time.Duration(rand.Intn(2e3)) * time.Millisecond)
}
//out <- `foo said something`
}()
printer(out)
}()
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// Execute this in browser console to initiate websococket connection and to send ws.send() commands etc.
/*
var ws = new WebSocket("ws://localhost:8080/ws")
ws.addEventListener("message", function(e) {console.log(e);});
ws.onmessage = function (event) {
console.log(event.data);
}
ws.send("foo")
ws.send(JSON.stringify({username: "Sat"}))
ws.readyState
ws.CLOSED
ws.OPEN
ws.close()
*/
func ws(w http.ResponseWriter, r *http.Request) {
socket, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println(err)
return
}
for {
msgType, msg, err := socket.ReadMessage()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(msg))
if err = socket.WriteMessage(msgType, msg); err != nil {
fmt.Println(err)
}
}
}
func main() {
var c hotcat
http.Handle("/cat", c)
http.HandleFunc("/ws", logging(ws))
http.ListenAndServe(":8080", nil)
}
func timeConsumingWork(n int) int {
time.Sleep(time.Microsecond * time.Duration(rand.Intn(500)))
return n + rand.Intn(1000)
}
func printer(in <-chan string) {
//log.Println(<-in)
go func() {
for {
log.Println(<-in)
}
}()
}
# command shell output
$ go run main.go
Random Number Print from cat: 891
2019/03/11 14:15:32 foo said something
2019/03/11 14:15:33 /ws
2019/03/11 14:15:33 WebSocket is accessed from ws://localhost:8080/ws
foo
2019/03/11 14:15:34 foo said something
2019/03/11 14:15:34 foo said something
2019/03/11 14:15:34 foo said something
2019/03/11 14:15:36 foo said something
2019/03/11 14:15:36 foo said something
^Csignal: interrupt
$
我想显示随机输出字符串“2019/03/11 14:15:34 foo said something”,就像浏览器中 websocket 输出中的输出一样。
我真的很感激一些指导或帮助。
我认为这个问题的代码、终端输出和浏览器截图中的注释应该清楚我正在尝试做什么,但如果这个问题仍然不清楚,请告诉我,我会尝试扩展它更多。
感谢和问候,
桑德
更新 1:
我阅读并尝试了聊天应用程序的 Mat Ryer 示例:https://github.com/matryer/goblueprints/tree/master/chapter1/chat
这是可能的代码副本:https://github.com/satindergrewal/golang-practice/tree/master/chat-examples/mychat02
从示例中我了解到,如果我有一个 Web 套接字句柄,我可以将消息从该 WebSocket http 句柄传送到其他连接的 Web 客户端。但我仍然很困惑如何将消息从非 websocket http 句柄发送到 websocket 句柄路由/地址。
我知道我不能只将此代码用于 / 的 ServeHTTP:
// ServeHTTP handles the HTTP request.
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t.once.Do(func() {
t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
})
fmt.Println(r.Host)
t.templ.Execute(w, r)
var room *room
socket, _ := upgrader.Upgrade(w, r, nil)
client := &client{
socket: socket,
send: make(chan []byte, messageBufferSize),
room: room,
}
room.join <- client
go func() {
for i := 0; i < 10; i++ {
time.Sleep(time.Duration(rand.Intn(8e3)) * time.Millisecond)
client.socket.WriteMessage(websocket.TextMessage, []byte("Hello from / ServeHTTP Handle"))
//fmt.Println("Sending automatic hello from root ServeHTTP handle to web page!")
}
}()
}
它已经给我以下错误:
localhost:8080
2019/03/25 11:19:18 http: superfluous response.WriteHeader call from github.com/gorilla/websocket.(*Upgrader).returnError (server.go:81)
2019/03/25 11:19:18 http: panic serving [::1]:52691: runtime error: invalid memory address or nil pointer dereference
goroutine 39 [running]:
net/http.(*conn).serve.func1(0xc00013e140)
/usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:1769 +0x139
panic(0x13a3e00, 0x172dc20)
/usr/local/Cellar/go/1.12.1/libexec/src/runtime/panic.go:522 +0x1b5
main.(*templateHandler).ServeHTTP(0xc00008ef60, 0x14954a0, 0xc0001dc380, 0xc000214200)
/Users/satinder/go/src/golang-practice/chat-examples/mychat02/main.go:40 +0x209
net/http.(*ServeMux).ServeHTTP(0x173cfa0, 0x14954a0, 0xc0001dc380, 0xc000214200)
/usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:2375 +0x1d6
net/http.serverHandler.ServeHTTP(0xc000130000, 0x14954a0, 0xc0001dc380, 0xc000214200)
/usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:2774 +0xa8
net/http.(*conn).serve(0xc00013e140, 0x1495ba0, 0xc0000a0380)
/usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:1878 +0x851
created by net/http.(*Server).Serve
/usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:2884 +0x2f4
更新 2: 在重新阅读评论的第一个回复后,尝试了不同的方法。
// ServeHTTP handles the HTTP request.
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t.once.Do(func() {
t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
})
fmt.Println(r.Host)
t.templ.Execute(w, r)
room := newRoom()
go func() {
for i := 0; i < 10; i++ {
time.Sleep(time.Duration(rand.Intn(8e3)) * time.Millisecond)
room.forward <- []byte("Hello from / ServeHTTP Handle")
//client.socket.WriteMessage(websocket.TextMessage, []byte("Hello from / ServeHTTP Handle"))
fmt.Println("Sending automatic hello from root ServeHTTP handle to web page!")
}
}()
}
现在它没有给出错误,但我没有看到控制台显示添加的第二个客户端,我期望通过命令行添加 /。
GoldenBook:mychat02 satinder$ go build -o chat
GoldenBook:mychat02 satinder$ ./chat
2019/03/25 11:37:49 Starting web server on :8080
localhost:8080
New client joined
^C
再次尝试使用以前和新的代码组合,结果如下:
// ServeHTTP handles the HTTP request.
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t.once.Do(func() {
t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
})
fmt.Println(r.Host)
t.templ.Execute(w, r)
room := newRoom()
socket, _ := upgrader.Upgrade(w, r, nil)
client := &client{
socket: socket,
send: make(chan []byte, messageBufferSize),
room: room,
}
room.join <- client
go func() {
for i := 0; i < 10; i++ {
time.Sleep(time.Duration(rand.Intn(8e3)) * time.Millisecond)
room.forward <- []byte("Hello from / ServeHTTP Handle")
//client.socket.WriteMessage(websocket.TextMessage, []byte("Hello from / ServeHTTP Handle"))
//fmt.Println("Sending automatic hello from root ServeHTTP handle to web page!")
}
}()
}
GoldenBook:mychat02 satinder$ go build -o chat
GoldenBook:mychat02 satinder$ ./chat
2019/03/25 11:40:44 Starting web server on :8080
localhost:8080
2019/03/25 11:40:50 http: superfluous response.WriteHeader call from github.com/gorilla/websocket.(*Upgrader).returnError (server.go:81)
^C
还是一头雾水……
有人可以给我一个解决方案吗?非常感谢您的帮助。
提前致谢。
桑德
最佳答案
我想我已经解决了。至少它正在做我想象中的代码来做我想做的事情,即使它可能不是正确的做事方式。正确的做事方式或做这件事的有效方式,我想你们中的许多人可能会纠正我并帮助我这样做,我希望如此,所以如果您认为它是错误的或低效的,请发表评论并纠正我。
我是这样解决的:
我查看了 gorilla 的 websocket 示例的 echo 示例 ( https://github.com/gorilla/websocket/blob/master/examples/echo/client.go ),并从 clients.go 文件中获取了一些基本代码,它是 make作为客户端连接到 websocket。
我的最终目标是将事件更新从另一个 http 句柄发送到 websocket,因此我在控制台输出中模拟了一些字符串的打印日志示例。
这是我在 Mat Ryer 聊天示例代码的 main.go 文件中所做的更改:
// ServeHTTP handles the HTTP request.
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t.once.Do(func() {
t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
})
fmt.Println(r.Host)
t.templ.Execute(w, r)
// Creating the URL scheme to use with websocket Dialer
// to connnect to ws://localhost:8080/room
u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/room"}
log.Printf("connecting to %s", u.String())
// Initiate the websocket connection from the go code **as a client** to connect to the chat room
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
go func() {
for i := 0; i < 10; i++ {
time.Sleep(time.Duration(rand.Intn(8e3)) * time.Millisecond)
// Just printing the log of the same message in command line. Might better to ignore it.
// log.Println("Sending automatic hello from root ServeHTTP handle to web page!")
// Write the Message as Text message to the web socket connection
// which will show up in the chat box
err := c.WriteMessage(websocket.TextMessage, []byte("Sending automatic hello from root ServeHTTP handle to web page!"))
if err != nil {
log.Println("write:", err)
return
}
}
}()
}
构建二进制文件后运行此代码会在控制台中打印以下示例输出:
GoldenBook:mychat02 satinder$ ./chat
2019/03/31 03:44:27 Starting web server on :8080
localhost:8080
2019/03/31 03:44:31 connecting to ws://localhost:8080/room
New client joined
New client joined
2019/03/31 03:44:33 Sending automatic hello from root ServeHTTP handle to web page!
Message received: Sending automatic hello from root ServeHTTP handle to web page!
-- sent to client
-- sent to client
2019/03/31 03:44:36 Sending automatic hello from root ServeHTTP handle to web page!
Message received: Sending automatic hello from root ServeHTTP handle to web page!
-- sent to client
-- sent to client
2019/03/31 03:44:43 Sending automatic hello from root ServeHTTP handle to web page!
Message received: Sending automatic hello from root ServeHTTP handle to web page!
-- sent to client
-- sent to client
2019/03/31 03:44:45 Sending automatic hello from root ServeHTTP handle to web page!
Message received: Sending automatic hello from root ServeHTTP handle to web page!
-- sent to client
-- sent to client
2019/03/31 03:44:45 Sending automatic hello from root ServeHTTP handle to web page!
Message received: Sending automatic hello from root ServeHTTP handle to web page!
-- sent to client
-- sent to client
Message received: HELLO
-- sent to client
-- sent to client
2019/03/31 03:44:48 Sending automatic hello from root ServeHTTP handle to web page!
Message received: Sending automatic hello from root ServeHTTP handle to web page!
-- sent to client
-- sent to client
2019/03/31 03:44:48 Sending automatic hello from root ServeHTTP handle to web page!
Message received: Sending automatic hello from root ServeHTTP handle to web page!
-- sent to client
-- sent to client
2019/03/31 03:44:49 Sending automatic hello from root ServeHTTP handle to web page!
Message received: Sending automatic hello from root ServeHTTP handle to web page!
-- sent to client
-- sent to client
2019/03/31 03:44:52 Sending automatic hello from root ServeHTTP handle to web page!
Message received: Sending automatic hello from root ServeHTTP handle to web page!
-- sent to client
-- sent to client
^C
以下是在 http://localhost:8080/ 的网页中显示的输出
这是完整的代码更新: https://github.com/satindergrewal/golang-practice/tree/master/chat-examples/mychat02
因为这是我想为我的实际应用程序解决的示例代码,它是一个基于 WebTTY 的应用程序,我将能够使用此代码将 WebTTY session 结束事件更新从其他一些 http 句柄发送到 websocket位。
我仍然可以使用在线围棋大师的一些帮助。
我需要在某个地方添加靠近此拨号器的 channel 吗?我想我知道,但如果有人可以修复其中的任何错误或预期的低效率,我将不胜感激。
非常感谢 @cerise-limón 到目前为止对我的帮助。 :-)
关于go - 如何将一些事件更新从 http 处理程序发送到 WebSocket 处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55094133/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/