首页 > 其他分享 >Go实现点对点聊天

Go实现点对点聊天

时间:2024-06-18 10:58:34浏览次数:17  
标签:rw log err 点对点 Println 聊天 go Go libp2p

这里测试最基本的,服务发现的那种需要魔法,我的虚拟机不行,示例去官网的example查看

p2p.go

go get 安装缺失的库

package main

import (
	"bufio"
	"context"
	"crypto/rand"
	"flag"
	"fmt"
	"io"
	"log"
	mrand "math/rand"
	"os"

	"github.com/libp2p/go-libp2p"
	"github.com/libp2p/go-libp2p/core/crypto"
	"github.com/libp2p/go-libp2p/core/host"
	"github.com/libp2p/go-libp2p/core/network"
	"github.com/libp2p/go-libp2p/core/peer"
	"github.com/libp2p/go-libp2p/core/peerstore"

	"github.com/multiformats/go-multiaddr"
)

func handleStream(s network.Stream) {
	log.Println("Got a new stream!")
	rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))

	go readData(rw)
	go writeData(rw)
}

func readData(rw *bufio.ReadWriter) {
	for {
		str, _ := rw.ReadString('\n')

		if str == "" {
			return
		}
		if str != "\n" {
			//彩色打印
			fmt.Printf("\x1b[32m%s\x1b[0m> ", str)
		}

	}
}

func writeData(rw *bufio.ReadWriter) {
	stdReader := bufio.NewReader(os.Stdin)

	for {
		fmt.Print("> ")
		sendData, err := stdReader.ReadString('\n')
		if err != nil {
			log.Println(err)
			return
		}

		rw.WriteString(fmt.Sprintf("%s\n", sendData))
		rw.Flush()
	}
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	sourcePort := flag.Int("sp", 0, "要开启的端口")
	dest := flag.String("d", "", "要连接的目的地")
	help := flag.Bool("help", false, "帮助")
	debug := flag.Bool("debug", false, "Debug")

	flag.Parse()

	if *help {
		fmt.Println("A服务器启动: ./chat -sp <SOURCE_PORT> <SOURCE_PORT>是端口数字")
		fmt.Println("B服务器启动: ./chat -d <MULTIADDR> <MULTIADDR>是A服务启动后返回的字符串")
		os.Exit(0)
	}

	var r io.Reader
	if *debug {
		//随机端口
		r = mrand.New(mrand.NewSource(int64(*sourcePort)))
	} else {
		r = rand.Reader
	}

	h, err := makeHost(*sourcePort, r)
	if err != nil {
		log.Println(err)
		return
	}

	if *dest == "" {
		startPeer(ctx, h, handleStream)
	} else {
		rw, err := startPeerAndConnect(ctx, h, *dest)
		if err != nil {
			log.Println(err)
			return
		}

		// 启动协程监听读写
		go writeData(rw)
		go readData(rw)

	}

	select {}
}

func makeHost(port int, randomness io.Reader) (host.Host, error) {
	// 证书
	prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, randomness)
	if err != nil {
		log.Println(err)
		return nil, err
	}

	// 0.0.0.0 监听在任何网卡
	sourceMultiAddr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port))

	return libp2p.New(
		libp2p.ListenAddrs(sourceMultiAddr),
		libp2p.Identity(prvKey),
	)
}

func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHandler) {
	// 设置一个函数处理流
	// 只在接收端生效
	h.SetStreamHandler("/chat/1.0.0", streamHandler)

	var port string
	for _, la := range h.Network().ListenAddresses() {
		if p, err := la.ValueForProtocol(multiaddr.P_TCP); err == nil {
			port = p
			break
		}
	}

	if port == "" {
		log.Println("端口不可位空")
		return
	}

	log.Printf("在另外一台机器上启动命令 './chat -d /ip4/127.0.0.1/tcp/%v/p2p/%s' \n", port, h.ID())
	log.Println("你可以替换 127.0.0.1 作为公网ip")
	log.Println("等待连接.....")
	log.Println()
}

func startPeerAndConnect(ctx context.Context, h host.Host, destination string) (*bufio.ReadWriter, error) {
	for _, la := range h.Addrs() {
		log.Printf(" - %v\n", la)
	}
	log.Println()

	maddr, err := multiaddr.NewMultiaddr(destination)
	if err != nil {
		log.Println(err)
		return nil, err
	}

	info, err := peer.AddrInfoFromP2pAddr(maddr)
	if err != nil {
		log.Println(err)
		return nil, err
	}

	h.Peerstore().AddAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL)

	s, err := h.NewStream(context.Background(), info.ID, "/chat/1.0.0")
	if err != nil {
		log.Println(err)
		return nil, err
	}
	log.Println("Established connection to destination")
	
	rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))

	return rw, nil
}

