// someip_client.h
// 定义一些常量和结构体
#define SOMEIP_MSG_ID_SD 0xFFFF8100 // someip-sd的报文ID
#define SOMEIP_MSG_TYPE_REQUEST 0x00 // 请求类型
#define SOMEIP_MSG_TYPE_REQUEST_NO_RETURN 0x01 // 不期待响应的请求类型
#define SOMEIP_MSG_TYPE_NOTIFICATION 0x02 // 通知类型
#define SOMEIP_MSG_TYPE_RESPONSE 0x80 // 响应类型
#define SOMEIP_MSG_TYPE_ERROR 0x81 // 错误类型
// someip报文头结构体
struct someip_header {
uint32_t msg_id; // 报文ID,由服务ID、方法ID、事件ID和会话ID组成
uint32_t len; // 报文长度,不包括报文头本身
uint8_t ver; // 版本号,默认为1
uint8_t iface_ver; // 接口版本号,默认为1
uint8_t msg_type; // 报文类型
uint8_t retcode; // 返回码,仅在响应或错误类型时有效
};
// someip负载数据结构体
struct someip_payload {
uint8_t *data; // 负载数据指针
uint32_t len; // 负载数据长度
};
// someip报文段结构体
struct someip_packet {
struct someip_header header; // 报文头
struct someip_payload payload; // 负载数据
};
// someip客户端结构体
struct someip_client {
int sock; // 套接字描述符
struct sockaddr_in server_addr; // 服务器地址结构体
uint8_t session_id; // 会话ID,用于区分不同的请求或响应
};
// someip_client.c
// 实现一些函数
#include "someip_client.h"
// 创建一个someip客户端,并连接到指定的服务器地址和端口
struct someip_client *create_someip_client(char *server_ip, int server_port) {
struct someip_client *client = malloc(sizeof(struct someip_client)); // 分配内存空间
if (client == NULL) {
perror("malloc failed");
return NULL;
}
client->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 创建TCP套接字
if (client->sock < 0) {
perror("socket failed");
free(client);
return NULL
}
client->server_addr.sin_family = AF_INET; // 设置地址族为IPv4
client->server_addr.sin_port = htons(server_port); // 设置端口号
client->server_addr.sin_addr.s_addr = inet_addr(server_ip); // 设置IP地址
if (connect(client->sock, (struct sockaddr *)&client->server_addr, sizeof(client->server_addr)) < 0) { // 连接到服务器
perror("connect failed");
close(client->sock);
free(client);
return NULL;
}
client->session_id = 0; // 初始化会话ID为0
return client; // 返回客户端指针
}
// 销毁一个someip客户端,关闭套接字并释放内存空间
void destroy_someip_client(struct someip_client *client) {
if (client == NULL) {
return;
}
close(client->sock); // 关闭套接字
free(client); // 释放内存空间
}
// 构造一个someip报文段,根据参数设置报文头和负载数据
struct someip_packet *create_someip_packet(uint32_t msg_id, uint8_t msg_type, uint8_t retcode, uint8_t *data, uint32_t len) {
struct someip_packet *packet = malloc(sizeof(struct someip_packet)); // 分配内存空间
if (packet == NULL) {
perror("malloc failed");
return NULL;
}
packet->header.msg_id = htonl(msg_id); // 设置报文ID,并转换为网络字节序
packet->header.len = htonl(len); // 设置报文长度,并转换为网络字节序
packet->header.ver = 0x01; // 设置版本号为1
packet->header.iface_ver = 0x01; // 设置接口版本号为1
packet->header.msg_type = msg_type; // 设置报文类型
packet->header.retcode = retcode; // 设置返回码
packet->payload.data = data; // 设置负载数据指针
packet->payload.len = len; // 设置负载数据长度
return packet; // 返回报文段指针
}
// 销毁一个someip报文段,释放内存空间
void destroy_someip_packet(struct someip_packet *packet) {
if (packet == NULL) {
return;
}
free(packet); // 释放内存空间
}
// 发送一个someip报文段,通过套接字将报文头和负载数据发送出去,并更新会话ID
int send_someip_packet(struct someip_client *client, struct someip_packet *packet) {
if (client == NULL || packet == NULL) {
return -1;
}
client->session_id++; // 更新会话ID
if (client->session_id == 0) { // 防止会话ID溢出
client->session_id++;
}
packet->header.msg_id = (packet->header.msg_id & 0xFFFFFF00) | client->session_id; // 将会话ID写入报文ID的最低字节
int n = send(client->sock, &packet->header, sizeof(packet->header), 0); // 发送报文头
if (n < 0) {
perror("send failed");
return -1;
}
if (packet->payload.len > 0 && packet->payload.data != NULL) { // 如果有负载数据
n = send(client->sock, packet->payload.data, packet->payload.len, 0); // 发送负载数据
if (n < 0) {
perror("send failed");
return -1;
}
}
return 0; // 返回成功
}
// 接收一个someip报文段,通过套接字接收报文头和负载数据,并解析出报文头和负载数据
struct someip_packet *recv_someip_packet(struct someip_client *client) {
if (client == NULL) {
return NULL;
}
struct someip_packet *packet = malloc(sizeof(struct someip_packet)); // 分配内存空间
if (packet == NULL) {
perror("malloc failed");
return NULL;
}
int n = recv(client->sock, &packet->header, sizeof(packet->header), MSG_WAITALL); // 接收报文头
if (n < 0) {
perror("recv failed");
free(packet);
return NULL;
}
if (n == 0) { // 如果对方关闭了连接
free(packet);
return NULL;
}
packet->header.msg_id = ntohl(packet->header.msg_id); // 将报文ID转换为主机字节序
packet->header.len = ntohl(packet->header.len); // 将报文长度转换为主机字节序
if (packet->header.len > 0) { // 如果有负载数据
packet->payload.data = malloc(packet->header.len); // 分配内存空间
if (packet->payload.data == NULL) {
perror("malloc failed");
free(packet);
return NULL;
}
packet->payload.len = packet->header.len; // 设置负载数据长度
n = recv(client->sock, packet->payload.data, packet->payload.len, MSG_WAITALL); // 接收负载数据
if (n < 0) {
perror("recv failed");
free(packet->payload.data);
free(packet);
return NULL;
}
if (n == 0) { // 如果对方关闭了连接
free(packet->payload.data);
free(packet);
return NULL;
}
} else { // 如果没有负载数据
packet->payload.data = NULL; // 设置负载数据指针为NULL
packet->payload.len = 0; // 设置负载数据长度为0
}
return packet; // 返回报文段指针
}
// 处理一个someip报文段,根据报文类型进行相应的处理或反馈
int handle_someip_packet(struct someip_client *client, struct someip_packet *packet) {
if (client == NULL || packet == NULL) {
return -1;
}
switch (packet->header.msg_type) { // 根据报文类型
case SOMEIP_MSG_TYPE_REQUEST: // 如果是请求类型
// TODO: 根据报文ID和负载数据,执行相应的服务,并返回响应或错误类型的报文段
break;
case SOMEIP_MSG_TYPE_REQUEST_NO_RETURN: // 如果是不期待响应的请求类型
// TODO: 根据报文ID和负载数据,执行相应的服务,无需返回报文段
break;
case SOMEIP_MSG_TYPE_NOTIFICATION: // 如果是通知类型
// TODO: 根据报文ID和负载数据,处理相应的事件,无需返回报文段
break;
case SOMEIP_MSG_TYPE_RESPONSE: // 如果是响应类型
// TODO: 根据报文ID和负载数据,验证返回码,并处理相应的结果,无需返回报文段
break;
case SOMEIP_MSG_TYPE_ERROR: // 如果是错误类型
// TODO: 根据报文ID和负载数据,验证返回码,并处理相应的错误,无需返回报文段
break;
default: // 如果是其他类型
fprintf(stderr, "invalid message type\n");
return -1;
}
return 0; // 返回成功
}
// someip_client_test.c
// 测试someip客户端的功能
#include "someip_client.h"
int main() {
// 创建一个someip客户端,并连接到服务器的1234端口
struct someip_client *client = create_someip_client("127.0.0.1", 1234);
if (client == NULL) {
return -1;
}
// 构造一个请求类型的someip报文段,报文ID为0x01020304,负载数据为"hello"
struct someip_packet *packet = create_someip_packet(0x01020304, SOMEIP_MSG_TYPE_REQUEST, 0x00, "hello", 5);
if (packet == NULL) {
destroy_someip_client(client);
return -1;
}
// 发送该报文段
if (send_someip_packet(client, packet) < 0) {
destroy_someip_packet(packet);
destroy_someip_client(client);
return -1;
}
// 销毁该报文段
destroy_someip_packet(packet);
// 接收一个报文段
packet = recv_someip_packet(client);
if (packet == NULL) {
destroy_someip_client(client);
return -1;
}
// 处理该报文段
if (handle_someip_packet(client, packet) < 0) {
destroy_someip_packet(packet);
destroy_someip_client(client);
return -1;
}
// 销毁该报文段
destroy_someip_packet(packet);
// 销毁someip客户端
destroy_someip_client(client);
return 0;
}