网络通信(待补充)

网络通信

互联网中主机和主机连接必须遵守特定的要求,这个要求成为协议
osi开放式系统互联,定义了计算机互联时网络通信的7层

  • 目前大规模使用的是tcp/ip协议:
  • 应用层:
    合并osi中567层(绘画,表示,应用)
    常用协议:http,ftp,smtp,pop3,ssl,rpc
  • 传输层:
    osi中第四层
    常用协议:tcp,udp
  • 网络层:
    osi中第三层
    常用协议:ip,ipv4,ipv6
  • 网络接口层:
    osi中第1,2层

ipv4和ipv6

  • ip网络协议,互联网上任何操作系统只要遵守ip协议就可以与互联网胡同
  • ipv4和ipv6的v时version,即版本的意思
  • 互联网上每台电脑的ip是唯一的
  • ip本质就是给连接到互联网上的计算机分配32位地址,由于二进制32位地址比较难及,所以用十进制表示,为了更方便记忆使用’点‘拆分发,分成四段,每段8位,每段最大255
  • 随着互联网的发展,接入网络的计算机在增多,ipv4逐渐无法满足大量计算机数量,ipv4的下个版本ipv6就出现了
  • ipv6是128位,是ipv4的4倍,以16进制表示

tcp和udp

  • 英文全称 transmission control protocol
  • 中文全称 传输传输控制协议
  • 是tcp/ip中传输层协议,一种面向连接的,可靠的,基于字节流的协议
  • tcp协议每次连接/关闭都需要确认,也就是经典的连接三次握手,断开连接四次握手
  • tcp中的几个术语:
    syn 同步标识
    fin 结束标识
    ack 确认标识
    psh 有data传输
    rst 连接重置
    seq 数据包
  • 三次握手:
    1.客户端发送syn=1.seq=随机数字报文给服务器端,客户端进入syn_send状态
    2.服务器收到syn报文,syn=1知道客户端要建立连接,相应ack number(客户端seq+1),syn=1,ack=1,seq=随机数字,连接状态syn_recv状态
    3.客户端收到syn,检查ack number和ack=1是否正确,如果全部正确,向服务器端发送ack number=服务端seq+1和ack=1,服务器接收到后检查是否正确,正确后建立连接,连接状态进入established
  • tcp关闭连接需要四次握手,包含一次角色转换(以客户端主动发起关闭请求举例):
    1.主机A(主动方)向主机B(被动方)发送fin表示数据发送完成ack=z,seq=x,主机A进入fin_wait1状态,这个状态非常短暂,之后等待对方确认ack过程自己进入fin_wait2状态,如果超时没有收到ack直接进入closed
    2.主机B收到fin后返回主机A内容ack=x+1,seq=z,主机B进入close_wait状态
    3.主机B关闭连接发送给主机A内容ack=x,seq=y,主机A进入到time_wait状态,处于这个状态后等待2个报文最大存活周期后进入closed状态,此时主机B进入last_ack状态
    4.主机A收到后返回ack=y+1,seq=x
  • udp:
    1.全称user datagram protocol 用户数据报协议
    2.是一种无连接的协议
    3.基于udp协议主机把数据包发送给网络后就不管了,是一种不可靠协议
  • tcp和udp的主要区别:
    1.tcp安全可靠,udp不安全不可靠
    2.udp的速度高于tcp

socket

  • 在标准库的net包中提供了可移植德网络i/o接口,其中就包含了socket
  • socket在tcp/ip网络分层中并不存在,是对tcp或udp封装
  • 如果非要给socket一个解释:
    1.实现网络双向通讯连接德一套api
    2.常称socket为“套接字”
  • socket分类:
    1.按照连接时间:
    短连接
    长连接(http1.1开始也支持长连接,socket替换方案)
    2.按照客户端和服务器端数量:
    点对点
    点对多
    多对多

go语言对socket的支持

  • tcpaddr结构体表示服务器ip和端口
    ip是type ip []byte
    port是服务器监听的接口
    zone是ipv6的地址表示

type TCPAddr struct{
IP IP
Port int
zone string
}

  • tcpconn结构体表示连接,封装了数据读写操作

type TCPConn struct{
conn
}

  • tcplistener负责监听服务器特定端口

type TCPListener struct{
fd *netFD
}

客户端向服务端发送消息

  • 服务端代码

