c语言自定义tcp协议实现socket通信(windows版本)

    前面一篇博客介绍了mac/linux下通过C语言自定义协议实现socket通信的示例,因为大部分api与windows还有很多区别,这里就特意把windows下的tcp通信实例给介绍一下。

    无论是linux,还是windows,其实c语言都是默认小端序,这个需要注意,还有一个就是结构体的内存对齐问题也是存在的,所以协议结构体我们需要注意他的大小就行了,在进行拷贝的时候,不能直接使用sizeof来计算发送数据的长度。

    因为是windows,所以我们可以通过网络小助手来模拟一个服务端,而不需要通过netcat指令了,其实都一样,我的windows专业版好像不支持netcat也就是nc,当你运行nc指令,系统会默认把他干掉,很遗憾。

    协议中最重要的部分,就是数据体,这个部分严格来说会不一样,这里结合了cJSON这个库来做数据json格式化。借助了rand函数来做一个随机数。所以会比上一个示例复杂一些。

    这里多说一句,就是cJSON这个库是开源免费的,可以直接将头文件和cpp源文件加入项目中就可以使用了。

    show me the code:

#include 
#include 
#include 
#include 
#include 
#include "cJSON.h"
#pragma comment(lib,"ws2_32.lib")
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
#define PAYLOAD_SIZE 1024
typedef struct _pktdata{
	uint16_t flag;    //帧头标识符 0x5aa5
	uint16_t version; //版本       0x0001
	uint16_t type;    //类型       0x0001
	uint16_t reserved; //保留位    0xffff
	uint32_t length;   //数据长度  int
	uint8_t payload[PAYLOAD_SIZE];//数据体 可变长度
	uint8_t checksum;  //校验位  本例中没有设置
}pktdata;
void createPacket(pktdata* data){
	data->flag = (0x5aa5);
	data->version = (0x0001);
	data->type = (0x0001);
	data->reserved = (0xffff);
	//build payload
	cJSON* root = cJSON_CreateObject();
	int random = rand();
	char name[20];
	sprintf(name,"xxx%d",random);
	cJSON_AddItemToObject(root,"name",cJSON_CreateString(name));
	cJSON_AddItemToObject(root,"age",cJSON_CreateNumber(18));
	char* payload = cJSON_PrintUnformatted(root);
	data->length = strlen(payload);//25 -> {"name":"admin","age":18}
	printf("%s\n",payload);
	memcpy(data->payload,payload,data->length);
}
int main(int argc,char** argv){
	srand((unsigned)time(NULL));
	WORD sockVersion = MAKEWORD(2,2);
	WSADATA data;
	if(WSAStartup(sockVersion,&data)!=0){
	    return 0;
	}
	printf("start up.\n");
	SOCKET sclient = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(sclient==INVALID_SOCKET){
		printf("invalid socket!\n");
	    return 0;
	}
	struct sockaddr_in servAddr;
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(6666);
	servAddr.sin_addr.S_un.S_addr = inet_addr("172.16.5.33");
	if(connect(sclient,(struct sockaddr*)&servAddr,sizeof(servAddr))==SOCKET_ERROR){
		printf("connect error!\n");
		closesocket(sclient);
		return 0;
	}

	for(int i=0;i<10;i++){
		pktdata pd;
		createPacket(&pd);
		char sendData[1040] = {0};
		//strlen() = 3 是因为字符串数组中有0,这里不能使用strlen()来求字符串长度
		int len = pd.length + 12;
		memcpy(sendData,(void *)&pd,len); //? 如果拷贝struct _pktdata的长度,会是一个1040的长度,所以取的是payload数据长度+其余字段除去checksum长度
		send(sclient,sendData,len+1,0);//发送的时候多发送一位,是为了保证有一位留给校验位,虽然没有给它设置值
		Sleep(10);
	}
	closesocket(sclient);
	WSACleanup();
	printf("done!\n");
    return 0;
}

    这个代码,我是在visual c++6.0编辑器中运行的,同样的,在运行之前,先开启一个tcp server,这里使用网络小助手来模拟,监听本机6666端口。

    控制台打印的10条payload数据:

    c语言自定义tcp协议实现socket通信(windows版本)_第1张图片 

    小助手收到的数据hex表示:

    c语言自定义tcp协议实现socket通信(windows版本)_第2张图片

    这里数据长度会发生变化,这是模拟的一个变化的数据体,无论怎么变化,我们都需要根据这个长度去求取数据体payload的内容。数字类型也都是小端序,高位在后,低位在前,我们在计算他们真实值的时候需要注意,尤其是服务端使用java来编码的时候。

你可能感兴趣的:(c++,windows,tcp,socket,自定义协议,protocol,小端序)