网络编程的本质是两个设备之间的数据交换,在计算机网络中,设备主要指计算机。数据传递本身没有多大的难度,不就是把一个设备中的数据发送给另外一个设备,然后接受另外一个设备反馈的数据。
现在的网络编程基本上都是基于请求/响应方式的,也就是一个设备发送请求数据给另外一个设备,然后接收这个设备的反馈。
在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称作客户端(Client),等待其他程序连接的程序被称作服务器(Server)。客户端程序可以在需要的时候启动,而服务器为了能够时刻相应连接,则需要一直启动。
连接一旦建立以后,就客户端和服务器端就可以进行数据传递了,而且两者的身份是等价的。
为了在一台设备上可以运行多个程序,人为的设计了端口(Port)的概念,类似的例子是公司内部的分机号码。
每个网络程序,无论是客户端还是服务器端,都对应一个或多个特定的端口号。由于 0-1024 之间多被操作系统占用,所以实际编程时一般采用1024 以后的端口号。
使用端口号,可以找到一台设备上唯一的一个程序。所以如果需要和某台计算机建立连接的话,只需要知道IP地址或域名即可,但是如果想和该台计算机上的某个程序交换数据的话,还必须知道该程序使用的端口号。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket后面,对用户来说只需要调用Socket规定的相关函数,让Socket去组织符合指定的协议数据然后进行通信。
golang中标准库提供了net
包,用来进行Socket编程
SOCK_STREAM
)和数据报式Socket(SOCK_DGRAM
),流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,针对于无连接的UDP服务应用package main
import (
"fmt"
"net"
)
const (
IP = "127.0.0.1"
PORT = 8080
)
func main() {
// 设置监听地址
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", IP, PORT))
if err != nil {
fmt.Println(err)
}
// 开启udp监听
conn, err := net.ListenUDP("udp", addr)
if err != nil {
fmt.Println(err)
}
defer conn.Close()
for {
data := make([]byte, 1024)
_, clientAddr, err := conn.ReadFromUDP(data)
if err != nil {
fmt.Println(err)
}
if clientAddr != nil {
fmt.Printf("msg from client:%s,msg:%s", clientAddr.IP.String(), string(data))
conn.WriteToUDP([]byte("accept ok"), clientAddr)
}
}
}
package main
import (
"fmt"
"net"
)
const (
IP = "127.0.0.1"
PORT = 8080
)
func main() {
// 连接udp server
conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", IP, PORT))
if err != nil {
fmt.Println(err)
}
defer conn.Close()
var msg string
for {
fmt.Scanln(&msg)
if msg == "exit" {
break
}
_, err = conn.Write([]byte(msg))
if err != nil {
fmt.Println(err)
}
data := make([]byte, 1024)
_, err := conn.Read(data)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(data))
}
}
Go 语言TcpServer 教程的步骤可以总结为:定义通信的地址和端口、使用 Listen 函数监听 TCP 的地址和端口信息并得到连接信息、使用连接信息的 Accept 函数等待连接、每来一个连接得到一个连接,使用连接进行读写数据。
一个TCP服务端可以同时连接很多个客户端。因为Go语言中创建多个goroutine实现并发非常方便和高效,所以我们可以每建立一次链接就创建一个goroutine去处理。
package main
import (
"fmt"
"net"
)
const (
SERVER_IP = "127.0.0.1"
SERVER_PORT = 8080
)
//处理tcp连接
func handler(conn net.Conn) {
defer conn.Close()
clientAddr := conn.RemoteAddr().String()
fmt.Println("新客户端连接:", clientAddr)
for {
data := make([]byte, 1024)
i, err := conn.Read(data)
if err != nil {
fmt.Println("客户端断开连接:", clientAddr)
return
}
fmt.Println(string(data[:i]))
conn.Write([]byte("服务器成功接收"))
}
}
func main() {
//开启监听
li, err := net.Listen("tcp", fmt.Sprintf("%s:%d", SERVER_IP, SERVER_PORT))
if err != nil {
fmt.Println(err)
}
defer li.Close()
for {
//接收并建立链接
conn, err := li.Accept()
if err != nil {
fmt.Println(err)
}
go handler(conn)
}
}
package main
import (
"fmt"
"net"
)
const (
CLIENT_IP = "127.0.0.1"
CLIENT_PORT = 8080
)
func main() {
//连接服务端
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", CLIENT_IP, CLIENT_PORT))
if err != nil {
fmt.Println(err)
}
defer conn.Close()
for {
var msg string
fmt.Scanln(&msg)
if msg == "exit" {
break
}
_, err := conn.Write([]byte(msg))
if err != nil {
fmt.Println(err)
}
data := make([]byte, 1024)
i, err := conn.Read(data)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(data[:i]))
}
}