网络前三层协议的代码模拟

最近看了一篇文章觉得非常好:

 https://www.zhihu.com/question/21546408/answer/2303205686

为了加深印象,特写了一个程序来模拟一下其中的传输

传送门:github

因为代码和现实的一些差异,可能显得有些地方设计的有些丑陋了,有想法的可以多提提意见~

首先是主机层 

package model

import "fmt"

//主机
type Computer struct {
	Ip string//ip
	Mac string//mac地址
	SubnetMask string//子网掩码
	DefaultGateway string//默认网关
	Arp map[string]string//arp 用于缓存ip到mac的关系
	ExchangeBoard *ExchangeBoard//所属的交换机
	Port int//所属的交换机的端口
	MsgCh chan Message //监听消息
	MsgList []Message//消息存储下来
}
func (c *Computer)IsSubnet(ip string)bool{
	return isIpSubNet(c.Ip,ip,c.SubnetMask)
}
func (c *Computer)NewMessage(ip,msg string)Message{
	mac:=""
	if c.IsSubnet(ip){//同一个子网走正常ip
		mac=c.Arp[ip]
	}else{
		mac=c.Arp[c.DefaultGateway]//不是同一个子网走默认网关
	}
	return Message{
		Head: MessageHead{
			FromMac:  c.Mac,
			ToMac: mac,
			FromIp:   c.Ip,
			ToIp:     ip,
			FromPort: c.Port,
		},
		Body: msg,
	}
}
func (c *Computer)SendMessage(message Message){//只知道目标ip
	c.ExchangeBoard.SendMessage(message)//通过交换机发送消息
}
func (c *Computer)Wait(){
	go func() {
		for{
			select {
			case v:=<-c.MsgCh:
				if v.Head.ToMac==""{//第一次,不知道我的mac,需要通过ip验证
					if v.Head.ToIp==c.Ip{
						if v.Head.IsArpReq{//arp请求
							c.Arp[v.Head.FromIp]=v.Head.FromMac//更新自己的arp表
							message:=Message{
								Head: MessageHead{
									FromMac: c.Mac,
									ToMac:   v.Head.FromMac,
									FromIp:  c.Ip,
									ToIp:    v.Head.FromIp,
									IsArpRes: true,
									FromPort: c.Port,
								},
							}
							c.MsgList=append(c.MsgList,v)//不确定要不要发送

							c.ExchangeBoard.SendMessage(message)//通过交换机响应
						}
					}
				} else if v.Head.ToMac==c.Mac{//收到消息,确实是自己的mac
					if v.Head.IsArpRes{
						wait=true
						c.Arp[v.Head.FromIp]=v.Head.FromMac//只更新自己的arp表即可
					}else{
						fmt.Println(fmt.Sprintf("[%s]电脑收到[%s]的消息啦:%s",c.Mac,v.Head.FromMac,v.Body))
						//正常请求,接收即可
						c.MsgList=append(c.MsgList,v)
					}
				}
			}
		}
	}()
}

然后是交换机:

 

package model

import (
	"fmt"
	"time"
)

