现在我是这样子做的,A和和服务端S建立一个和UDP连接,然后B和服务器S也建立一个长连接,B通过rpc向S发消息,然后S发给A。这样子就达到了B向A发消息了。反之A给B发消息也是这样子。因为发出去的消息要经过S处理的,比如敏感词之类的。
首先是服务端的代码:
package mainimport ("fmt""log""net""net/http""net/rpc""os")type TCPPush struct {}type PushRequest struct {A string}type PushResponse struct {Data string}// 建立连接池,用于广播消息var conns = make(map[string]net.Conn)func main() {port := "9090"go Start(port)// 以下是创建rpc服务器rpc.Register(new(TCPPush)) // 注册rpc服务rpc.HandleHTTP() // 采用http协议作为rpc载体lis, err := net.Listen("tcp", "127.0.0.1:8095")if err != nil {log.Fatalln("fatal error: ", err)}fmt.Fprintf(os.Stdout, "%s", "start rpc connection!\n")http.Serve(lis, nil)}func (*TCPPush) PushData(req PushRequest, res *PushResponse) error {doPush(req)res.Data = "success!"return nil}func doPush(req PushRequest) {// 发送数据for _, conn := range conns {sendData := []byte(req.A)fmt.Println("remoteAddr:", conn, "sendData:", string(sendData))_, err := conn.Write(sendData)if err != nil {log.Printf("broad message to %s failed.\n", conn)}}}// 启动服务器func Start(port string) {host := ":" + port// 获取tcp地址tcpAddr, err := net.ResolveTCPAddr("tcp4", host)if err != nil {log.Printf("resolve tcp addr failed: %v\n", err)return}// 监听listener, err := net.ListenTCP("tcp", tcpAddr)if err != nil {log.Printf("listen tcp port failed: %v\n", err)return}// 消息通道messageChan := make(chan string, 10)// 广播消息go BroadMessages(&conns, messageChan)// 启动for {fmt.Printf("listening port %s ...\n", port)conn, err := listener.AcceptTCP()if err != nil {log.Printf("Accept failed:%v\n", err)continue}// 把每个客户端连接扔进连接池conns[conn.RemoteAddr().String()] = connfmt.Println(conns)// 处理消息go Handler(conn, &conns, messageChan)}}// 向所有连接上的乡亲们发广播func BroadMessages(conns *map[string]net.Conn, messages chan string) {for {// 不断从通道里读取消息msg := <-messagesfmt.Println(msg)// 向所有的乡亲们发消息for key, conn := range *conns {fmt.Println("connection is connected from ", key)_, err := conn.Write([]byte(msg))if err != nil {log.Printf("broad message to %s failed: %v\n", key, err)delete(*conns, key)}}}}// 处理客户端发到服务端的消息,将其扔到通道中func Handler(conn net.Conn, conns *map[string]net.Conn, messages chan string) {fmt.Println("connect from client ", conn.RemoteAddr().String())buf := make([]byte, 1024)for {length, err := conn.Read(buf)if err != nil {log.Printf("read client message failed:%v\n", err)delete(*conns, conn.RemoteAddr().String())conn.Close()break}// 把收到的消息写到通道中receiveStr := string(buf[0:length])messages <- receiveStr}}
然后是tcp客户端:
package mainimport ("net""log""fmt""os")func main() {StartClient(os.Args[1])}func StartClient(tcpAddrStr string) {tcpAddr, err := net.ResolveTCPAddr("tcp4", tcpAddrStr)if err != nil {log.Printf("Resolve tcp addr failed: %v\n", err)return}// 向服务器拨号conn, err := net.DialTCP("tcp", nil, tcpAddr)if err != nil {log.Printf("Dial to server failed: %v\n", err)return}// 向服务器发消息go SendMsg(conn)// 接收来自服务器端的广播消息buf := make([]byte, 1024)for {length, err := conn.Read(buf)if err != nil {log.Printf("recv server msg failed: %v\n", err)conn.Close()os.Exit(0)break}fmt.Println(string(buf[0:length]))}}// 向服务器端发消息func SendMsg(conn net.Conn) {username := conn.LocalAddr().String()for {var input string// 接收输入消息,放到input变量中fmt.Scanln(&input)if input == "/q" || input == "/quit" {fmt.Println("ByeBye ...")conn.Close()os.Exit(0)}// 只处理有内容的消息if len(input) > 0 {msg := username + " say:" + input_, err := conn.Write([]byte(msg))if err != nil {conn.Close()break}}}}
rpc客户端:
package mainimport ("fmt""log""net/rpc")// 算数运算请求结构体type PushRequestClient struct {A string}type PushResponseClient struct {Data string}func main() {conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8095")if err != nil {log.Fatalln("dialing error: ", err)}req := PushRequestClient{"this data is from rpc client....\n"}var res PushResponseClienterr = conn.Call("TCPPush.PushData", req, &res) // 推送消息,TCPPush和rpcServer里面的是一样的,并且PushData在rpcServer里面是可导出的if err != nil {log.Fatalln("push error: ", err)}fmt.Println(res.Data)}
首先运行服务器:
go run server.go
再跑tcp客户端:
go run tcp_client.go :9090
最后跑rpc客户端:
go run rpc_client.go
这样子,在rpc发出去的东西,在tcp客户端就可以看到了。
搜索
标签
study
ab
amap
apache
apahe
awk
aws
bat
centos
CFS
chrome
cmd
cnpm
composer
consul
crontab
css
curl
cygwin
devops
di
docker
docker,docker-compose
ethereum
excel
fiddler
fluentd
framework
front-end
git
gitgui
github
glide
go
golang
gorm
grafana
gzip
ioc
item2
iterm2
javascript
jenkins
jsonp
kafka
laradock
laravel
larval
linux
liunux
log
mac
mac, wi-fi
macos
magento
mariaDB
minikube
mongoDB
msp
mysql
netbeans
nginx
nodejs
nohup
npm
nsq
php
php-fpm
php7
phpstorm
php扩展
Protobuf
python
redis
scp
server
shell
soap
socket
socket5
sql
sre
ssdb
ssh
ssl
study
sublime
swift
system
td-agent
uml
v2ray
vagrant
vagrnat
vim
vpn
vue
vue.js
webpack
webrtc
websocket
webtatic
windows
windows7
word
wps
xdebug
yarn
yii2
yum
zookeeper
世界国家
互联网
以太坊
分类
前端
小程序
打印机
排序算法
搞笑
权限
粤语
缓存
网络
虚拟机
视频
设计模式
项目管理
热门文章
友情链接