func (c *Client) TunReadRoutine() {
	for c.isAlive {
		for c.isConnected {
			packet := make([]byte, packetSize)
			n, err := c.intf.Read(packet)
			if err != nil {
				continue
			}
			p := &NetPacket{
				Packet: packet[:n],
			}
			c.incomingChannel <- p
		}
		time.Sleep(waitDelay)
	}
}
TunReadRoutine reads from TUN interface and writes on incomingChannel
package client
import (
	"errors"
	"github.com/lf-edge/edge-home-orchestration-go/internal/common/logmgr"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net"
	"sync"
	"time"
	restclient "github.com/lf-edge/edge-home-orchestration-go/internal/restinterface/client"
	networkhelper "github.com/lf-edge/edge-home-orchestration-go/internal/common/networkhelper"
	"github.com/lf-edge/edge-home-orchestration-go/internal/controller/discoverymgr/mnedc/connectionutil"
	"github.com/lf-edge/edge-home-orchestration-go/internal/controller/discoverymgr/mnedc/tunmgr"
	"github.com/songgao/water"
)
const (
	packetSize  = 1024
	channelSize = 200
	logTag = "[mnedcclient]"
)
type NetPacket struct {
	Packet []byte
}
type Client struct {
	conn            net.Conn
	incomingChannel chan *NetPacket
	outgoingChannel chan *NetPacket
	isAlive         bool
	isConnected     bool
	isSecure        bool
	intf            *water.Interface
	virtualIP       net.IP
	netMask         *net.IPNet
	mutexLock       sync.Mutex
	serverIP        string
	serverPort      string
	deviceID        string
	configPath      string
	clientAPI       restclient.Clienter
}
var (
	clientIns      *Client
	tunIns         tunmgr.Tun
	networkUtilIns connectionutil.NetworkUtil
	networkIns     networkhelper.Network
	log = logmgr.GetInstance()
)
const (
	waitDelay                = 150 * time.Millisecond
	retryDelay               = 5 * time.Second
	mnedcBroadcastServerPort = 3333
)
type MNEDCClient interface {
	Run()
	CreateClient(string, string, bool) (*Client, error)
	Close() error
	StartSendRoutine()
	StartRecvRoutine()
	NotifyClose()
	ConnectionReconciled()
	HandleError(error)
	ParseVirtualIP(string) error
	TunReadRoutine()
	TunWriteRoutine()
	NotifyBroadcastServer(configPath string) error
	SetClient(clientAPI restclient.Clienter)
}
func init() {
	clientIns = &Client{}
	tunIns = tunmgr.GetInstance()
	networkUtilIns = connectionutil.GetInstance()
	networkIns = networkhelper.GetInstance()
}
type serverConf struct {
	ServerIP string `yaml:"server-ip"`
	Port     string `yaml:"port"`
}
func GetInstance() MNEDCClient {
	return clientIns
}
func (c *Client) CreateClient(deviceID, configPath string, isSecure bool) (*Client, error) {
	logPrefix := logTag + "[CreateClient]"
	for {
		servAddr, servPort, err := getMNEDCServerAddress(configPath)
		if err != nil {
			return nil, errors.New("Cannot read config file, " + err.Error())
		}
		conn, err := networkUtilIns.ConnectToHost(servAddr, servPort, isSecure)  
		if err != nil {
			log.Println(logPrefix, "Dial failed", err.Error(), ", retrying")
			time.Sleep(retryDelay)
			continue
		}
		buf := []byte(deviceID)  
		err = networkUtilIns.WriteTo(conn, buf)
		if err != nil {
			log.Println(logPrefix, "Secret Write error", err.Error(), ", retrying")
			conn.Close()
			time.Sleep(retryDelay)
			continue
		}
		log.Println("Write Successful")
		readBufSize, readBuf, err := networkUtilIns.ReadFrom(conn)
		if err != nil {
			log.Println(logPrefix, "Read Error: ", err.Error(), ", retrying")
			time.Sleep(retryDelay)
			conn.Close()
			continue
		}
		intf, err := tunIns.CreateTUN()
		if err != nil {
			c.Close()
			return nil, errors.New(logPrefix + " TUN error: " + err.Error())
		}
		c.conn = conn
		c.incomingChannel = make(chan *NetPacket, channelSize)
		c.outgoingChannel = make(chan *NetPacket, channelSize)
		c.isAlive = true
		c.isConnected = true
		c.intf = intf
		c.serverIP = servAddr
		c.serverPort = servPort
		c.deviceID = deviceID
		c.isSecure = isSecure
		c.configPath = configPath
		err = c.ParseVirtualIP(string(readBuf[0:readBufSize]))  
		if err == nil {
			setIPError := tunIns.SetTUNIP(c.intf.Name(), c.virtualIP, c.netMask, true)
			if setIPError != nil {
				err = setIPError
			}
			setStatusError := tunIns.SetTUNStatus(c.intf.Name(), true, true)
			if setStatusError != nil {
				err = setStatusError
			}
		}
		if err != nil {
			c.Close()
		}
		return c, err
	}
}
func (c *Client) Run() {
	go c.StartSendRoutine()
	go c.StartRecvRoutine()
	go c.TunReadRoutine()
	go c.TunWriteRoutine()
}
func (c *Client) StartSendRoutine() {
	for c.isAlive {
		for c.isConnected {
			pkt, ok := <-c.incomingChannel
			if !ok {
				break
			}
			err := networkUtilIns.WriteTo(c.conn, pkt.Packet)
			if err != nil {
				log.Println("error in send: ", err)
				c.mutexLock.Lock()
				c.isConnected = false
				c.mutexLock.Unlock()
				c.HandleError(err)
				break
			}
		}
		time.Sleep(waitDelay)
		dropSendBuffer(c.incomingChannel)
	}
}
func (c *Client) StartRecvRoutine() {
	for c.isAlive {
		for c.isConnected {
			_, vpnbuf, err := networkUtilIns.ReadFrom(c.conn)
			if err != nil {
				log.Println("Read error", err.Error())
				c.mutexLock.Lock()
				c.isConnected = false
				c.mutexLock.Unlock()
				c.HandleError(err)
				break
			}
			c.outgoingChannel <- &NetPacket{Packet: vpnbuf}
		}
		time.Sleep(waitDelay)
	}
}
func dropSendBuffer(buffer chan *NetPacket) {
	for {
		select {
		case <-buffer:
		default:
			return
		}
	}
}
func (c *Client) ParseVirtualIP(parameters string) error {
	virtualIP, virtualNetMask, err := net.ParseCIDR(parameters + "/24")
	if err != nil {
		return errors.New(parameters + "/24" + "is Invalid network/mask " + err.Error())
	}
	c.netMask = virtualNetMask
	c.virtualIP = virtualIP
	return nil
}
func (c *Client) Close() error {
	if !c.isAlive {
		return errors.New("Client not alive")
	}
	c.isAlive = false
	var err error
	if c.conn != nil {
		err = c.conn.Close()
	}
	if c.intf != nil {
		err = c.intf.Close()
	}
	c.NotifyClose()
	return err
}
func (c *Client) HandleError(err error) {
	logPrefix := "[hadError]"
	log.Println(logTag, logPrefix, err.Error(), ", Connection problem detected. Re-connecting.")
	if !c.isAlive {
		return
	}
	c.mutexLock.Lock()
	defer c.mutexLock.Unlock()
	c.isConnected = false
	c.conn.Close()
	if c.intf != nil {
		c.intf.Close()
	}
	c.NotifyClose()
	deviceID := c.deviceID
	for {
		servAddr, servPort, err := getMNEDCServerAddress(c.configPath)
		if err != nil {
			log.Println(errors.New("Cannot read config file," + err.Error()))
			return
		}
		conn, err := networkUtilIns.ConnectToHost(servAddr, servPort, c.isSecure)  
		if err != nil {
			log.Println(logPrefix, "Dial failed", err.Error(), ", retrying")
			time.Sleep(retryDelay)
			continue
		}
		buf := []byte(deviceID)
		err = networkUtilIns.WriteTo(conn, buf)
		if err != nil {
			log.Println(logPrefix, "Secret Write error", err.Error(), ", retrying")
			conn.Close()
			time.Sleep(retryDelay)
			continue
		}
		n, readBuf, err := networkUtilIns.ReadFrom(conn)
		if err != nil {
			log.Println(logPrefix, "Read Error: ", err.Error(), ", retrying")
			time.Sleep(retryDelay)
			conn.Close()
			continue
		}
		intf, err := tunIns.CreateTUN()
		if err != nil {
			log.Println(logPrefix, "TUN error:", err.Error())
			return
		}
		c.intf = intf
		err = c.ParseVirtualIP(string(readBuf[0:n]))  
		if err != nil {
			log.Println(logPrefix, "Parse Virtual IP error:", err.Error())
			return
		}
		err = tunIns.SetTUNIP(c.intf.Name(), c.virtualIP, c.netMask, true)
		if err != nil {
			log.Println(logPrefix, "TUN error:", err.Error())
			return
		}
		err = tunIns.SetTUNStatus(c.intf.Name(), true, true)
		if err != nil {
			log.Println(logPrefix, "TUN error:", err.Error())
			return
		}
		c.serverIP = servAddr
		c.serverPort = servPort
		c.conn = conn
		c.isConnected = true
		time.Sleep(3 * time.Second)
		go c.ConnectionReconciled()
		return
	}
}