理论
声明:此案例只用于学习,掌握相关知识,请勿用于实际开发。
TCP连接过程
tcp建立连接会有三次握手
- 客户端发送的TCP报文中标志位SYN置1,初始序号seq=x(随机选择)。Client进入SYN_SENT状态,等待Server确认。
- 服务器收到数据包后,根据标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=x+1,随机产生一个初始序号seq=y,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
- Client收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并将该数据包发送给Server。Server检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
tcp建立连接会有三次握手
- Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
- Server收到FIN后,发送一个ACK给Client,确认序号为u + 1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
- Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
- Client收到FIN后,Client进入TIME_WAIT状态(主动关闭方才会进入该状态),接着发送一个ACK给Server,确认序号为w + 1,Server进入CLOSED状态,完成四次挥手。
以上就是书本上面的理论知识的,还是来做实际操作吧!
聊天室的组成
1、服务端和客户端
这里的服务端,只用户协调数据
服务端代码
package main
import (
"fmt"
"log"
"net"
)
// Client 创建用户结构体类型
type Client struct {
Msg chan string // 消息内容
Addr string // IP地址
}
var (
// 创建全局map,将用户存储到这里
onlineMap = make(map[string]Client)
// 创建全局channel传递用户消息
message = make(chan string)
)
func main() {
// 创建监听套接字
listener, err := net.Listen("tcp", "127.0.0.1:9001")
if err != nil {
fmt.Println("Listen err", err)
return
}
defer listener.Close()
// 创建管理者go程
go Manager()
// 循环监听客户端请求
for {
// 等待客户端的连接, 无连接就等待
conn, err := listener.Accept()
if err != nil {
fmt.Println("accept err", err)
return
}
// 获取用户网络地址IP + 端口号(port)
netAddr := conn.RemoteAddr().String()
// 创建新连接用户的结构体
c := Client{Msg: make(chan string), Addr: netAddr}
// 将新连接用户添加到在线用户map中,key:IP+port value:client
onlineMap[netAddr] = c
// 创建专门用来给当前用户发送消息的goroutine
go WriteMsgToClient(c, conn)
// 发送用户上线消息到全局通道中
message <- MakeMsg(c, "login")
// 创建一个sendMsg,专门处理用户发送的消息
go SendMsg(conn, c)
}
}
func WriteMsgToClient(c Client, conn net.Conn) {
// 监听传过来的用户的客户端是否有消息,如果有消息就写消息,没有进行等待状态
for msg := range c.Msg {
_, err := conn.Write([]byte(msg + "\n"))
if err != nil {
// 打印错误
log.Printf("[%v], err: %v", c.Addr, err)
}
}
}
func SendMsg(conn net.Conn, c Client) {
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n == 0 {
fmt.Printf("检测到客户端%s退出 \n", c.Addr)
delete(onlineMap, conn.RemoteAddr().String()) // 删除此客户端
conn.Close()
return
}
if err != nil {
fmt.Println("conn Read err", err)
return
}
// 将读到的用户消息保存到msg中,string类型
msg := string(buf[:n])
// 将读到的用户消息广播给所用在线用户(写入到message中)
message <- MakeMsg(c, msg)
}
}
func Manager() {
// 初始化map,onlineMap
for { // 循环从message中读取是否有数据
// 监听channel中是否有数据,有数据存储至message,无数据就阻塞
msg := <-message
// 循环发送消息给所有在线用户,给所有用户赋值
for _, c := range onlineMap {
c.Msg <- msg
}
}
}
// MakeMsg 组装要发送的消息结构
func MakeMsg(c Client, msg string) (buf string) {
return "[" + c.Addr + "]" + msg
}
客服端代码
package main
import (
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:9001")
if err != nil {
panic(err)
}
conn.Write([]byte("123" + "\n"))
buf := make([]byte, 1024)
for {
n, err := conn.Read(buf)
if err != nil {
panic(err)
}
if n != 0 {
fmt.Println(string(buf[:n]))
}
}
}
转载自:查看原文章请点击