golang tcp客户端断开自动恢复实现(附编译为dll,C#调用)

package  main

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"time"
)

func main(){
  //开一个goroutine,做连接,并启动接收,如果连接断开,接收报错,则尝试恢复连接
  go Link()
  //来一个goroutine,做心跳,如果不发心跳,拔掉网线不会触发接收异常
  go BeatHeart()
  //在启动的goroutine中做发送操作
	input := bufio.NewScanner(os.Stdin)
	for{
		input.Scan()
		str := input.Text()
		fmt.Println("你输入的是:", str)
		sendBuf := []byte(str)
		if conn == nil{
			fmt.Println("conn is nil")
			continue
		}
		n, err := conn.Write(sendBuf[:])
		if err != nil {
			fmt.Println(err)
		}else{
			fmt.Println("send:", n)
		}
	}
}

//全局tcp连接对象
var conn net.Conn

//负责连接以及连接恢复
func Link(){
	hostInfo := "192.168.1.100:9090"
	for{
		var err error
		conn, err = net.Dial("tcp", hostInfo)
		fmt.Print("connect (")
		if err != nil {
			fmt.Print(") fail")
		}else{
			fmt.Println(") ok")
			defer func() {
				conn.Close()
				conn = nil
			}()
			doTask(conn)
		}
		time.Sleep(3 * time.Second)
	}
}

//心跳 每8秒发送一个包
func BeatHeart(){
	for{
		if conn!=nil{
			conn.Write([]byte("beatHeart"))
		}
		time.Sleep(time.Second * 8)
	}
}

//接收
func doTask(conn net.Conn)  {
	var buf [128]byte
	for{
		conn.SetReadDeadline(time.Now().Add(time.Second * 10))
		n , err := conn.Read(buf[:])
		if err != nil{
			fmt.Println("接收错误,进行重连:", err)
			break
		}
		fmt.Println("接收到:", string(buf[:n]))
	}
}

没有给全局变量加锁,正式项目需要加上。这里只维护一个tcp客户端对象,如果改造成dll,需要多个客户端对象的话,则需要设置一个字典或数组,进行维护。并在连接断开、连接恢复的时候发送通知。代码很简洁......

 

服务端简单实现:

package main

import (
	"fmt"
	"net"
)

func main(){
	listener, err := net.Listen("tcp", "0.0.0.0:9090")
	if err != nil{
		fmt.Printf("listen fail, err:%v\n", err)
		return
	}

	for{
		conn, err := listener.Accept()
		if err != nil{
			fmt.Printf("accept fail, err:%v\n", err)
			continue
		}

		go process(conn)
	}
}

func process(conn net.Conn){
	defer func() {
		conn.Close()
		fmt.Println("连接断开...")
	}()
	for{
		var buf [128]byte
		n, err := conn.Read(buf[:])
		if err != nil{
			fmt.Printf("read from connect failed, err:%v\n", err)
			break
		}
		str := string(buf[:n])
		fmt.Printf("receive from client, data:%v\n", str)
		conn.Write(buf[:n])
	}
}

改为dll,并在C#中进行调用:

//自动恢复Tcp客户端封装
package TcpClient

import (
	"fmt"
	"net"
	"time"
)

type AutoRepairTcpClient struct {
	Conn net.Conn
	ServerIp string
	Port int
	NotifyFun func(id int)
	ReceiveFun func(data []byte)
}

func (obj *AutoRepairTcpClient) SetAddr(ip string, port int){
	obj.ServerIp = ip
	obj.Port = port
}

func (obj *AutoRepairTcpClient)Start(){
	go obj.Link()
	go obj.BeatHeart()
}

func (obj *AutoRepairTcpClient)Link(){
	hostInfo := fmt.Sprintf("%s:%d", obj.ServerIp, obj.Port)
	for{
		conn, err := net.Dial("tcp", hostInfo)
		fmt.Print("connect()")
		if err != nil{
			fmt.Print("fail!\n")
			obj.NotifyFun(0)
		}else{
			fmt.Print("Ok\n")
			obj.Conn = conn
			obj.NotifyFun(1)
			defer func(){
				obj.Conn.Close()
				obj.Conn = nil
			}()
			obj.ReceiveMsg()
		}
		time.Sleep(3 * time.Second)
	}
}

func (obj *AutoRepairTcpClient)BeatHeart(){
	for{
		if obj.Conn!=nil{
			obj.Conn.Write([]byte("beatHeart"))
		}
		time.Sleep(time.Second * 8)
	}
}

func (obj *AutoRepairTcpClient)ReceiveMsg()  {
	var buf [128]byte
	for{
		obj.Conn.SetReadDeadline(time.Now().Add(time.Second * 10))
		n , err := obj.Conn.Read(buf[:])
		if err != nil{
			fmt.Println("接收错误,进行重连:", err)
			obj.NotifyFun(0)
			break
		}
		fmt.Println("接收到:", string(buf[:n]))
		obj.ReceiveFun(buf[:n])
	}
}

func (obj *AutoRepairTcpClient)Write(buf []byte) bool  {
	if obj.Conn == nil{
		return false
	}
	n, err := obj.Conn.Write(buf)
	if err != nil {
		fmt.Println(err)
		return false
	}else{
		fmt.Println("send:", n)
		return true
	}
}

dll函数:

package main

import "C"
import (
	"fmt"
)
import (
	"./TcpClient"
	"./github.com/lxn/win"
	"unsafe"
)

//export FuncType
type FuncType func()

//export PrintBye
func PrintBye(){
	fmt.Println("From dll:Bye!")
}

//export Sum
func Sum(a int, b int) int{
	return a + b;
}

var _handle uintptr

//export SetMainHandle
func SetMainHandle(handle uintptr){
	_handle = handle
}

//export Echo
func Echo(msg string){
	fmt.Println(msg)
	var a [10]byte
	for i := 0; i < 10 ; i++  {
		a[i] = byte(i)
	}
	wparam := &a[0]
	win.SendMessage(win.HWND(_handle), uint32(win.WM_USER + 1000), uintptr(unsafe.Pointer(wparam)), 10)
}

//export BytesTest
func BytesTest(data []byte){
	for i := 0;i < len(data) ;i++  {
		fmt.Println(data[i])
	}
}

//export funVar
var funVar func(key string)

var tcpClient *TcpClient.AutoRepairTcpClient

//export StartTcpClient
func StartTcpClient(ip string, port int)  {
	tcpClient = &TcpClient.AutoRepairTcpClient{}
	tcpClient.NotifyFun = func(id int) {
		win.SendMessage(win.HWND(_handle), uint32(win.WM_USER + 1001), uintptr(id), 0)
	}
	tcpClient.ReceiveFun = func(data []byte) {
		fmt.Println(data)
		fmt.Println(string(data))
		var a [128]byte
		for i := 0; i < len(data); i++  {
			a[i] = data[i]
		}
		w := &a[0]
		win.SendMessage(win.HWND(_handle), uint32(win.WM_USER + 1002), uintptr(unsafe.Pointer(w)), uintptr(len(data)))
	}
	tcpClient.SetAddr(ip, port)
	tcpClient.Start()
}

//export Send
func Send(data []byte){
	fmt.Println(data)
	tcpClient.Write(data[:])
}

func main(){
	// Need a main function to make CGO compile package as C shared library
}

C#调用:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace CSharpTest
{
    public struct GoString
    {
        public string Value { get; set; }
        public int Length { get; set; }

        public static implicit operator GoString(string s)
        {
            var res = new GoString() { Value = s, Length = s.Length };
            //res.Value.Append(s);
            return res;
        }

        public static implicit operator string(GoString s) => s.Value.ToString();
    }

    /// 
    /// typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
    /// 
    //[StructLayout(LayoutKind.Sequential)]
    public struct GoSlice
    {
        public IntPtr Data;

        public long Len;

        public long Cap;
    }

    public partial class Form1 : Form
    {

        [DllImport("exportgo.dll", EntryPoint = "PrintBye", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe void PrintBye();

        [DllImport("exportgo.dll", EntryPoint = "Sum", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe int Sum(int a, int b);

        [DllImport("exportgo.dll", EntryPoint = "Echo", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
        static extern unsafe void Echo(GoString msg);

        [DllImport("exportgo.dll", EntryPoint = "SetMainHandle", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
        static extern unsafe void SetMainHandle(UIntPtr handle);

        //extern void BytesTest(GoSlice p0);
        [DllImport("exportGo.dll", EntryPoint = "BytesTest", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
        static extern unsafe void BytesTest(GoSlice slice);

        //extern void StartTcpClient(GoString p0, GoInt p1);
        [DllImport("exportgo.dll", EntryPoint = "StartTcpClient", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
        static extern unsafe void StartTcpClient(GoString ip, int port);

        //extern void Send(GoSlice p0);
        [DllImport("exportgo.dll", EntryPoint = "Send", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
        static extern unsafe void Send(GoSlice p0);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public delegate void CallBackFun();

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            PrintBye();
            Debug.WriteLine(Sum(22, 33));
        }

        private void button2_Click(object sender, EventArgs e)
        {
            CallBackFun f = new CallBackFun(callback);
            var p = Marshal.GetFunctionPointerForDelegate(f);
            Echo("agc");
        }

        private void callback()
        {
            Debug.WriteLine("");
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            SetMainHandle((UIntPtr)(Handle.ToInt64()));
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            if (m.Msg == 1024 + 1000)
            {
                var bytes = new byte[(int)m.LParam];
                Marshal.Copy(m.WParam, bytes, 0, (int)m.LParam);
                MessageBox.Show("go 触发..." + m.LParam + " " + bytes.Aggregate("", (sum, i) => sum = sum + "," + i.ToString()));
            }
            else if (m.Msg == 1024 + 1001)
            {
                var type = (int)m.WParam;
                ShowLog(type == 0 ? "通信故障" : "通信恢复");
            }
            if (m.Msg == 1024 + 1002)
            {
                var bytes = new byte[(int)m.LParam];
                Marshal.Copy(m.WParam, bytes, 0, (int)m.LParam);
                var str = Encoding.UTF8.GetString(bytes);
                MessageBox.Show("接收到..." + str);
            }
        }

        //byte[]转换为Intptr
        public static IntPtr BytesToIntptr(byte[] bytes)
        {
            int size = bytes.Length;
            IntPtr buffer = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.Copy(bytes, 0, buffer, size);
                return buffer;
            }
            finally
            {
                //Marshal.FreeHGlobal(buffer);
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            var p = new GoSlice { Len = 3, Cap = 3 };
            var data = new byte[3];
            data[0] = 2;
            data[1] = 3;
            data[2] = 4;
            p.Data = BytesToIntptr(data);
            BytesTest(p);
            Marshal.FreeHGlobal(p.Data);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            GoString addr = new GoString { Value = "127.0.0.1", Length = 9 };
            StartTcpClient(addr, 9090);
        }

        private void button5_Click(object sender, EventArgs e)
        {
            var p = new GoSlice { Len = 3, Cap = 3 };
            var data = Encoding.UTF8.GetBytes("hello world");
            p.Len = data.Length;
            p.Cap = data.Length;
            p.Data = BytesToIntptr(data);
            Send(p);
            Marshal.FreeHGlobal(p.Data);
        }

        private void ShowLog(string log)
        {
            BeginInvoke(new Action(() =>
            {
                textBox1.AppendText(log + "\r\n");
            }));
        }
    }
}

golang tcp客户端断开自动恢复实现(附编译为dll,C#调用)_第1张图片

你可能感兴趣的:(go)