多进程之间的通信常见的手段有管道/信号量/共享内存/Socket等,在上篇文章中介绍了管道的使用方法。管道在多进程通信中使用方便但是也具局限性,当通信双方在不同的机器上的时候通信方式更多采用Socket方式。在这篇文章中我们将会继续探索如何使用go所提供的net包等实现TCP和UDP方式的Socket通信。
传输层协议
OSI模型
OSI七层模型,简单来说,下三层主要负责数据通信,而上三层主要负责数据处理。处于第四层的传输层是通信子网和资源子网的接口和桥梁,整体起到承上启下的作用。而TCP和UDP则是传输层的协议。
![这里写图片描述](https://img-blog.csdn.net/20170203211411245?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1bWlhb2Nu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
TCP和UDP的区别
TCP和UDP最大的区别在于是否是面向连接的协议,作为面向连接的TCP协议,以此决定了在本质上是否是安全可靠的特性。
![这里写图片描述](https://img-blog.csdn.net/20170203211502012?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1bWlhb2Nu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
TCP通信
无论TCP方式还是UDP方式,常见的场景应用会分为服务器端和客户端的类型。作为面向连接的TCP方式,服务器端的应用一般会绑定到某一端口,然后再此端口上进行监听,当其受到客户端的连接时便会进行交互。
Server端实现
[root@liumiaocn goprj]# cat basic-tcp-server.go
package main
import "fmt"
import "os"
import "net"
import "time"
func main() {
tcp_address, err := net.ResolveTCPAddr("tcp4", ":8848")
if err != nil {
fmt.Println("Error happened when getting a tcp address...", err)
os.Exit(1)
}
tcp_listener, err := net.ListenTCP("tcp", tcp_address)
if err != nil {
fmt.Println("Error happened when listening to the tcp address...", err)
os.Exit(1)
}
str_header := "Hello, this is information from tcp server , and current time is : "
for {
tcp_connection, err := tcp_listener.Accept()
if err != nil {
continue
}
str_time := time.Now().String()
str_greetings := str_header + str_time
tcp_connection.Write([]byte(str_greetings))
tcp_connection.Close()
}
}
[root@liumiaocn goprj]#
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
客户端实现
[root@liumiaocn goprj]# cat basic-tcp-client.go
package main
import "fmt"
import "io/ioutil"
import "os"
import "net"
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0])
os.Exit(1)
}
arg_address := os.Args[1]
tcp_address, err := net.ResolveTCPAddr("tcp4", arg_address)
if err != nil {
fmt.Println("Error happened when getting a tcp address...", err)
os.Exit(1)
}
tcp_connection, err := net.DialTCP("tcp", nil, tcp_address)
if err != nil {
fmt.Println("Error happened when connecting a tcp server...", err)
os.Exit(1)
}
str_greeting, err := ioutil.ReadAll(tcp_connection)
if err != nil {
fmt.Println("Error happened when reading from the tcp server...", err)
os.Exit(1)
}
fmt.Println(string(str_greeting))
os.Exit(0)
}
[root@liumiaocn goprj]#
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
执行确认
首先将server端运行起来,等待client端的连接
[root@liumiaocn goprj]
运行client端,可以得到从server端发过的信息
[root@liumiaocn goprj]
Hello, this is information from tcp server , and current time is : 2017-01-31 10:56:48.590783817 -0500 EST
[root@liumiaocn goprj]
[root@liumiaocn goprj]
Hello, this is information from tcp server , and current time is : 2017-01-31 10:56:54.969361975 -0500 EST
[root@liumiaocn goprj]
[root@liumiaocn goprj]
Hello, this is information from tcp server , and current time is : 2017-01-31 10:57:08.226263371 -0500 EST
[root@liumiaocn goprj]
UDP方式
通过上述例子,可以了解到TCP方式的简单实现。接下来看一下UDP方式。UDP作为无连接的传输层协议的实现,在go所提供的API可以看到,形式已经非常类似。
Server端实现
[root@liumiaocn goprj]# cat basic-udp_server.go
package main
import "fmt"
import "os"
import "net"
import "time"
func main() {
udp_address, err := net.ResolveUDPAddr("udp4", ":9848")
if err != nil {
fmt.Println("Error happened when getting a udp address...", err)
os.Exit(1)
}
udp_connection, err := net.ListenUDP("udp", udp_address)
if err != nil {
fmt.Println("Error happened when listening to the udp address...", err)
os.Exit(1)
}
str_header := "Hello, this is information from udp server , and current time is : "
for {
var udp_buffer [1024]byte
_, address, err := udp_connection.ReadFromUDP(udp_buffer[0:])
if err != nil {
continue
}
str_time := time.Now().String()
str_greetings := str_header + str_time
udp_connection.WriteToUDP([]byte(str_greetings), address)
}
}
[root@liumiaocn goprj]#
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
客户端实现
[root@liumiaocn goprj]# cat basic-udp_client.go
package main
import "fmt"
import "os"
import "net"
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0])
os.Exit(1)
}
arg_address := os.Args[1]
udp_address, err := net.ResolveUDPAddr("udp4", arg_address)
if err != nil {
fmt.Println("Error happened when getting a udp address...", err)
os.Exit(1)
}
udp_connection, err := net.DialUDP("udp", nil, udp_address)
if err != nil {
fmt.Println("Error happened when connecting a udp server...", err)
os.Exit(1)
}
_, err = udp_connection.Write([]byte("hello"))
if err != nil {
fmt.Println("Error happened when sending message to the udp server...", err)
os.Exit(1)
}
var udp_buffer [1024]byte
num, err := udp_connection.Read(udp_buffer[0:])
if err != nil {
fmt.Println("Error happened when reading from the tcp server...", err)
os.Exit(1)
}
fmt.Println(string(udp_buffer[0:num]))
os.Exit(0)
}
[root@liumiaocn goprj]#
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
执行确认
首先将server端运行起来,等待client端的连接
[root@liumiaocn goprj]
运行client端,可以得到从server端发过的信息
[root@liumiaocn goprj]
Hello, this is information from udp server , and current time is : 2017-01-31 11:25:03.274881649 -0500 EST
[root@liumiaocn goprj]
[root@liumiaocn goprj]
Hello, this is information from udp server , and current time is : 2017-01-31 11:25:05.895074805 -0500 EST
[root@liumiaocn goprj]
[root@liumiaocn goprj]
Hello, this is information from udp server , and current time is : 2017-01-31 11:25:09.886004761 -0500 EST
[root@liumiaocn goprj]
总结
在这篇文章中,我们继续讨论了go语言中Socket通信的两个简单实例,通过这两个例子,实现了TCP和UDP的服务器和客户端的交互。虽然在实际中的应用还有很远一段距离,比如并发的控制上述简单的例子没有涉及到,但是通过这两个例子,已经能看出多进程通信的go的实现方式