1. 程式人生 > 實用技巧 >使用protobuf c實現TCP網路資料傳輸

使用protobuf c實現TCP網路資料傳輸

1.自定義資料欄位型別:

enum UserStatus {
        UNKNOWN = 0;
        IDLE = 1;
        BUSY = 2;
}
message UserInfo {
        required string name = 1;
        required uint32 age = 2;
        optional string phone = 3;
        required UserStatus stat = 4;
        optional string email = 5;
}

2. 生成.c 和.h檔案:

protoc-c --c_out=./ UserInfo.proto

3. 編寫伺服器端程式碼:

#include "UserInfo.pb-c.h"
#include <stdio.h>
#include <sys/socket.h>
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

struct msg_proto_header
{
	uint8_t version;
	uint16_t command;
	uint32_t length;
};
enum MSG_COMMOND
{
	LOGIN_REQUEST,
};

int main()
{
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
		return -1;
	struct sockaddr_in local_addr = {0};
	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(9999);
	local_addr.sin_addr.s_addr = INADDR_ANY;
	
	int ret = bind(fd,(struct sockaddr *)&local_addr, sizeof(local_addr));
	if(ret < 0)
		return -1;
	
	listen(fd, 5);
	struct sockaddr_in client_addr = {0};
	int len = sizeof(client_addr);
	int clientfd = accept(fd,(struct sockaddr *)&client_addr, &len);
	if(clientfd < 0)
		return -1;
	
	int read_size = 0;
	char buf[1024];
	struct msg_proto_header header;
	while(1)
	{
		memset(&header, 0, sizeof(struct msg_proto_header));
		memset(buf, 0, 1024);
		read_size = recv(clientfd, &header, sizeof(struct msg_proto_header), 0);
		if(read_size < 0){
			perror("recv fail:");
			break;
		}
	
		if(header.command == LOGIN_REQUEST && header.length > 0)
		{
			read_size = recv(clientfd, buf, header.length, 0);
			if(read_size < 0){
				perror("recv fail:");
				break;
			}
			UserInfo * userInfo = user_info__unpack(NULL, header.length, buf);
			if(!userInfo){
				printf("user_information__unpack is fail!\n");
				continue;
			}
			printf("Unpack: %s %d %d\n", userInfo->name, userInfo->age, userInfo->stat);
			user_info__free_unpacked(userInfo, NULL);
		}
	}
	close(fd);
	return 0;
}

4. 編寫客戶端程式碼:

#include "UserInfo.pb-c.h"
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

struct msg_proto_header
{
	uint8_t version;
	uint16_t command;
	uint32_t length;
};
enum MSG_COMMOND
{
	LOGIN_REQUEST,
};

int main()
{
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
		return -1;
		
	struct sockaddr_in server_addr = {0};
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(9999);
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	int ret = connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if(ret < 0)
	{
		printf("connect fail!\n");
		return -1;
	}
	
	uint8_t buffer[1024] = {0};
	UserInfo user;
	user_info__init(&user);
    	user.name = "dabai";
    	user.age = 18;
    	user.stat = USER_STATUS__IDLE;
	
	size_t length = user_info__pack(&user, buffer);
	struct msg_proto_header msg_header;
	msg_header.version = 0x01;
	msg_header.command = LOGIN_REQUEST;
	msg_header.length = length;
	
	//每隔5s傳送一次資料包
	while(1)
	{
		send(fd, &msg_header, sizeof(struct msg_proto_header), 0);
		send(fd, buffer, length, 0);
		sleep(5);
	}
	close(fd);
	return 0;
}

5.驗證結果:

啟動server: ./server
啟動client: ./client

觀察server 效果:
Unpack: dabai 18 1
Unpack: dabai 18 1