本文将从回顾TCP的基本概念出发,到了解Golang下网络Socket编程的理念,再到使用Golang搭建几个简单的TCP Server Demo。
TCP位于传输层,在TCP/IP五层协议中,处于网络层之上,应用层之下。具体来看,TCP链接是一个典型的C-S模型,也就是客户端(Client)-服务端(Server)模型。客户端发出连接请求,服务端接受请求,并返回一个ACK,客服端收到ACK之后,明白了Server已经接受了自己的请求,然后发送一个ACK回去,告知Server,“我知道你接受我了,我们可以开始传输数据了。”至此,TCP三次握手完成。
TCP通过传输序列有序的数据并附加ACK的方式,来保证其稳定性,来防止连接过程中发生丢包。除此之外,有一些有名的TCP改进机制,比如TCP-Reno,这个以后有机会再重新开一篇文章详细聊。
直接搬出学校的笔记 2333:
- TCP is connection-oriented protocol(3-Way handshake), UDP is connection less (multicast).
- TCP is reliable, delivery guaranteed(resend), UDP is unreliable.
- TCP is ordered (has sequence), UDP may not in the order.
- TCP is slow and heavy weight(20 bytes header), UDP is fast and light-weight (8 b header).
- TCP has flow control and congestion control.
- TCP is used by HTTP, HTTPS, FTP, SMP; UDP is used by DHCP, DNS, NFS...
以前在学校做Project的时候,遇到用C++写TCP的时候有时还挺头痛的,体验不好。使用Golang写TCP/IP层,相比C++,简直不要简单太多。难能可贵的是在简单的同时,Golang还保持着不俗的性能。
基于Golang的I/O机制和并发原语的原生支持,再加上对网络API的封装,我们可以比较轻松地实现一个高效的服务端或者客户端程序。一般的实现就是调用net.Listen(“tcp4”, address)得到一个net.Listener,然后无限循环调用net.Listener.Accept,之后就可以得到一个net.Conn,可以调用net.Conn的接口设置发送和接收缓冲区大小,可以设置KEEPALIVE等。因为TCP的双工特性,所以可以针对一个net.Conn可以专门启动一个goroutine去无限循环接收对端发来的数据,然后解包等。
我们下面来写一个简单的回音服务器(Echo Server),就是client发送一个信息,server稍微处理一下,就把原信息返回来给client。整个过程是,Server在监听,拿到监听到的信息后,通过写网络文件传回给Client,然后继续监听接下来的信息。
package main
import (
"bufio"
"fmt"
"log"
"net"
)
func main() {
//Server gets a listener, listening to localhost:8080
li, err := net.Listen("tcp", ":8080")
if err != nil {
log.Panic(err)
}
//Don't forget to close later, otherwise resourse will leak.
defer li.Close()
//Keep listening
for {
//Listener accepts the request, and build the connection
conn, err := li.Accept()
if err != nil {
log.Panic(err)
}
//Do the job.
go handle(conn)
}
}
func handle(conn net.Conn) {
defer conn.Close()
//returns a new Scanner to read from connection io.Reader.
scanner := bufio.NewScanner(conn)
//Scan will get next token, which will then be available through the Bytes or Text method.
for scanner.Scan() {
//Get most recent token generated by a call to Scan
ln := scanner.Text()
//Server prints the info from the connection
fmt.Println(ln)
//Write back to connection:
fmt.Fprintf(conn, "I heard you said: %s\n", ln)
}
}