sock_ev——linux平台socket事件框架(socket API的封装)

本文介绍了一种将Linux平台的Socket API进行封装的方法,旨在简化Socket编程并提供统一的接口。通过创建基类CommBase及派生类StreamSock和DgramSock,实现了对流式和数据报Socket的不同操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

把linux平台提供的有关socket操作的API进行封装是有必要的;基于stream操作的流程与基于dgram操作的流程略有不同,分别放在两个类中,但两者又有很多相似的操作,因此写一个基类,让其继承自这个基类;基类起名为:CommBase,操作stream与dgram两个类分别起名为:StreamSock、DgramSock;

/***************************************************************************************
****************************************************************************************
* FILE		: socket_base.h
* Description	: 
*			  
* Copyright (c) 2012 by Liu Yanyun(E-mail:liuyun827@foxmail.com). All Rights Reserved.
*            Without permission, shall not be used for any commercial purpose
* 
* History:
* Version		Name       		Date			Description
   0.1		Liu Yanyun		2012/12/06		Initial Version
   
****************************************************************************************
****************************************************************************************/


#ifndef _SOCKET_BASE_H_
#define _SOCKET_BASE_H_


#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <string>

class SockAddr;


/*==================================================================
* Function	: CommBase
* Description	: socket base class
==================================================================*/
class CommBase
{
public:

  /*==================================================================
  * Function	: CommBase
  * Description	: construction function
  ==================================================================*/
  CommBase();

  /*==================================================================
  * Function	: CommBase
  * Description	: Destructor function
  ==================================================================*/
  virtual ~CommBase();

  /*==================================================================
  * Function	: CommBase.getSockFd
  * Description	: get socket fd
  * Return Value: socket fd
  ==================================================================*/
  int getSockFd();

  /*==================================================================
  * Function	: CommBase.setSockFd
  * Description	: set socket fd
  * Input Para	: socket fd
  * Return Value: void
  ==================================================================*/
  void setSockFd(int fd);

  /*==================================================================
  * Function	: CommBase.openSock
  * Description	: for stream (socket->bind->listen);for dgram(socket->bind)
  * Input Para	: socket address
  * Return Value: if success return true, or else false is return
  ==================================================================*/
  virtual bool openSock(SockAddr &addr_) = 0;

  /*==================================================================
  * Function	: CommBase.acceptSock
  * Description	: used for server accept client connection
  * Input Para	: addr_--socket address
  * Output Para	: addr_--client address
  * Return Value: if success return client fd, or else -1 is return
  ==================================================================*/
  int acceptSock(SockAddr &addr_);

  /*==================================================================
  * Function	: CommBase.connectTo
  * Description	: used for client to connect to server
  * Input Para	: socket address
  * Return Value: if success return client fd, or else -1 is return
  ==================================================================*/
  bool connectTo(SockAddr &addr_);

  /*==================================================================
  * Function	: CommBase.sendData
  * Description	: send data
  * Input Para	: data_--data buf pointer
  * Input Para	: len_--data buf length
  * Return Value: success send length
  ==================================================================*/
  virtual int sendData(void *data_, 
      uint32_t len_);

  /*==================================================================
  * Function	: CommBase.sendData
  * Description	: send data
  * Input Para	: data_--data buf pointer
  * Input Para	: len_--data buf length
  * Input Para	: addr_--address
  * Return Value: success send length
  ==================================================================*/
  virtual int sendData(void *data_, 
      uint32_t len_, 
      SockAddr &addr_);

  /*==================================================================
  * Function	: CommBase.recvData
  * Description	: recv data
  * Input Para	: data_--data buf pointer
  * Input Para	: len_--data buf length
  * Return Value: success recv length
  ==================================================================*/
  virtual int recvData(void *data_, 
      uint32_t len_);

  /*==================================================================
  * Function	: CommBase.recvData
  * Description	: recv data
  * Input Para	: data_--data buf pointer
  * Input Para	: len_--data buf length
  * Input Para	: addr_--address
  * Return Value: success recv length
  ==================================================================*/
  virtual int recvData(void *data_, 
      uint32_t len_,
      SockAddr &addr_);

  /*==================================================================
  * Function	: CommBase.enableSockReuse
  * Description	: enable socket address reuse
  * Return Value: if success return true, or else false is return
  ==================================================================*/
  bool enableSockReuse();

  /*==================================================================
  * Function	: CommBase.setSendBufSize
  * Description	: set send buffer size
  * Input Para	: size_--buffer size
  * Return Value: if success return true, or else false is return
  ==================================================================*/
  bool setSendBufSize(uint32_t size_);

  /*==================================================================
  * Function	: CommBase.setRecvBufSize
  * Description	: set recv buffer size
  * Input Para	: size_--buffer size
  * Return Value: if success return true, or else false is return
  ==================================================================*/
  bool setRecvBufSize(uint32_t size_);

  /*==================================================================
  * Function	: CommBase.getSendBufSize
  * Description	: get send buffer size
  * Return Value: if success return buffer size, or else -1 is return
  ==================================================================*/
  int getSendBufSize();

  /*==================================================================
  * Function	: CommBase.getRecvBufSize
  * Description	: get recv buffer size
  * Return Value: if success return buffer size, or else -1 is return
  ==================================================================*/
  int getRecvBufSize();
  
protected:

  //socket fd
  int sockFd;
  
private:

  // Disable copy construction and assignment.
  CommBase(const CommBase&);
  const CommBase &operator = (const CommBase&);
};



/*==================================================================
* Function	: StreamSock 
* Description	: Stream Socket class
==================================================================*/
class StreamSock : public CommBase
{
public:

  /*==================================================================
  * Function	: StreamSock 
  * Description	: construction function
  ==================================================================*/
  StreamSock();

  /*==================================================================
  * Function	: StreamSock 
  * Description	: Destructor function
  ==================================================================*/
  virtual ~StreamSock();

  /*==================================================================
  * Function	: StreamSock 
  * Description	: Please reference to CommBase
  ==================================================================*/
  bool openSock(SockAddr &addr_);

  /*==================================================================
  * Function	: StreamSock 
  * Description	: Please reference to CommBase
  ==================================================================*/
  int sendData(void *data_, 
    uint32_t len_);

  /*==================================================================
  * Function	: StreamSock 
  * Description	: Please reference to CommBase
  ==================================================================*/
  int recvData(void *data_, 
    uint32_t len_);
};

/*==================================================================
* Function	: DgramSock 
* Description	: Dgram Socket class
==================================================================*/
class DgramSock : public CommBase
{
public:

  /*==================================================================
  * Function	: DgramSock 
  * Description	: construction function
  ==================================================================*/
  DgramSock();

  /*==================================================================
  * Function	: StreamSock 
  * Description	: Destructor function
  ==================================================================*/
  virtual ~DgramSock();

  /*==================================================================
  * Function	: StreamSock 
  * Description	: Please reference to CommBase
  ==================================================================*/
  bool openSock(SockAddr &addr_);

  /*==================================================================
  * Function	: StreamSock 
  * Description	: Please reference to CommBase
  ==================================================================*/
  int sendData(void *data_, 
    uint32_t len_, 
    SockAddr &addr_);

  /*==================================================================
  * Function	: StreamSock 
  * Description	: Please reference to CommBase
  ==================================================================*/
  int recvData(void *data_, 
    uint32_t len_,
    SockAddr &addr_);
};

#endif /*_SOCKET_BASE_H_*/

L28:尽量使用类型声明方式,而不是包含头文件

通过上面的函数声明可以看出,有关socket地址的参数统一为我们前一节中介绍的,SockAddr

 

#include "socket_base.h"
#include "socket_addr.h"
#include "log_trace.h"
#include <string>

using namespace std;

CommBase::CommBase()
{
  sockFd = -1;
}

CommBase::~CommBase()
{
  close(sockFd);
  sockFd = -1;
}

int CommBase::getSockFd()
{
  return sockFd;
}

void CommBase::setSockFd(int fd)
{
  sockFd = fd;
}
int CommBase::acceptSock(SockAddr &addr_)
{
  struct sockaddr *sockAddr = addr_.getAddr();
  socklen_t addrLen = addr_.getSockLen();

  int connFd = accept(sockFd, sockAddr, &addrLen);
  if(-1 == connFd)
  {
    logTrace("%m");
  }

  return connFd;
}
bool CommBase::connectTo(SockAddr &addr_)
{
  sockFd = socket(addr_.getDomain(), addr_.getType(), 0);
  if( -1 == sockFd)
  {
    logTrace("%m");
    return false;
  }
  
  const struct sockaddr *sockAddr = addr_.getAddr();
  if(NULL == sockAddr)
  {
    logTrace("");
    return false;
  }

  int rc = connect(sockFd, sockAddr, addr_.getSockLen());
  if(-1 == rc)
  {
    logTrace("%m");
    return false;
  }

  return true;
}

int CommBase::sendData(void *data_, 
    uint32_t len_)
{
  logTrace("you can't use this method");

  return -1;
}

int CommBase::sendData(void *data_, 
    uint32_t len_, 
    SockAddr &addr_)
{
  logTrace("you can't use this method");

  return -1;
}

int CommBase::recvData(void *data_, 
    uint32_t len_)
{
  logTrace("you can't use this method");

  return -1;
}

int CommBase::recvData(void *data_, 
    uint32_t len_,
    SockAddr &addr_)
{
  logTrace("you can't use this method");

  return -1;
}
      
bool CommBase::enableSockReuse()
{
  int flag = 1;
  int rc = setsockopt (sockFd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (flag));
  if(0 != rc)
  {
    logTrace("%m");
  }

  return (0 == rc);
}

bool CommBase::setSendBufSize(uint32_t size)
{
  int rc = setsockopt(sockFd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
  if(0 != rc)
  {
    logTrace("%m");
  }

  return (0 == rc);
}

bool CommBase::setRecvBufSize(uint32_t size)
{
  int rc = setsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
  if(0 != rc)
  {
    logTrace("%m");
  }

  return (0 == rc);
}

int CommBase::getSendBufSize()
{
  int size;
  socklen_t len = sizeof(size);
  int rc = getsockopt(sockFd, SOL_SOCKET, SO_SNDBUF, &size, &len );
  if(0 != rc)
  {
    logTrace("%m");
    return -1;
  }

  return size;
}

int CommBase::getRecvBufSize()
{
  int size;
  socklen_t len = sizeof(size);
  int rc = getsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, &size, &len );
  if(0 != rc)
  {
    logTrace("%m");
    return -1;
  }

  return size;
}

StreamSock::StreamSock()
{
  //do nothing
}
StreamSock::~StreamSock()
{
  //do nothing
}
bool StreamSock::openSock(SockAddr &addr_)
{
  sockFd = socket(addr_.getDomain(), addr_.getType(), 0);
  if( -1 == sockFd)
  {
    logTrace("%m");
    return false;
  }

  enableSockReuse();
  
  const struct sockaddr *sockAddr = addr_.getAddr();
  if(NULL == sockAddr)
  {
    logTrace("");
    return false;
  }

  int ret = bind(sockFd, sockAddr, addr_.getSockLen());
  if(0 != ret)
  {
    logTrace("%m");
    return false;
  }

  ret = listen(sockFd, 32);
  if(0 != ret)
  {
    logTrace("%m");
    return false;
  }

  return true;
}
int StreamSock::sendData(void *data_, 
    uint32_t len_)
{
  int sendLen = 0;
  do
  {
    sendLen = send(sockFd, data_, len_, 0);
  }while(-1 == sendLen && EINTR == errno);

  if(sendLen <= 0)
  {
    logTrace("%m");
  }

  return sendLen;
}

int StreamSock::recvData(void *data_, 
    uint32_t len_)
{
  int recvLen = 0;
  do
  {
    recvLen = recv(sockFd, data_, len_, 0);
  }while(-1 == recvLen && EINTR == errno);

  if(recvLen <= 0)
  {
    logTrace("%m");
  }

  return recvLen;
}
DgramSock::DgramSock()
{
  //do nothing
}
DgramSock::~DgramSock()
{
  //do nothing
}
bool DgramSock::openSock(SockAddr &addr_)
{
  sockFd = socket(addr_.getDomain(), addr_.getType(), 0);
  if( -1 == sockFd)
  {
    logTrace("%m");
    return false;
  }

  
  const struct sockaddr *sockAddr = addr_.getAddr();
  if(NULL == sockAddr)
  {
    logTrace("");
    return false;
  }

  int ret = bind(sockFd, sockAddr, addr_.getSockLen());
  if(0 != ret)
  {
    logTrace("%m");
    return false;
  }

  return true;
}
int DgramSock::sendData(void *data_, 
    uint32_t len_, 
    SockAddr &addr_)
{
  const struct sockaddr *sockAddr = addr_.getAddr();
  
  if(NULL == sockAddr)
  {
    logTrace("");
    return false;
  }

  int sendLen = 0;
  do
  {
    sendLen = sendto(sockFd, data_, len_, 0, sockAddr, addr_.getSockLen());
  }while(-1 == sendLen && EINTR == errno);

  if(sendLen <= 0)
  {
    logTrace("%m");
  }

  return sendLen;
}
int DgramSock::recvData(void *data_, 
    uint32_t len_,
    SockAddr &addr_)
{
  struct sockaddr *sockAddr = addr_.getAddr();
  socklen_t addrLen = addr_.getSockLen();
  
  int recvLen = 0;
  do
  {
    recvLen = recvfrom(sockFd, data_, len_, 0, sockAddr, &addrLen);
  }while(-1 == recvLen && EINTR == errno);

  if(recvLen <= 0)
  {
    logTrace("%m");
  }

  return recvLen;
}

上面的都是最简单的socket调用,不多说明!

<think>好的,用户现在需要写一个嵌入式Linux联网触发的事件回调。我需要仔细分析他的需求,然后给出一个合适的解决方案。首先,用户之前的问题是关于代码是否与Linux内核交互,之前的回答提到了用户空间通过sysfs和系统调用与内核交互,而这次是关于联网的事件回调,可能需要网络方面的处理。 用户可能是在开发一个嵌入式设备,需要在网络状态变化或者接收到特定网络数据时触发某些操作。比如,当设备连接到Wi-Fi,或者收到某个UDP/TCP消息时执行回调函数。这时候需要确定用户的具体需求,是监控网络状态的变化,还是监听特定的网络数据包,或者是基于某种协议(如HTTP、MQTT)的事件触发。 首先,我需要考虑嵌入式Linux中常见的网络事件回调方法。可能的方案包括: 1. **使用系统调用或库函数监听网络状态变化**:比如检测网络接口的状态(up/down),IP地址变化等。可以通过netlink socket或者ioctl来获取这些信息,但需要轮询或者事件驱动的方式。 2. **基于socket编程的事件监听**:比如创建一个服务器socket,当有连接或者数据到达时触发回调。这可以使用select/poll/epoll等I/O多路复用机制来实现异步处理。 3. **使用现有的库或框架**:比如libevent、libuv等,这些库封装事件循环,可以更方便地处理网络事件。 4. **利用网络服务或协议**:比如使用MQTT订阅主题,当消息到达时触发回调;或者使用HTTP长轮询、WebSocket等实现实时通信。 5. **内核模块或网络钩子**:如果用户需要更底层的控制,可能需要编写内核模块,使用netfilter等框架来捕获网络数据包,但这种方法复杂度高,且用户之前的问题是关于用户空间的,可能更倾向于用户空间方案。 接下来,我需要确定用户的应用场景。例如,如果用户需要响应特定的网络请求,如收到某个UDP包后执行操作,那么用socket编程配合epoll可能是合适的。如果需要监控网络连接状态,可能需要结合netlink或者dbus监听NetworkManager的事件。 另外,考虑到嵌入式系统的资源限制,可能需要轻量级的解决方案。例如,使用epoll比select更高效,适合处理大量连接;或者使用基于回调的库如libevent可以减少代码复杂度。 同时,用户可能需要代码示例,所以需要准备一个简单但完整的例子,展示如何设置事件监听和回调机制。例如,使用epoll监听socket事件,当数据到达时调用回调函数。 还要考虑错误处理和资源释放,确保示例代码的健壮性。例如,正确关闭socket,处理信号中断等。 可能需要分步骤说明: 1. 创建socket并绑定到端口。 2. 设置socket为非阻塞模式。 3. 使用epoll注册socket事件。 4. 进入事件循环,等待事件触发。 5. 当事件发生时,调用相应的回调函数处理。 6. 处理完成后继续监听。 另外,如果用户需要的是更高层次的事件,比如HTTP请求,可能需要提到Web服务器框架,或者使用curl等工具,但可能超出回调的范围。 最后,需要总结不同方法的优缺点,帮助用户根据具体需求选择合适的方式。例如,epoll适用于高性能需求,而MQTT适合物联网场景的发布/订阅模型。</think>在嵌入式Linux中实现**网络事件触发的回调机制**,可通过监听网络状态变化或数据到达事件实现。以下是两种典型方案及代码示例: --- ### 方案一:基于epoll的Socket事件回调(TCP/UDP) 适用于**实时响应网络数据包**,如接收控制指令、传感器数据等。 ```c #include <stdio.h> #include <sys/epoll.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <fcntl.h> #define MAX_EVENTS 5 #define PORT 8080 // 回调函数原型 typedef void (*event_callback)(int fd); // 事件处理结构体 struct event_data { int fd; event_callback cb; }; // 全局epoll实例 int epoll_fd; // 注册事件到epoll void add_event(int fd, event_callback cb) { struct epoll_event ev; struct event_data *ed = malloc(sizeof(struct event_data)); ed->fd = fd; ed->cb = cb; ev.events = EPOLLIN | EPOLLET; // 边缘触发模式 ev.data.ptr = ed; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev); } // 回调示例:处理TCP连接 void tcp_connection_callback(int fd) { struct sockaddr_in client_addr; socklen_t len = sizeof(client_addr); int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len); printf("新的TCP连接: %d\n", client_fd); // 在此添加业务逻辑 } // 回调示例:处理数据接收 void data_receive_callback(int fd) { char buffer[1024]; ssize_t len = recv(fd, buffer, sizeof(buffer), 0); if(len > 0) { printf("收到数据: %.*s\n", (int)len, buffer); send(fd, "ACK\n", 4, 0); // 示例响应 } else { close(fd); // 连接关闭 } } int main() { // 创建TCP Socket int sock_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(PORT), .sin_addr.s_addr = INADDR_ANY }; bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)); listen(sock_fd, 5); // 创建epoll实例 epoll_fd = epoll_create1(0); add_event(sock_fd, tcp_connection_callback); // 事件循环 struct epoll_event events[MAX_EVENTS]; while(1) { int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); for(int i=0; i<n; i++) { struct event_data *ed = events[i].data.ptr; ed->cb(ed->fd); // 触发回调 } } close(sock_fd); return 0; } ``` --- ### 方案二:使用libevent库(高级抽象) 适合需要**同时处理多种事件源**(网络+定时器+信号)的场景。 ```c #include <event2/event.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #define PORT 8080 void connection_callback(evutil_socket_t fd, short events, void *arg) { struct event_base *base = arg; struct sockaddr_in client_addr; socklen_t len = sizeof(client_addr); int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len); // 为新连接注册数据接收事件 struct event *ev = event_new(base, client_fd, EV_READ | EV_PERSIST, data_callback, event_self_cbarg()); event_add(ev, NULL); } void data_callback(evutil_socket_t fd, short events, void *arg) { char buf[1024]; ssize_t len = recv(fd, buf, sizeof(buf), 0); if(len > 0) { printf("Received: %.*s\n", (int)len, buf); send(fd, "OK\n", 3, 0); } else { event_free(arg); // 释放事件对象 close(fd); } } int main() { struct event_base *base = event_base_new(); int sock_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(PORT), .sin_addr.s_addr = INADDR_ANY }; bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)); listen(sock_fd, 5); // 注册连接事件 struct event *listen_event = event_new(base, sock_fd, EV_READ | EV_PERSIST, connection_callback, base); event_add(listen_event, NULL); event_base_dispatch(base); // 启动事件循环 event_base_free(base); close(sock_fd); return 0; } ``` --- ### 关键实现机制 | 组件/技术 | 作用 | 性能特点 | |-----------------|--------------------------------|---------------------------| | **epoll** | 高效I/O多路复用(内核事件通知)| 适合高并发,O(1)时间复杂度| | **非阻塞Socket**| 避免进程阻塞 | 需配合边缘触发(ET)模式使用| | **回调函数** | 事件响应逻辑封装 | 实现业务逻辑与事件解耦 | | **libevent** | 跨平台事件库 | 封装epoll/kqueue等底层API | --- ### 扩展:网络状态监控 如需检测**网络接口状态变化**(如Wi-Fi连接断开),可结合`netlink`: ```c #include <linux/rtnetlink.h> #include <net/if.h> void netlink_listener() { struct sockaddr_nl sa; int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; sa.nl_groups = RTMGRP_LINK; // 监听链路层变化 bind(sock, (struct sockaddr*)&sa, sizeof(sa)); while(1) { char buffer[4096]; struct nlmsghdr *nh; recv(sock, buffer, sizeof(buffer), 0); for(nh = (struct nlmsghdr*)buffer; NLMSG_OK(nh, sizeof(buffer)); nh = NLMSG_NEXT(nh, sizeof(buffer))) { if(nh->nlmsg_type == RTM_NEWLINK) { struct ifinfomsg *ifi = NLMSG_DATA(nh); printf("接口 %d 状态变化: %s\n", ifi->ifi_index, (ifi->ifi_flags & IFF_UP) ? "UP" : "DOWN"); } } } } ``` --- ### 方案选型建议 | 场景 | 推荐方案 | 优势 | |-----------------------|-------------------------|-------------------------------| | 简单TCP/UDP服务 | 原生epoll实现 | 无外部依赖,适合资源受限环境 | | 复杂事件处理 | libevent/libuv | 简化多事件源管理 | | 网络接口状态监控 | netlink监听 | 实时获取底层网络状态 | | IoT设备通信 | MQTT+回调(如Mosquitto)| 适合云端指令下发 | --- ### 最佳实践 1. **错误处理**:所有系统调用必须检查返回值 2. **资源释放**:及时关闭fd/释放event对象 3. **信号处理**:为`SIGINT`添加处理函数以优雅退出 4. **日志记录**:使用syslog替代printf保证可靠性 5. **性能调优**:根据场景选择LT/ET模式,调整内核网络参数(如`/proc/sys/net/core/rmem_max`)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值