go语言学习笔记29------Goland之TCP、UDP通信并发②

1.TCP并发C/S模型通信

1.1并发Server

现在已经完成了客户端与服务端的通信,但是服务端只能接收一个用户发送过来的数据,怎样接收多个客户端发送过来的数据,实现一个高效的并发服务器呢?

Accept()函数的作用是等待客户端的链接,如果客户端没有链接,该方法会阻塞。如果有客户端链接,那么该方法返回一个Socket负责与客户端进行通信。所以,每来一个客户端,该方法就应该返回一个Socket与其通信,因此,可以使用一个死循环,将Accept()调用过程包裹起来。

需要注意的是,实现并发处理多个客户端数据的服务器,就需要针对每一个客户端连接,单独产生一个Socket,并创建一个单独的goroutine与之完成通信。


package main

import (
   "net"
   "fmt"
)

func Handle(conn net.Conn)  {
   buf:=make([]byte,4096)
   n,err:=conn.Read(buf)
   if err!=nil{
      fmt.Println("read err",err)
      return 
   }
   messsage:=buf[:n]
   fmt.Println("%d服务器端发来信息%s",conn.RemoteAddr(),messsage)
}
func main() {
   listen, err := net.Listen("tcp", "127.0.0.1")
   if err != nil {
      fmt.Println("listen err", err)
      return
   }
   defer listen.Close()
   for {
      conn,err:=listen.Accept()
      if err!=nil{
         fmt.Println("accept err",err)
         return 
      }
      go Handle(conn)
   }
}

1.2并发Client

客户端不仅需要持续的向服务端发送数据,同时也要接收从服务端返回的数据。因此可将发送和接收放到不同的协程中。
主协程循环接收服务器回发的数据(该数据应已转换为大写),并打印至屏幕;子协程循环从键盘读取用户输入数据,写给服务器。读取键盘输入可使用 os.Stdin.Read(str)。定义切片str,将读到的数据保存至str中。
这样,客户端也实现了多任务。

客户端代码实现:

package main

import (
   "net"
   "fmt"
   "os"
)

func main() {
   conn, err := net.Dial("tcp", "127.0.0.1")
   if err != nil {
      fmt.Println("dial err", err)
      return
   }
   defer conn.Close()
   go func() {
      buf := make([]byte, 4096)
      for {
         n, err := os.Stdin.Read(buf)
         if err != nil {
            fmt.Println("read err", err)
         }
         conn.Write(buf[:n])
      }
   }()
   read := make([]byte, 4096)
   n, err := conn.Read(read)
   if err != nil {
      fmt.Println("read err", err)
      return
   }
   fmt.Println("从服务器中读取数据%s", string(read[:n]))
}

2.UDP并发C/S模型通信

2.1并发Server

其实对于UDP而言,服务器不需要并发,只要循环处理客户端数据即可。客户端也等同于TCP通信并发的客户端。

服务器:

package main

import (
   "net"
   "fmt"
)

func main() {
   // 创建 服务器 UDP 地址结构。指定 IP + port
   laddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:8003")
   if err != nil {
      fmt.Println("ResolveUDPAddr err:", err)
      return
   }
   // 监听 客户端连接
   conn, err := net.ListenUDP("udp", laddr)
   if err != nil {
      fmt.Println("net.ListenUDP err:", err)
      return
   }
   defer conn.Close()

   for {
      buf := make([]byte, 1024)
      n, raddr, err := conn.ReadFromUDP(buf)
      if err != nil {
         fmt.Println("conn.ReadFromUDP err:", err)
         return
      }
      fmt.Printf("接收到客户端[%s]:%s", raddr, string(buf[:n]))

      conn.WriteToUDP([]byte("I-AM-SERVER"), raddr) // 简单回写数据给客户端
   }
}

1.2并发Client

客户端

ackage main

import (
   "net"
   "fmt"
   "os"
)

func main() {
   conn, err := net.Dial("udp", "127.0.0.1")
   if err != nil {
      fmt.Println("dial err", err)
      return
   }
   defer conn.Close()
   go func() {
      buf := make([]byte, 4096)
      for {
         n, err := os.Stdin.Read(buf)
         if err != nil {
            fmt.Println("read err", err)
         }
         conn.Write(buf[:n])
      }
   }()
   read := make([]byte, 4096)
   n, err := conn.Read(read)
   if err != nil {
      fmt.Println("read err", err)
      return
   }
   fmt.Println("从服务器中读取数据%s", string(read[:n]))
}

3.UDP与TCP的差异

这里写图片描述

展开阅读全文

没有更多推荐了,返回首页