//交换机
type ExchangeBoard struct {
	MacPort map[string]int//mac与端口映射表
	PortList PortSenderM//所有的端口 端口后可能是电脑,也可能是交换机
	//ExchangeBoardPortM ExchangeBoardPortM//交换机与别的交换机连接的端口,比如A的端口上是B  就是A.ExchangeBoardPortM= [B]1
	Router *Router//路由器
	Port int //交换机与路由器连接的端口
}
func NewExchangeBoard()*ExchangeBoard{
	return &ExchangeBoard{
		MacPort:  map[string]int{},
		PortList: PortSenderM{},
	}
}
func (e *ExchangeBoard)NewMessage(ip,msg string)Message{//交换机本身没有自己的初始消息
	return Message{}
}
func (exchangeBoard *ExchangeBoard)SetSender(portSenderM PortSenderM){
	for k,v:=range portSenderM{
		exchangeBoard.PortList[k]=v
	}
}
func (exchangeBoard *ExchangeBoard)GetSender(port int)Sender{
	return exchangeBoard.PortList[port]
}
var wait bool
func (e *ExchangeBoard)SendMessage(message Message){
	e.MacPort[message.Head.FromMac]=message.Head.FromPort//更新mac与端口映射表,每次都更新,防止机器换端口
	if message.Head.ToMac==""{//目标mac为空,发送广播
		message.Head.IsArpReq=true
		for k,v:=range e.PortList{
			if k==message.Head.FromPort && !message.Head.IsFromRouter{//发送方端口不对其发送消息 且不是从路由器来的消息
				continue
			}
			e.commonSendMessage(v,message)
		}
		//这一段主要是为了解决同一个交换机下的机器不用通过路由器广播,如果同交换机下找不到,再走路由器
		time.Sleep(1*time.Second)
		if wait{
			wait=false
			return
		}
		if !message.Head.IsFromRouter{
			message.Head.ToMac=e.Router.PortMacM[e.Port].Mac
			e.Router.SendMessage(message)//给路由器也发
		}
	}else{//有mac
		if port,ok:=e.MacPort[message.Head.ToMac];ok{
			if port==-1{
				e.Router.SendMessage(message)
			}else{
				e.commonSendMessage(e.PortList[port],message)
			}
		}else{//广播,更新mac地址表,不存在了
			fmt.Println("?")
		}
	}
}
//给指定端口机器发送消息
func (e *ExchangeBoard)commonSendMessage(v interface{},message Message){
	switch v.(type) {
	case *Computer:
		v.(*Computer).MsgCh<-message
	//case *ExchangeBoard:
	//	message.Head.FromPort=v.(*ExchangeBoard).ExchangeBoardPortM[e]//交换机的的fromport需要替换
	//	v.(*ExchangeBoard).SendMessage(message)

	}
}

然后是路由器:

 

package model

import (
	"strings"
)

//路由器
type Router struct {
	IpPort IpPort//ip网段与端口映射表  路由表 分为静态和动态
	Arp map[string]string//arp 用于缓存ip到mac的关系
	PortExchangeBoardM PortExchangeBoardM //路由器端口列表
	PortMacM PortMacM//保存端口和mac,ip的关系 初始化就有
}
type NextPort struct {
	Next *Router//下一跳
	Port int//端口
}
type MacAndIp struct {
	Mac string
	Ip string
}

func (r *Router)SendMessage(message Message){
	if message.Head.IsArpReq || message.Head.IsArpRes{
		r.Arp[message.Head.FromIp]=message.Head.FromMac
	}
	port:=r.GetPortFromIp(message.Head.ToIp)//根据路由表找到ip对应的端口
	message.Head.FromMac=r.PortMacM[port].Mac//来源mac置为路由器发送端口的mac
	message.Head.ToMac=r.Arp[message.Head.ToIp]//需要根据arp得到ip对应的mac
	message.Head.IsFromRouter=true
	message.Head.FromPort=-1//-1代表在交换机上 路由器没有插在正常的lan口上,而是wan口,这里用-1表示
	r.PortExchangeBoardM[port].SendMessage(message)//向指定端口发送消息
}

func (r *Router)GetPortFromIp(ip string)int{
	for k,v:=range r.IpPort{
		ipList:=strings.Split(k,"/")
		if len(ipList)==0{
			panic("error")
		}
		if isIpSubNet(ip,ipList[0],SubNetM[ipList[1]]){
			if v.Next!=nil{
				return v.Next.GetPortFromIp(ip)
			}
			return v.Port
		}
	}
	panic("数据不可达")
}

还有一些其他的方法先不展示了,直接初始化各个设备,插上设备:

package main

import (
	"mac-ip/model"
	"time"
)
var exchangeBoard1,exchangeBoard2,exchangeBoard3 *model.ExchangeBoard
var senderM1=model.PortSenderM{
	0:&model.Computer{
		Ip:             "192.168.0.1",
		Mac:            "AAAA",
		SubnetMask:     "255.255.255.0",
		DefaultGateway: "192.168.0.254",
		ExchangeBoard:exchangeBoard1,
		Port: 0,
	},1:&model.Computer{
		Ip:             "192.168.0.2",
		Mac:            "BBBB",
		SubnetMask:     "255.255.255.0",
		DefaultGateway: "192.168.0.254",
		ExchangeBoard: exchangeBoard1,
		Port: 1,
	},
}
var senderM2=model.PortSenderM{
	0:&model.Computer{
		Ip:             "192.168.1.1",
		Mac:            "CCCC",
		SubnetMask:     "255.255.255.0",
		DefaultGateway: "192.168.1.254",
		ExchangeBoard: exchangeBoard2,
		Port: 0,
	},1:&model.Computer{
		Ip:             "192.168.1.2",
		Mac:            "DDDD",
		SubnetMask:     "255.255.255.0",
		DefaultGateway: "192.168.1.254",
		ExchangeBoard: exchangeBoard2,
		Port: 1,
	}}