package main
import (
“net”
“fmt”
)
func main(){
//创建TCPAddress变量,指定协议tcp4,监听本机8899端口
addr,_:= net.ResolveTCPAddr(“tcp4”,“localhost:8899”)

//监听TCPAddress设定的地址
lis,_ := net.ListenTCP(“tcp4”,addr)
fmt.Println(“服务器已经启动”)

//服务器端添加死循环,不停接收客户端对象
fo{
//阻塞式等待客户端消息,返回连接对象,用于接收客户端消息或向客户端发送消息
conn,_ := lis.Accept()

//在此处添加协程实现并发
go func() {

//把数据读取到切片中
b:= make([]byte,256)
fmt.Println(“read之前”)

//客户端没有发送数据且客户端对象没有关闭,Read()将会阻塞,一旦接收到数据就不阻塞
count,_:= conn.Read(b)
fmt.Println(“接收到的数据”,string(b[:count]))

//向客户端发送消息
conn.Write(append([]byte*(“server:”),b[:count]…))

//关闭连接
conn.Close()
}()
}
fmt.Println(“服务器结束”)
}

  • 客户端代码

package main
func main(){
//创建服务器端地址
addr,_:= net.ResolveTCPAddr(“tcp4”,“localhost:8899”)

//创建连接
conn,_:= net.DialTCP(“tcp”,nil,addr)

//发送数据
conn.Write([]byte(“客户端发送的数据”))

//接收信息
b := make([]byte,1024)
count,_:=conn,Read(b)
fmt.Println(“服务器发送回来的消息为:”,b[:count])

//关闭连接
conn.Close()
}

点对点通信

概述

  • 点对点通信就是客户端A发送消息给服务端,再由服务端把消息传递给客户端B
  • 正常情况下客户端A和客户端B可以通过用户名,IP等唯一身份标识区分每个用户。

代码实现

-项目结构如下:
–项目名
–src
–client
client.go
–server
server.go
main.go

  • 服务端代码
    package main
    import(
    “net”
    “fmt”
    “strings”
    )

type user struct {
Username string
OtherUsername string
Msg string
ServerMsg string
}
var(
userMap =make(map[string]net.Conn)
user = new(User)
)
func main(){
addr,_ := net.ResolveTCPAddr(“tcp4”,“:要链接的端口号”)
lis,_ := net.ListenTCP(“tcp4”,addr)
for{
conn,_ := lis.Accept()
go func(){
for{
b:= make([]byte,512)
count,_ := conn.Read(b)
arrStr := strings.Split(string(b[:count]),“-”)
user.Username = arrStr[0]
user.OtherUsername = arrStr[1]
user.Msg = arrStr[2]
user.ServerMsg = arrStr[3]
userMap[user.Username] = conn
if v,ok := userMap[user.OtherUsername];ok&&v != nil{
user.ServerMsg = “”
n,e := v.Write([]byte(fmt.Sprintf(“%s-%s-%s-%s”,user.Username),user.OtherUsername,user.Msg,user.ServerMsg))
fmt.Println(n,e)
if n==0 || e != nil{
delete(user.Map,user.OtherUsername)
conn.Close()
v.Vlose()
break
}
else{
user.ServerMsg = “对方不在线”
n,e := conn.Write([]byte(fmt.Sprintf(“%s-%s-%s-%s”,user.Username),user.OtherUsername,user.Msg,user.ServerMsg))
fmt.Println(n,e)
}
}()
}
}
}

  • 客户端代码

package main
type User struct{
Username string
OtherUsername string
Msg string
ServerMsg string
}

var(
user = new(User)//当前登录用户信息
wg sync.WaitGroup
)

func main(){
wg.Add(1)
fmt.Println(“请输入您的账号:”)
fmt.Scanln(&user.Username)
fmt.Println(“请输入要给谁发送信息”)
fmt.Scanln(&user.OtherUsername)
addr,_ := net.ResolveTCPAddr(“tcp4”,“loaclhost:8899”)
conn._ := net.DialTCP(“tcp4”,nil,addr)
go func(){
fmt.Println(“请输入您要发送的消息:只提示一次”)
for{
fmt.Scanln(&user.Msg)
if user.Msg ==“exit”{
conn.Close()
wg.Done()
os.Exit(0)
}
conn.Write([]byte(fmt.Sprintf(“%s-%s-%s-%s”,user.Username,user.OtherUsername,user.Msg,user.ServerMsg)))
}
}()
//接收消息
go func(){
for{
b :=make([]byte,1025)
count,_ := conn,Read(b)
arry:= strings.Split(string(b[:count]),sep"-“)
user2 := new(User)
user2.Username =array[0]
user2.OtherUsername=array[1]
user2.Msg=array[2]
user2.ServerMsg=array[3]
if user2.ServerMsg != “”{
fmt.Println(”\t\t服务器的消息:“,user2.ServerMsg)
}else{
fmt.Println(”\t\t",user2.Username,“:”,user2.Msg)
}
}
}()
wg.Wait()
}

你可能感兴趣的:(golang)