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");
}));
}
}
}