Skip to content

Instantly share code, notes, and snippets.

@hyper0x
Last active August 15, 2023 12:34
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save hyper0x/8f724925c344f896b63c to your computer and use it in GitHub Desktop.
Save hyper0x/8f724925c344f896b63c to your computer and use it in GitHub Desktop.
The interaction demo via TCP in Golang.
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"log"
"net"
"os"
"sync"
"time"
)
const (
DELIMITER byte = '\n'
QUIT_SIGN = "quit!"
)
func Read(conn net.Conn, delim byte) (string, error) {
reader := bufio.NewReader(conn)
var buffer bytes.Buffer
for {
ba, isPrefix, err := reader.ReadLine()
if err != nil {
if err == io.EOF {
break
}
return "", err
}
buffer.Write(ba)
if !isPrefix {
break
}
}
return buffer.String(), nil
}
func Write(conn net.Conn, content string) (int, error) {
writer := bufio.NewWriter(conn)
number, err := writer.WriteString(content)
if err == nil {
err = writer.Flush()
}
return number, err
}
func main() {
go func() {
listener, err := net.Listen("tcp", ":9090")
if err != nil {
log.Printf("Listener: Listen Error: %s\n", err)
os.Exit(1)
}
log.Println("Listener: Listening...")
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("Listener: Accept Error: %s\n", err)
continue
}
go func(conn net.Conn) {
defer conn.Close()
for {
log.Println("Listener: Accepted a request.")
log.Println("Listener: Read the request content...")
content, err := Read(conn, DELIMITER)
if err != nil {
log.Printf("Listener: Read error: %s", err)
}
if content == QUIT_SIGN {
log.Println("Listener: Quit!")
break
}
log.Printf("Listener: Received content: %s\n", content)
respContent := fmt.Sprintf("listener response: %s%c", content, DELIMITER)
log.Printf("Listener: the response content: %s\n", respContent)
num, err := Write(conn, respContent)
if err != nil {
log.Printf("Listener: Write Error: %s\n", err)
}
log.Printf("Listener: Wrote %d byte(s)\n", num)
}
}(conn)
}
}()
time.Sleep(time.Millisecond * 500)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
conn, err := net.DialTimeout("tcp", "127.0.0.1:9090", time.Millisecond*200)
if err != nil {
log.Printf("Sender: DialTimeout Error: %s\n", err)
os.Exit(1)
}
log.Println("Sender: Dial OK.")
for i := 0; i < 10; i++ {
reqContent := fmt.Sprintf("sender request %d.%c", i, DELIMITER)
log.Printf("Sender: the request content: %s\n", reqContent)
num, err := Write(conn, reqContent)
if err != nil {
log.Printf("Sender: Write Error: %s\n", err)
break
}
log.Printf("Sender: Wrote %d byte(s)\n", num)
respContent, err := Read(conn, DELIMITER)
if err != nil {
log.Printf("Sender: Read error: %s", err)
break
}
log.Printf("Sender: Received content: %s\n", respContent)
}
reqContent := fmt.Sprintf("%s%c", QUIT_SIGN, DELIMITER)
log.Printf("Sender: the request content: %s\n", reqContent)
num, err := Write(conn, reqContent)
if err != nil {
log.Printf("Sender: Write Error: %s\n", err)
}
log.Printf("Sender: Wrote %d byte(s)\n", num)
}()
wg.Wait()
time.Sleep(time.Millisecond * 500)
}
@dreamerlzl
Copy link

A great example, thank you!
I am wondering why we need to create a new bufio.Writer / bufio.Reader for each write/read in a single connection.

@hyper0x
Copy link
Author

hyper0x commented Aug 16, 2020

This allows data in multiple connections to not interfere with each other. You can also use only one reader/writer per connection, but be aware that you must reset them when appropriate.

@H7-25
Copy link

H7-25 commented Sep 25, 2020

i've used your example, i don't understard why it look read function lose some lines

@hyper0x
Copy link
Author

hyper0x commented Sep 26, 2020

i've used your example, i don't understard why it look read function lose some lines

Can you tell me the details? You can provide screenshots or other things.

@hamburghammer
Copy link

I would extract some of the inline functions to reduce the nesting and increase the readability.

@hyper0x
Copy link
Author

hyper0x commented Nov 2, 2020

I would extract some of the inline functions to reduce the nesting and increase the readability.

OK, it's valuable.

@geolffreym
Copy link

Could be a good option use scanner := bufio.NewScanner(conn) in place of bufio.NewReader(conn) then is possible use scanner.Split(bufio.ScanWords) ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment