shopnc本地生活o2o网站系统,seo营销怎么做,网站幕布拍摄,自己电脑做网站要下载一 简介
我们常常在Linux系统中编写socket接收TCP/UDP协议数据#xff0c;大家有没有想过它怎么实现的#xff0c;如果我们要实现socket接收自定义的协议数据又该怎么做呢#xff1f;带着这个疑问#xff0c;我们一起往下看吧~~
二 Linux内核函数简介
在Linux系统中要想…一 简介
我们常常在Linux系统中编写socket接收TCP/UDP协议数据大家有没有想过它怎么实现的如果我们要实现socket接收自定义的协议数据又该怎么做呢带着这个疑问我们一起往下看吧~~
二 Linux内核函数简介
在Linux系统中要想实现通过socket接收网络协议栈送过来的数据首先要对这两个内核函数实现注册, 先来看看这两个函数原型
//ops: 指向一个描述套接字协议族的 net_proto_family 结构体。这个结构体定义了
//协议族编号family比如 AF_INETIPv4或 AF_INET6IPv6。
//与此协议族关联的 create 函数用于创建套接字。
int sock_register(const struct net_proto_family *ops);//prot: 指向一个描述传输层协议的 proto 结构体该结构体定义了协议的具体实现包括各种操作和资源管理逻辑例如发送、接收、内存分配等。
//alloc_slab: 一个布尔值指定是否为该协议分配内存缓存slab 缓存通常用于控制协议的缓冲区管理。
int proto_register(struct proto *prot, int alloc_slab);
1.sock_register 将用户自定义的协议族挂载到内核的协议族表net_families,注册成功后用户态程序可以通过指定协议族如 socket(AF_INET, ..)来创建对应的套接字。
2.proto_register 函数将传输层协议的实现注册到内核的协议栈中使其可以在对应的套接字类型下运行如 SOCK_STREAM 对应 TCP注册的协议通常与 sock_register 中的协议族关联用于实现更高层次的功能。
两者的关系 sock_register: 是更高层的接口用于注册协议族如 AF_INET。 proto_register: 是传输层的具体实现用于注册实际的协议逻辑如 TCP、UDP、或自定义协议。
通常一个协议族可能对应多种协议比如AF_INETIPv4对的具体协议又TCP/UDP等,proto_register 是为了支持某个协议族的实际功能而 sock_register 提供的是更外层的入口。二者可以配合使用以实现从协议族到协议具体实现的完整功能。
自定义协议族内核模块实现
下面这个代码是实现自定义一个协议族AF_MYPROTO28的内核模块。
测试平台CentOS7 Linux内核版本 3.10.0编译工具gcc 4.8.5
#include linux/module.h
#include linux/kernel.h
#include linux/net.h
#include linux/socket.h
#include linux/skbuff.h
#include net/sock.h
#include linux/init.h/* 自定义协议族号 (通常选择未使用的值,sock.h有定义) */
#define AF_MYPROTO 28 /* 定义 proto_ops用于实现 socket 操作 */
static int myproto_sock_create(struct net *net, struct socket *sock, int protocol, int kern);/* 应用层调用close会调用sock_my_release */
static int sock_my_release (struct socket *sock)
{pr_info(sock_my_release............\n);return 0;
}/* 应用层调用recv函数会触发调用下面这个sock_my_recvmsg函数 */
static int sock_my_recvmsg (struct kiocb *iocb, struct socket *sock,struct msghdr *msg, size_t total_len,int flags)
{char buf[32];int i0;for(i0; i32; i) buf[i] (total_leni)0xFF;memcpy_toiovec(msg-msg_iov, buf, 32); /* 拷贝数据到应用层buffer */pr_info(total_len%ld flags%d\n, total_len, flags);return 0;
}/* 应用层调用send函数会触发调用该sock_my_sendmsg函数 */
static int sock_my_sendmsg (struct kiocb *iocb, struct socket *sock,struct msghdr *m, size_t total_len)
{pr_info(sock_my_sendmsg............\n);return 0;
}static const struct proto_ops myproto_ops {.family AF_MYPROTO,.owner THIS_MODULE,.release sock_my_release, .bind sock_no_bind, /* 默认空函数 xxx_no_xxx()下同 */.connect sock_no_connect,.socketpair sock_no_socketpair,.accept sock_no_accept,.getname sock_no_getname,.poll sock_no_poll,.ioctl sock_no_ioctl,.listen sock_no_listen,.shutdown sock_no_shutdown,.setsockopt sock_no_setsockopt,.getsockopt sock_no_getsockopt,.sendmsg sock_my_sendmsg, /* 指定上面实现的函数 */.recvmsg sock_my_recvmsg, /* 指定上面实现的函数 */.mmap sock_no_mmap,.sendpage sock_no_sendpage,
};/* 定义传输协议 proto 结构体 */
static struct proto myproto_proto {.name MYPROTO,.owner THIS_MODULE,.obj_size sizeof(struct sock),
};// 定义协议族 net_proto_family
static const struct net_proto_family myproto_family {.family AF_MYPROTO,.create myproto_sock_create,.owner THIS_MODULE,
};/* 创建 socket 函数 */
static int myproto_sock_create(struct net *net, struct socket *sock, int protocol, int kern) {struct sock *sk;pr_info(MYPROTO: Creating socket\n);if (!protocol)protocol IPPROTO_IP;/* 分配 socket 的底层数据结构PF_INET: Internet IP Protocol */sk sk_alloc(net, PF_INET, GFP_KERNEL, myproto_proto);if (!sk) return -ENOMEM;sock-ops myproto_ops;sock_init_data(sock, sk);return 0;
}/* 模块加载和卸载模块入口 */
static int __init myproto_init(void) {int ret;pr_info(MYPROTO: Initializing module\n);/* 注册传输层协议,具体协议 */ret proto_register(myproto_proto, 1);if (ret) {pr_err(MYPROTO: Failed to register proto\n);return ret;}ret sock_register(myproto_family); /* 注册协议族 */if (ret) {pr_err(MYPROTO: Failed to register protocol family\n);proto_unregister(myproto_proto);return ret;}pr_info(MYPROTO: Module loaded\n);return 0;
}/* 模块卸载时候调用 */
static void __exit myproto_exit(void) {pr_info(MYPROTO: Cleaning up module\n);// 注销协议族和传输层协议sock_unregister(AF_MYPROTO);proto_unregister(myproto_proto);pr_info(MYPROTO: Module unloaded\n);
}module_init(myproto_init);
module_exit(myproto_exit);MODULE_LICENSE(GPL);
MODULE_AUTHOR(Linux编程用CYoung);
编译内核模块的Makefile如下注意内核代码名称为net_prot.c才能编译
obj-m:net_prot.o #名称对应起来也可自己修改KERNELDIR:/lib/modules/$(shell uname -r)/buildPWD:$(shell pwd)default: $(MAKE) -C $(KERNELDIR) M$(PWD) modules
clean: rm -rf *.o *.mod.c *.mod.o *.koendif编译完成后使用insmod向内核插入模块insmod net_prot.ko
使用dmesg查看模块加载信息 三 自定义协议族应用程序实现
这个应用程序创建AF_MYPROTO自定义协议的socket读取数据示例如下
#include stdio.h
#include stdlib.h
#include sys/socket.h
#include unistd.h
#include errno.h#define AF_MYPROTO 28 // 和内核协议族号保持一致int main() {int sockfd, i100;char buf[2048];// 创建套接字sockfd socket(AF_MYPROTO, SOCK_RAW, 0);if (sockfd 0) {perror(socket);return EXIT_FAILURE;}printf(Socket created successfully with AF_MYPROTO\n);while(--i){read(sockfd, buf, 32i);for(int i0; i32; i){printf(0x%x , buf[i]);}printf(\n);}// 关闭套接字close(sockfd);return EXIT_SUCCESS;
}
使用dmesgch
四 测试结果
收到来自内核返回数据该数据是固定填充做示例演示。 五 总结
通过这个例子我们了解到了内核协议族的注册与使用流程加深对Linux协议栈的了解。
我是小C欢迎大家一起交流学习~