이것은 libp2p 라이브러리를 사용한 p2p(peer-to-peer) 통신의 사용을 보여주는 간단한 채팅 애플리케이션이다. 중앙 서버 없이도 두 사용자가 직접 네트워크 연결을 통해 서로 통신할 수 있습니다
go get "package" 로 환경설정
go mod tidy
/*
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Juan Batiz-Benet
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This program demonstrate a simple chat application using p2p communication.
*
*/
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!")
// Create a buffer stream for non blocking read and write.
//읽기 및 쓰기를 차단하지 않는 버퍼 스트림을 만듭니다.
rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))
//고루틴 readData(rw) 호출
//고루틴 writeData(rw) 호출
go readData(rw)
go writeData(rw)
// stream 's' will stay open until you close it (or the other side closes it).
//스트림 's'는 사용자가 닫을 때까지(또는 다른 쪽이 닫을 때까지) 열려 있습니다.
}
func readData(rw *bufio.ReadWriter) {
for {
//str변수가 데이터를 읽음
str, _ := rw.ReadString('\n')
if str == "" {
return
}
if str != "\n" {
// Green console colour: \x1b[32m
// Reset console colour: \x1b[0m
// 녹색 콘솔 색상 : \x1b[32m]
// 콘솔 색상 재설정: \x1b[0m]
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
}
//쓰기함수를 통해 sendData를 보냄?
rw.WriteString(fmt.Sprintf("%s\n", sendData))
rw.Flush()
}
}
func main() {
//ctx 변수에 context 변수 저장
ctx, cancel := context.WithCancel(context.Background())
//예약어 defer 사용
defer cancel()
//flag는 명령줄 인자 읽는 함수
sourcePort := flag.Int("sp", 0, "Source port number")
dest := flag.String("d", "", "Destination multiaddr string")
help := flag.Bool("help", false, "Display help")
debug := flag.Bool("debug", false, "Debug generates the same node ID on every execution")
flag.Parse()
if *help {
fmt.Printf("This program demonstrates a simple p2p chat application using libp2p\n\n")
fmt.Println("Usage: Run './chat -sp <SOURCE_PORT>' where <SOURCE_PORT> can be any port number.")
fmt.Println("Now run './chat -d <MULTIADDR>' where <MULTIADDR> is multiaddress of previous listener host.")
os.Exit(0)
}
// If debug is enabled, use a constant random source to generate the peer ID. Only useful for debugging,
// off by default. Otherwise, it uses rand.Reader.
// 디버그를 사용할 수 있는 경우 일정한 랜덤 소스를 사용하여 피어 ID를 생성합니다. 디버깅에만 유용합니다,
// 그렇지 않으면 랜덤.reader 를 사용합니다.
var r io.Reader
if *debug {
// Use the port number as the randomness source.
// This will always generate the same host ID on multiple executions, if the same port number is used.
// Never do this in production code.
// 포트 번호를 랜덤 소스로 사용합니다.
// 이렇게 하면 동일한 포트 번호가 사용되는 경우 여러 실행에서 항상 동일한 호스트 ID가 생성됩니다.
// 프로덕션 코드에서 이 작업을 수행하지 마십시오.
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
}
// Create a thread to read and write data.
go writeData(rw)
go readData(rw)
}
// Wait forever
select {}
}
func makeHost(port int, randomness io.Reader) (host.Host, error) {
// Creates a new RSA key pair for this host.
// 이 호스트에 대한 새 RSA 키 쌍을 생성합니다.
prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, randomness)
if err != nil {
log.Println(err)
return nil, err
}
// 0.0.0.0 will listen on any interface device.
// 0.0.0.0은 모든 인터페이스 장치에서 수신합니다.
sourceMultiAddr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port))
// libp2p.New constructs a new libp2p Host.
// Other options can be added here.
// libp2p.New는 새 libp2p Host를 구성합니다.
// 다른 옵션을 여기에 추가할 수 있습니다.
return libp2p.New(
libp2p.ListenAddrs(sourceMultiAddr),
libp2p.Identity(prvKey),
)
}
func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHandler) {
// Set a function as stream handler.
// This function is called when a peer connects, and starts a stream with this protocol.
// Only applies on the receiving side.
// 함수를 스트림 처리기로 설정합니다.
// 이 기능은 피어가 연결하고 이 프로토콜로 스트림을 시작할 때 호출됩니다.
// 수신측에만 적용됩니다.
h.SetStreamHandler("/chat/1.0.0", streamHandler)
// Let's get the actual TCP port from our listen multiaddr, in case we're using 0 (default; random available port).
// 0(기본값; 임의 사용 가능한 포트)을 사용하는 경우를 대비하여 수신이 되는 멀티addr 에서 실제 TCP 포트를 가져옵니다.
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("was not able to find actual local port")
return
}
log.Printf("Run './chat -d /ip4/127.0.0.1/tcp/%v/p2p/%s' on another console.\n", port, h.ID().Pretty())
log.Println("You can replace 127.0.0.1 with public IP as well.")
log.Println("Waiting for incoming connection")
log.Println()
}
func startPeerAndConnect(ctx context.Context, h host.Host, destination string) (*bufio.ReadWriter, error) {
log.Println("This node's multiaddresses:")
for _, la := range h.Addrs() {
log.Printf(" - %v\n", la)
}
log.Println()
// Turn the destination into a multiaddr.
// 대상을 다중 addr로 변경합니다.
maddr, err := multiaddr.NewMultiaddr(destination)
if err != nil {
log.Println(err)
return nil, err
}
// Extract the peer ID from the multiaddr.
// 다중 addr 기능에서 피어 ID를 추출합니다.
info, err := peer.AddrInfoFromP2pAddr(maddr)
if err != nil {
log.Println(err)
return nil, err
}
// Add the destination's peer multiaddress in the peerstore.
// This will be used during connection and stream creation by libp2p.
// 피어 store에 목적지의 피어 multiaddress를 추가합니다.
// 이것은 libp2p에 의한 연결 및 스트림 생성 중에 사용될 것이다.
h.Peerstore().AddAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL)
// Start a stream with the destination.
// Multiaddress of the destination peer is fetched from the peerstore using 'peerId'.
// 목적지로 스트림을 시작합니다.
// 'peerId'를 사용하여 목적지 피어의 다중 주소를 피어 저장소에서 가져옵니다.
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")
// Create a buffered stream so that read and writes are non blocking.
// 읽기 및 쓰기가 차단되지 않도록 버퍼링된 스트림을 만듭니다.
rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))
return rw, nil
}
cd "go 소스가 있는 경로"
go build .
./chat
Golang Algorithm - jobAllocation (0) | 2023.02.27 |
---|---|
Golang Algorithm - decompression (0) | 2023.02.24 |
Golang Algorithm - coinChange (0) | 2023.02.23 |
Golang Algorithm - 백준 1015번 수열 정렬 알고리즘 (0) | 2023.02.22 |
Golang Algorithm - uglyNumbers (0) | 2023.02.21 |