Unity 服务器踩坑 C# 与C++服务器 (一)

Unity C# 与C++服务器通信踩坑


目录

Unity C# 与C++服务器通信踩坑


API与C#数据结构类型对应关系表

                                            API与C#的数据类型对应关系表

API数据类型 类型描述 C#类型 API数据类型 类型描述 C#类型
WORD 16位无符号整数 ushort CHAR 字符 char
LONG 32位无符号整数 int DWORDLONG 64位长整数 long
DWORD 32位无符号整数 uint HDC 设备描述表句柄 int
HANDLE 句柄,32位整数 int HGDIOBJ GDI对象句柄 int
UINT 32位无符号整数 uint HINSTANCE 实例句柄 int
BOOL 32位布尔型整数 bool HWM 窗口句柄 int
LPSTR 指向字符的32位指针 string HPARAM 32位消息参数 int
LPCSTR 指向常字符的32位指针 String LPARAM 32位消息参数 int
BYTE 字节 byte WPARAM 32位消息参数 int

C#代码

消息发送类

[Serializable] //序列化对象
[StructLayout(LayoutKind.Sequential, Pack = 1)] // 按1字节对齐
public class UserMsg
{
    public int messageID;
    public int clientID;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)] //限制200字节
    public byte[] message;
}

接下来是两个byte和object转换函数
 

    /// <summary>
    /// 将结构转换为字节数组
    /// </summary>
    /// <param name="obj">结构对象</param>
    /// <returns>字节数组</returns>
    public static byte[] StructToBytes(object obj)
    {
        //得到结构体的大小
        int size = Marshal.SizeOf(obj);
        //创建byte数组
        byte[] bytes = new byte[size];
        //分配结构体大小的内存空间
        IntPtr structPtr = Marshal.AllocHGlobal(size);
        //将结构体拷到分配好的内存空间
        Marshal.StructureToPtr(obj, structPtr, false);
        //从内存空间拷到byte数组
        Marshal.Copy(structPtr, bytes, 0, size);
        //释放内存空间
        Marshal.FreeHGlobal(structPtr);
        //返回byte数组
        return bytes;
    }
    public static object BytesToStruct(byte[] bytes, Type type)
    {
        //得到结构的大小
        int size = Marshal.SizeOf(type);

        //byte数组长度小于结构的大小
        if (size > bytes.Length)
        {
            //返回空
            return null;
        }
        //分配结构大小的内存空间
        IntPtr structPtr = Marshal.AllocHGlobal(size);
        //将byte数组拷到分配好的内存空间
        Marshal.Copy(bytes, 0, structPtr, size);
        //将内存空间转换为目标结构
        object obj = Marshal.PtrToStructure(structPtr, type);
        //释放内存空间
        Marshal.FreeHGlobal(structPtr);
        //返回结构
        return obj;
    }
}

客户端连接、发送、接收

 public static void setUpClient()
 {
        IPAddress ip = IPAddress.Parse(IP);  //将IP地址字符串转换成IPAddress实例
        ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//使用指定的地址簇协议、套接字类型和通信协议
        IPEndPoint endPoint = new IPEndPoint(ip, port); // 用指定的ip和端口号初始化IPEndPoint实例
        ClientSocket.Connect(endPoint);  //与远程主机建立连接

        //实例化消息类
        UserMsg u = new UserMsg();
        u.messageID= 1;
        u.clientID = 2;
        //设置编码C++为ASCII
        u.message = Encoding.ASCII.GetBytes("connect server!");
        
        Debug.Log(u.message);
        byte[] message= StructToBytes(u);
        //向服务器发送消息
        ClientSocket.Send(message);
        
        //接收回执信息
        byte[] receive = new byte[1024];
        int length = ClientSocket.Receive(receive);
        UserMsg s = (UserMsg)BytesToStruct(receive,u.GetType());
        Debug.Log(Encoding.ASCII.GetString(s.message));
 }

C++代码

结构体对齐C# Class

typedef struct
{
	int messageID;
	int clientID;
	char message[200];
}UserMsg;

处理客户端请求函数

void  talkWithClient(void *p)
{
	const char *str = "hello client";
	UserMsg u;
	SOCKET *pSock = (SOCKET*)p;
	SOCKET socka = *pSock;
	/*byte *b;
	b = (BYTE*)str;
	int i = send(socka, str, strlen(str), 0);*/
	char buff[1024];
	sockaddr_in sa = { AF_INET };
	int i;
	while (true)
	{
        //接收客户端消息
		i = recv(socka, buff, sizeof(buff), 0);

        //如果收到消息长度小于等于0说明客户端断开
		if (i <= 0)
		{
			if (GetLastError() == 10054)
				cout << htons(sa.sin_port) << "客户端退出了:" << socka << endl;
			break;
		}
		buff[i] = 0;
		
        //byte数组转化为结构体
		u = *(UserMsg*)buff;
		
		cout << u.message << endl;
		
        //回执信息
		int k = 0, j = 0;
		char p[20] = "hello client";
		while (p[k -1] != '\0')
			u.message[j++] = p[k++];
        //发送回执信息
		int i = send(socka, (char*)(&u), sizeof(UserMsg), 0);
	}
}

main

int main()
{
	WSAData wd;
	WSAStartup(0x0202, &wd);
	SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
	sockaddr_in sa = { AF_INET };
	sa.sin_port = htons(PORT);
	bind(sock, (sockaddr*)&sa, sizeof(sa));
	listen(sock, 5);
	sockaddr_in from = { AF_INET };
	int nLen = sizeof(from);
	while (true) {
		SOCKET socka = accept(sock, (sockaddr*)&from, &nLen);
		cout << inet_ntoa(sa.sin_addr) << htons(from.sin_port) << "登录了" << endl;
		_beginthread(talkWithClient, 0, &socka); //开启一个线程 talkWithClient 参数为&socka
	}
	return 0;
}

测试结果(开发环境  Unity)

猜你喜欢

转载自blog.csdn.net/qq_34204419/article/details/82529386