var senderM3=model.PortSenderM{
	0:&model.Computer{
		Ip:             "192.168.2.1",
		Mac:            "EEEE",
		SubnetMask:     "255.255.255.0",
		DefaultGateway: "192.168.2.254",
		ExchangeBoard: exchangeBoard3,
		Port: 0,
	},1:&model.Computer{
		Ip:             "192.168.2.2",
		Mac:            "FFFF",
		SubnetMask:     "255.255.255.0",
		DefaultGateway: "192.168.2.254",
		ExchangeBoard: exchangeBoard3,
		Port: 1,
	}}
var router=&model.Router{
	IpPort:             model.IpPort{
		"192.168.0.0/24":&model.NextPort{
			Next: nil,
			Port: 0,
		},
		"192.168.1.0/24":&model.NextPort{
			Next: nil,
			Port: 1,
		},
	},
	Arp:                map[string]string{},
	PortExchangeBoardM: nil,
	PortMacM:           model.PortMacM{
		0:model.MacAndIp{
			Mac:        "ABAB",
			Ip:         "192.168.0.254",
		},1:model.MacAndIp{
			Mac:        "CDCD",
			Ip:         "192.168.1.254",
		},
	},
}
func init(){
	exchangeBoard1,exchangeBoard2,exchangeBoard3=model.NewExchangeBoard(),model.NewExchangeBoard(),model.NewExchangeBoard()
	exchangeBoard1.SetSender(senderM1)
	exchangeBoard2.SetSender(senderM2)
	exchangeBoard3.SetSender(senderM3)
	router.PortExchangeBoardM=model.PortExchangeBoardM{
		0:exchangeBoard1,
		1:exchangeBoard2,
	}

	exchangeBoard1.Router=router
	exchangeBoard1.Port=0
	exchangeBoard2.Router=router
	exchangeBoard2.Port=1
	initComputer(exchangeBoard1,exchangeBoard2,exchangeBoard3)

	wait(exchangeBoard1,exchangeBoard2,exchangeBoard3)//监听消息
}
func main(){
	computer,_,computer3,_:=exchangeBoard1.GetSender(0),exchangeBoard1.GetSender(1),exchangeBoard2.GetSender(0),exchangeBoard3.GetSender(0)
	computer.SendMessage(computer.NewMessage("192.168.0.2","arp"))
	computer.SendMessage(computer.NewMessage("192.168.1.1","arp"))
	//computer.SendMessage(computer.NewMessage("192.168.2.1","arp"))
	time.Sleep(1*time.Second)
	computer.SendMessage(computer.NewMessage("192.168.0.2","hello"))
	//computer.SendMessage(computer.NewMessage("192.168.0.2","hello2"))
	//computer2.SendMessage(computer2.NewMessage("192.168.0.1","hello2"))
	computer.SendMessage(computer.NewMessage("192.168.1.1","hello3"))
	computer3.SendMessage(computer3.NewMessage("192.168.0.1","hello4"))
	for{

	}
}
func wait(ExchangeBoardList ...*model.ExchangeBoard){
	for i,_:=range ExchangeBoardList{
		go func(i int) {
			for _,v:=range ExchangeBoardList[i].PortList{
				if _,ok:=v.(*model.Computer);ok{
					v.(*model.Computer).Wait()
				}
			}
		}(i)
	}
}
func initComputer(exchangeBoardList ...*model.ExchangeBoard){
	for _,exchangeBoard:=range exchangeBoardList{
		for _,v:=range exchangeBoard.PortList{
			if computer,ok:=v.(*model.Computer);ok{
				computer.ExchangeBoard=exchangeBoard
				computer.MsgCh=make(chan model.Message,20)
				computer.Arp=map[string]string{}
			}
		}
	}
}

 

跑起来:

没毛病

目录结构是这样的:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值