Order of calls to conn.Read and conn.Write on server and client

105 views
Skip to first unread message

lijh8

unread,
Aug 11, 2024, 2:44:31 PM8/11/24
to golang-nuts

Hi community,

In my little example:
if server reads first, then writes;
client must writes first, then read;

then client must performs the operations in reverse order as server,
or both server and client will block.

In C++ boost.asio, the order of calls to async_read, async_write 
does not matter in server and client.

Is there this kind of ` async ` read and write ability in golang packages?

Thanks

```

// server.go:

func main() {
logfile := "logfile.log"
logfd, err := log2.InitLog(logfile)
if err == nil {
defer logfd.Close()
}

if len(os.Args) != 2 {
log.Printf("Usage: ./server <port>")
os.Exit(-1)
}

port := os.Args[1]
ln, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Println(err)
os.Exit(-1)
}
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
os.Exit(-1)
}
go handleConnection(conn)
}
}

func handleConnection(conn net.Conn) {
defer conn.Close()

msgID := 0
buffer := make([]byte, 1024)

for {
n, err := conn.Read(buffer)
if err != nil {
log.Println(err, n)
break
}
fmt.Println(string(buffer))

msgID++
msg := fmt.Sprintf("msg from server with msgID: %d", msgID)
n, err = conn.Write([]byte(msg))
if err != nil {
log.Println(err, n)
break
}
}
}


// client.go:

func main() {
logfile := "logfile.log"
logfd, err := log2.InitLog(logfile)
if err == nil {
defer logfd.Close()
}

if len(os.Args) != 4 {
log.Printf("Usage: ./client <serverIP> <port> <tag>")
os.Exit(-1)
}

serverIP := os.Args[1]
port := os.Args[2]
tag := os.Args[3]
conn, err := net.Dial("tcp", serverIP+":"+port)
if err != nil {
log.Println(err)
os.Exit(-1)
}
handleConnection(conn, tag)
}

func handleConnection(conn net.Conn, tag string) {
defer conn.Close()

msgID := 0
buffer := make([]byte, 1024)

for {
msgID++
msg := fmt.Sprintf("msg from client: %s, msgID: %d", tag, msgID)
n, err := conn.Write([]byte(msg))
if err != nil {
log.Println(err, n)
break
}

n, err = conn.Read(buffer)
if err != nil {
log.Println(err, n)
break
}
fmt.Println(string(buffer))
}
}


```

Robert Engels

unread,
Aug 11, 2024, 2:50:02 PM8/11/24
to lijh8, golang-nuts
This is not true. The synchronous requirements are based on higher level protocol requirements. You can just as easily do async with Reader and Writer. 

Eg. The client can send multiple requests and the server could respond to these out of order (I.e the response contains the request id) or not respond at all. 

You might want to look at using gRPC which will help you understand the model better. 

On Aug 11, 2024, at 1:44 PM, 'lijh8' via golang-nuts <golan...@googlegroups.com> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/tencent_16864CF32F826D31908D052707F2E2621709%40qq.com.

Robert Engels

unread,
Aug 11, 2024, 2:51:21 PM8/11/24
to lijh8, golang-nuts
Also the proper use of Go routines allow you to implement async without the messiness of callbacks. 

On Aug 11, 2024, at 1:49 PM, Robert Engels <ren...@ix.netcom.com> wrote:



lijh8

unread,
Aug 12, 2024, 11:54:13 AM8/12/24
to Robert Engels, golang-nuts
Hi,

I updated code like this. now I can use the same handleConnection both server and client, the only difference is the content of message which is read or written.

```

func handleConnection(conn net.Conn) {
defer conn.Close()

msgID := 0
reader := bufio.NewReader(conn)
writer := bufio.NewWriter(conn)
readChannel := make(chan struct{})
writeChannel := make(chan struct{})

go func() {
for {
line, err := reader.ReadString('\n')
if err != nil {
log.Println(err, line)
close(readChannel)
break
}
fmt.Print(line)
}
}()

go func() {
for {
msgID++
msg := fmt.Sprintf("msg from server with msgID: %d\n", msgID)
if n, err := writer.WriteString(msg); err != nil {
log.Println(err, n)
close(writeChannel)
break
}
writer.Flush()
}
}()

for {
if _, ok := <-readChannel; !ok {
break
}
if _, ok := <-writeChannel; !ok {
break
}
}
}

```

lijh8

unread,
Aug 12, 2024, 12:48:55 PM8/12/24
to golang-nuts
updated. 
the order of calls to read and write does not matter, read and write will not block each other.
the same handleConnection function can be used on both server and client sides.
the only difference is the message content which is read and written.

```


func handleConnection(conn net.Conn, tag string) {
defer conn.Close()

msgID := 0
reader := bufio.NewReader(conn)
writer := bufio.NewWriter(conn)
readChannel := make(chan struct{})
writeChannel := make(chan struct{})

go func() {
for {
line, err := reader.ReadString('\n')
if err != nil {
log.Println(err, line)
close(readChannel)
break
}
fmt.Print(line)
}
}()

go func() {
for {
msgID++
msg := fmt.Sprintf("msg from client: %s, msgID: %d\n", tag, msgID)
if n, err := writer.WriteString(msg); err != nil {
log.Println(err, n)
close(writeChannel)
break
}
writer.Flush()
}
}()

if _, ok := <-readChannel; !ok {
return
}
if _, ok := <-writeChannel; !ok {
return
}
}


```
Reply all
Reply to author
Forward
0 new messages