应用程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>#define ERR_MSG(msg) \do \{ \printf("line:%d\n", __LINE__); \perror(msg); \} while (0)#define PORT 6666 //端口号
#define IP "192.168.250.100" // IP地址int main(int argc, const char *argv[])
{//创建流式套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if (sfd < 0){ERR_MSG("socket");return -1;}printf("socket create success sfd=%d __%d__\n", sfd, __LINE__);//设置端口快速被复用int reuse = 1;if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){ERR_MSG("setsockopt");return -1;}//填充服务器自身的地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(PORT);sin.sin_addr.s_addr = inet_addr(IP);//存储客户端信息结构体int s_res = 0;struct sockaddr_in cin;socklen_t len = sizeof(cin);struct sockaddr_in savecin[1024];//将IP地址和端口号绑定到指定的套接字文件描述符上if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0){ERR_MSG("bind");return -1;}printf("bind success __%d__\n", __LINE__);//将套接字设置为被动监听状态if (listen(sfd, 128) < 0){ERR_MSG("listen");return -1;}printf("listen success __%d__\n", __LINE__);char buf[128] = "";ssize_t res = 0;// epollint fd, newfd, epfd;struct epoll_event event;struct epoll_event events[10]; //存放就绪事件描述符的数组//创建epoll句柄epfd = epoll_create(1);if (epfd < 0){printf("epoll_create filed\n");exit(-1);}event.events = EPOLLIN;event.data.fd = sfd;if (epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &event) < 0){printf("epoll_ctl add filed\n");}//监听事件是否发生while (1){//等待事件发生int ret = epoll_wait(epfd, events, 10, -1);if (ret < 0){printf("epoll_wait filed\n");exit(-1);}int i;//循环遍历数组,做事件的处理for (i = 0; i < ret; i++){//监听到新连接if (events[i].data.fd == sfd){newfd = accept(sfd, (struct sockaddr *)&cin, &len);if (newfd < 0){perror("accept");return -1;}printf("[%s:%d] 客户端连接成功 newfd=%d __%d__\n",inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);savecin[newfd] = cin;//新客户端套接字加入epollevent.events = EPOLLIN;event.data.fd = newfd;if (epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &event) < 0){printf("epoll_ctl add filed\n");}}else if (events[i].data.fd == 0) //键盘输入事件{printf("触发键盘输入事件 __%d__\n", __LINE__);int sndfd = -1;res = scanf("%d %s", &sndfd, buf);while (getchar() != 10);if (res != 2){printf("请输入正确数据格式:fd string\n");continue;}}else{//客户端交互事件printf("触发客户端交互事件__%d__\n", __LINE__);bzero(buf, sizeof(buf));//接收数据res = recv(events[i].data.fd, buf, sizeof(buf), 0);if (res < 0){ERR_MSG("recv");return -1;}else if (0 == res){printf("[%s:%d] 客户端下线 newfd=%d __%d__\n",inet_ntoa(savecin[i].sin_addr), ntohs(savecin[i].sin_port), i, __LINE__);close(i); //未接收到数据,关闭文件描述符continue; //进入下一次循环}//发送数据strcat(buf, "*_*");if (send(events[i].data.fd, buf, sizeof(buf), 0) < 0){ERR_MSG("send");return -1;}printf("发送成功\n");}}}if (close(sfd) < 0){ERR_MSG("close");return -1;}return 0;
}
驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>
#include<linux/uaccess.h>
#include<linux/poll.h>
struct class *cls;
struct device *dev;
unsigned int major;//定义一个变量保存主设备号
char kbuf[128]={0};
//定义等待队列头
wait_queue_head_t wq_head;
int condition=0;//数据是否准备好的标志变量
//封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);if(size>sizeof(kbuf))//用户的需求内核满足不了{size=sizeof(kbuf);}long ret;ret=copy_to_user(ubuf,kbuf,size);if(ret){printk("copy_to_user filed\n");return -EIO;}condition=0;//表示下一次数据没有准备好return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);if(size>sizeof(kbuf))//用户的需求内核满足不了{size=sizeof(kbuf);}long ret;ret=copy_from_user(kbuf,ubuf,size);//表示模拟硬件数据就绪if(ret){printk("copy_from_user filed\n");return -EIO;}condition=1;wake_up_interruptible(&wq_head);//唤醒休眠的进程return 0;
}
//封装poll方法
__poll_t mycdev_poll(struct file *file, struct poll_table_struct *wait)
{__poll_t mask=0;//1.向上提交等待队列头poll_wait(file,&wq_head,wait);//2.根据事件是否发生给一个合适的返回值if(condition){mask=POLLIN;}return mask;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);return 0;
}
//定义一个操作方法结构体变量并且初始化
struct file_operations fops={.open=mycdev_open,.release=mycdev_close,.read=mycdev_read,.poll=mycdev_poll,.write=mycdev_write,
};
static int __init mycdev_init(void)
{//初始化等待队列头init_waitqueue_head(&wq_head);//注册字符设备驱动major=register_chrdev(0,"mychrdev",&fops);if(major<0){printk("注册字符设备驱动失败\n");return major;}printk("注册字符设备驱动成功major=%d\n",major);// 向上提交目录cls = class_create(THIS_MODULE, "myled");if (IS_ERR(cls)){printk("向上提交目录失败\n");return -PTR_ERR(cls);}printk("向上提交目录信息成功\n");// 向上提交设备节点信息dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "mycdev");if (IS_ERR(dev)){printk("向上提交设备节点信息失败\n");return -PTR_ERR(dev);}printk("向上提交设备节点成功\n");return 0;
}
static void __exit mycdev_exit(void)
{// 销毁节点信息device_destroy(cls, MKDEV(major, 0));// 销毁目录信息class_destroy(cls);//注销字符设备驱动unregister_chrdev(major,"mychrdev");}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");