使用方式

A服务器启动: ./chat -sp <SOURCE_PORT> <SOURCE_PORT>是端口数字
B服务器启动: ./chat -d <MULTIADDR> <MULTIADDR>是A服务启动后返回的字符串


标签:rw,log,err,点对点,Println,聊天,go,Go,libp2p
From: https://www.cnblogs.com/qcy-blog/p/18253887

相关文章

  • golang中用chan实现迭代器
    实现代码如下:packagemainimport( "log" "time")/* 两种迭代器的实现*///采用index的方式实现(非线程安全)typeListStructstruct{ indexint data[]int}func(sl*ListStruct)Next()int{ d:=sl.data[sl.index] sl.index+=1 returnd}func(......
  • go tcp 同步 请求
      客户端服务端典型的同步请求-响应模型简单的Go示例,展示了如何实现您描述的同步请求-响应模型。客户端会发送一个包,然后等待接收服务器的响应。服务器接收包后处理数据,然后发送处理完成的消息。客户端接收到响应后,再次发送下一个包 服务端:packagemainimport(......
  • Django 笔记一
    Django模板标签变量模板语法:view{“HTML变量名”:“views变量名”}HTML{{变量名}}过滤器模板语法:{{变量名|过滤器:可选参数}}模板过滤器可以在变量被显示前修改它,过滤器使用管道字符{{name|lower}}{{name}}变量被过滤器lower处......
  • NoSQLBooster for MongoDB延长-试用期
    mongo的客户端较少,其中NosqlBoosterForMongo算是一个不错的客户端软件,但是他也不是免费的,试用期只有30天。好在这个软件是基于nodejs的。所以我们可以利用npm对这个应用进行解包串改源码,将源码中设置试用期的参数改成足够大,然后再封包,这样我们就能不断的白嫖了。      ......
  • django学习入门系列之第三点《快速了解 CSS》
    文章目录CSS快速了解CSS应用方式在标签上在head标签中写到文件中问题:用Flask框架开发不方便往期回顾CSSCSS专门用来"美化"标签基础CSS,写简单的界面&能看懂&会改就行模块,调整和修改快速了解style这种就叫css样式<imgsrc"..."style="height:100px"/><d......
  • 一分钱不花!本地部署Google最强开源AI大模型Gemma教程
    谷歌发布了轻量级开源系列模型Gemma,其性能强大,可与主流开源模型竞争。通过Ollama可轻松部署Gemma模型,并使用JANAI美化UI界面。显卡在AIGC应用中至关重要,推荐选择性能强、显存大的NVIDIA系列显卡。半个月前,谷歌搞了一波突然袭击,毫无预兆地发布了新一代AI模型Gemma,并宣称这是......
  • C#聊天室客户端完整③
    窗体进入聊天室界面(panel里面,label,textbox,button):聊天界面(flowLayoutPanel(聊天面板)):文档大纲(panel设置顶层(登录界面),聊天界面在底层)步骤:设置进入聊天室→输入聊天→右边自己发送的消息→左边别人发的消息MyClient.cs(进入聊天室类)internalclassMyClie......
  • Web框架,Python框架初识,Django框架初识与安装,
    ⅠWeb框架【一】Web框架本质web框架本质上可以看成是一个功能强大的socket服务端,用户的浏览器可以看成是拥有可视化界面的socket客户端。两者通过网络请求实现数据交互,从架构层面上先简单的将Web框架看做是对前端、数据库的全方位整合#TCP服务端与客户端进行交互的过程#......
  • MongoDB基本操作(Windows)
    本篇博文介绍知识目标   熟悉数据库和集合操作本篇目标   掌握MongoDB的部署掌握文档的插入、更新、删除以及查询操作一、MongoDB的安装部署在浏览器输入网址:www.mongodb.com2.点击“TRYFREE”或“GETSTARTED”按钮,进入MongoDB的下载页面;3.在下载页面......
  • MongoDB分片部署(windows)
    【CSDN博客】MongoDB分片操作详解与实践OS:win10MongoDB:4.4.24伪分布式分片架构从图中可以看出,分片集群中主要由三个部分组成,即分片服务器(Shard)、路由服务器(Mongos)以及配置服务器(ConfigServer)组成。其中,分片服务器有三个,即Shard1、Shard2、Shard3;路由服务器......