sock_ev——linux平台socket事件框架(socket代理类)

本文详细介绍了如何使用代理类封装socket的基本操作,包括创建、销毁、获取文件描述符、打开、连接、接受、发送、接收等。同时,阐述了如何获取事件类型并处理事件,以及设置和清除回调函数。

前面分析了对socket基本操作的封装,并按照数据的传送方式写了两个类,本篇将写一个代理类提供给库的使用者使用的类。

/***************************************************************************************
****************************************************************************************
* FILE		: socket.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/11		Initial Version
   
****************************************************************************************
****************************************************************************************/


#ifndef _SOCKET_H_
#define _SOCKET_H_

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "sock_ev.h"

class EventLoop;
class CommBase;
class SockAddr;


/*==================================================================
* Function	: Socket
* Description	: Socket used for app, adapter CommBase
==================================================================*/
class Socket
{
public:

  /*==================================================================
  * Function	: Socket.create
  * Description	: static function used for create Socket,
  * Return Value: Socket pointer
  ==================================================================*/
  static Socket* create();

  /*==================================================================
  * Function	: Socket.destroy
  * Description	: static function used for destroy Socket created by Socket.create
  * Input Para	: sock_--socket pointer
  ==================================================================*/
  static void destroy(Socket* &sock_);

  /*==================================================================
  * Function	: Socket.getFd
  * Description	: get socket fd
  * Return Value: socket fd,if fd is not avaliable -1 is return
  ==================================================================*/
  int getFd();

  /*==================================================================
  * Function	: Socket.open
  * Description	: open socket in server of unconnection-mode
  * Input Para	: socket address uri
  * Return Value: if success return true, or else false is return
  ==================================================================*/
  bool open(const char *uri_);

  /*==================================================================
  * Function	: Socket.connect
  * Description	: connection-mode client connect to server
  * Input Para	: socket address uri
  * Return Value: if success return true, or else false is return
  ==================================================================*/
  bool connect(const char *uri_);

  /*==================================================================
  * Function	: Socket.accept
  * Description	: connection-mode server accept client connection
  * Input Para	: sock_--stand for client socket pointer
  * Return Value: if success return true, or else false is return
  ==================================================================*/
  bool accept(Socket *sock_);

  /*==================================================================
  * Function	: Socket.send
  * Description	: send data
  * Input Para	: data_--data pointer
  * Input Para	: len_--data length
  * Return Value: the number of characters sent
  ==================================================================*/
  int send(void *data_, 
      uint32_t len_);

  /*==================================================================
  * Function	: Socket.send
  * Description	: send data
  * Input Para	: data_--data pointer
  * Input Para	: len_--data length
  * Input Para	: to_--the address of the target
  * Return Value: the number of characters sent
  ==================================================================*/
  int send(void *data_, 
      uint32_t len_, 
      const char *to_);

  /*==================================================================
  * Function	: Socket.recv
  * Description	: recv data
  * Input Para	: data_--data pointer
  * Input Para	: len_--data length
  * Return Value: the number of characters received
  ==================================================================*/
  int recv(void *data_, 
      uint32_t len_);

  /*==================================================================
  * Function	: Socket.recv
  * Description	: recv data
  * Input Para	: data_--data pointer
  * Input Para	: len_--data length
  * Input Para	: from_--the address of the source
  * Return Value: the number of characters received
  ==================================================================*/
  int recv(void *data_, 
      uint32_t len_,
      char *from_);

  /*==================================================================
  * Function	: Socket.getEvt
  * Description	: get event type
  * Return Value: already register event
  ==================================================================*/
  EventType getEvt();

  /*==================================================================
  * Function	: Socket.processEvent
  * Description	: process Event
  * Input Para	: evt_--event
  ==================================================================*/
  void processEvent(EventType evt_);

  /*==================================================================
  * Function	: Socket.setCallBack
  * Description	: set calback function
  ==================================================================*/
  void setCallBack(EventLoop *loop_,
      EvCallBack cb_,
      EventType evt_,
      void *arg_);

  /*==================================================================
  * Function	: Socket.clearCallBack
  * Description	: clear calback function
  ==================================================================*/
  void clearCallBack(EventType evt_);

private:

  EventType   evt;
  CommBase   *comm;
  SockAddr   *addr;
  EventLoop  *loop;
  EvCallBack  rdCb;
  EvCallBack  wrCb;
  void       *rdArg;
  void       *wrArg;
  
  // Disable copy construction and assignment.
  Socket();
  virtual ~Socket();
  Socket(const Socket&);
  const Socket &operator = (const Socket&);
};

#endif /*_SOCKET_H_*/

上面是头文件有比较详细的注释,通过函数名也可以看出函数的意义。

 

/***************************************************************************************
****************************************************************************************
* FILE		: socket.cc
* 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/11		Initial Version
   
****************************************************************************************
****************************************************************************************/


#include "socket.h"
#include "sock_ev.h"
#include "socket_addr.h"
#include "socket_base.h"

using namespace std;

Socket::Socket()
{
  evt  = 0;
  comm = NULL;
  addr = NULL;
  loop = NULL;
  rdCb = NULL;
  wrCb = NULL;
  rdArg= NULL;
  wrArg= NULL;
}

Socket::~Socket()
{
  if(NULL != comm) delete comm;
  
  if(NULL != addr) delete addr;
}

Socket* Socket::create()
{
  Socket *sock = new Socket();
  if(NULL == sock)
  {
    logTrace("new Socket() failed");
    return NULL;
  }

  return sock;
}
void Socket::destroy(Socket* &sock_)
{
  if(NULL != sock_) delete sock_;

  sock_ = NULL;
}
int Socket::getFd()
{
  return comm->getSockFd();
}
bool Socket::open(const char *uri_)
{
  addr = new SockAddr(uri_);
  if(NULL == addr)
  {
    logTrace("new SockAddr(%s) failed", uri_);
    return false;
  }

  if(!addr->parseUri())
  {
    logTrace("parseUri() failed;uri:%s", uri_);
    return false;
  }
  
  int type = addr->getType();
  if(SOCK_STREAM == type)
  {
    comm = new StreamSock();
  }
  else if(SOCK_DGRAM == type)
  {
    comm = new DgramSock();
  }
  else
  {
    logTrace("addr.type is invalid;type:%s", type);
    return false;
  }

  if(NULL == comm)
  {
    logTrace("new StreamSock() failed");
    return false;
  }
  
  if(!comm->openSock(*addr))
  {
    logTrace("StreamSock.openSock() failed");
    return false;
  }

  return true;
}
bool Socket::connect(const char *uri_)
{
  addr = new SockAddr(uri_);
  if(NULL == addr)
  {
    logTrace("new SockAddr(%s) failed", uri_);
    return false;
  }

  if(!addr->parseUri())
  {
    logTrace("parseUri() failed;uri:%s", uri_);
    return false;
  }
  
  int type = addr->getType();
  if(SOCK_STREAM == type)
  {
    comm = new StreamSock();
  }
  else if(SOCK_DGRAM == type)
  {
    comm = new DgramSock();
  }
  else
  {
    logTrace("addr.type is invalid;type:%s", type);
    return false;
  }

  if(NULL == comm)
  {
    logTrace("new StreamSock() failed");
    return false;
  }
  
  if(!comm->connectTo(*addr))
  {
    logTrace("StreamSock.connectTo() failed");
    return false;
  }

  return true;
}
bool Socket::accept(Socket *sock_)
{
  sock_->addr = new SockAddr(addr->getDomain(), addr->getType());
  if(NULL == sock_->addr)
  {
    logTrace("new SockAddr(%d, %d) failed", addr->getDomain(), addr->getType());
    return false;
  }

  int acceptFd = comm->acceptSock(*(sock_->addr));
  if(-1 == acceptFd)
  {
    logTrace("accetp connection is failed");
    return false;
  }

  int type = addr->getType();
  if(SOCK_STREAM == type)
  {
    sock_->comm = new StreamSock();
  }
  else if(SOCK_DGRAM == type)
  {
    sock_->comm = new DgramSock();
  }
  else
  {
    logTrace("addr.type is invalid;type:%s", type);
    return false;
  }

  if(NULL == sock_->comm)
  {
    logTrace("new StreamSock() failed");
    return false;
  }
  
  sock_->comm->setSockFd(acceptFd);

  return true;
  
}
int Socket::send(void *data_, 
    uint32_t len_)
{
  return comm->sendData(data_, len_);
}
int Socket::send(void *data_, 
    uint32_t len_, 
    const char *to_)
{
  SockAddr *tmpAddr = new SockAddr(to_);
  if(NULL == tmpAddr)
  {
    logTrace("new SockAddr(%s) failed", to_);
    return -1;
  }

  if(!tmpAddr->parseUri())
  {
    logTrace("parseUri() failed;uri:%s", to_);
    delete tmpAddr;
    return false;
  }

  int sendLen = comm->sendData(data_, len_, *tmpAddr);

  delete tmpAddr;

  return sendLen;
}
int Socket::recv(void *data_, 
    uint32_t len_)
{
  return comm->recvData(data_, len_);
}
int Socket::recv(void *data_, 
    uint32_t len_,
    char *from_)
{
  SockAddr *tmpAddr = new SockAddr(addr->getDomain(), addr->getType());
  if(NULL == tmpAddr)
  {
    logTrace("new SockAddr(%s) failed");
    return -1;
  }

  int sendLen = comm->recvData(data_, len_, *tmpAddr);

  if(NULL != from_)
  {
    string tmpStr;
    if(tmpAddr->toStr(tmpStr))
    {
      sprintf(from_, "%s", tmpStr.c_str());
    }
    else
    {
      sprintf(from_, "%s", "Invalid socket address string");
    }
  }

  delete tmpAddr;

  return sendLen;
}
EventType Socket::getEvt()
{
  return evt;
}
void Socket::processEvent(EventType evt_)
{
  if(NULL != rdCb && evt_&evRead)
  {
    rdCb(loop, this, evRead, rdArg);
  }
  else if(NULL != wrCb && evt_&evWrite)
  {
    wrCb(loop, this, evWrite, wrArg);
  }
  else
  {
    logTrace("eventType:%d;reCb:%p,wrCb:%p", evt_, rdCb, wrCb);
  }
}

void Socket::setCallBack(EventLoop *loop_, 
    EvCallBack cb_,
    EventType evt_,
    void *arg_)
{
  loop = loop_;
  
  if(evt_& evRead)
  {
    evt |= evRead;
    rdCb = cb_;
    rdArg= arg_;
  }

  if(evt_& evWrite)
  {
    evt |= evWrite;
    wrCb = cb_;
    wrArg= arg_;
  }

}

void Socket::clearCallBack(EventType evt_)
{
  if(evt_&evRead)
  {
    evt &= ~evRead;
    rdCb = NULL;
    rdArg= NULL;
  }

  if(evt_&evWrite)
  {
    evt &= ~evRead;
    wrCb = NULL;
    wrArg= NULL;
  }

  if(0 == evt)
  {
    loop = NULL;
  }
}

基本上就是对前篇中封装的两个类的调用,字节流与数据包的区分在open、connect、accept三个函数中有所处理。

<think>我们正在查找Linux内核源码中创建SOCK_DGRAM类型socket的具体实现位置。根据用户上一次的问题,我们知道标准Linux内核不支持SOCK_DGRAM类型的ICMP套接字,但支持其他协议(如UDP)的SOCK_DGRAM套接字。现在,我们需要在内核源码中找到创建SOCK_DGRAM套接字的代码。 在Linux内核中,socket系统调用的实现会调用`sock_create`函数,然后根据指定的协议族、套接字类型和协议来创建套接字。对于SOCK_DGRAM类型,我们主要关注AF_INET(IPv4)和AF_INET6(IPv6)协议族。 关键步骤: 1. 内核中socket系统调用的入口是`__sys_socket`函数(在`net/socket.c`中)。 2. 该函数会调用`sock_create`,然后调用`__sock_create`。 3. 在`__sock_create`中,根据协议族查找对应的`net_proto_family`结构,并调用其`create`函数。 4. 对于AF_INET,其`create`函数是`inet_create`(在`net/ipv4/af_inet.c`中)。 因此,我们应重点查看`net/ipv4/af_inet.c`文件中的`inet_create`函数,该函数处理IPv4协议族的套接字创建,包括SOCK_DGRAM类型。 具体到SOCK_DGRAM套接字的创建: - 在`inet_create`函数中,会根据套接字类型(type)和协议(protocol)来查找对应的协议(`inetsw`数组中的项)。 - `inetsw`数组包含了支持的协议列表,其中SOCK_DGRAM类型对应UDP协议(`IPPROTO_UDP`)等。 所以,创建SOCK_DGRAM套接字的核心代码在`net/ipv4/af_inet.c`的`inet_create`函数中。 此外,对于AF_UNIX域套接字(本地套接字)的SOCK_DGRAM类型,其实现位于`net/unix/af_unix.c`中,但用户的问题主要关注的是网络套接字(AF_INET),因此我们重点放在AF_INET。 现在,我们总结一下源码位置: 1. **socket系统调用的入口**:`net/socket.c`中的`__sys_socket`函数。 2. **套接字创建通用函数**:`net/socket.c`中的`__sock_create`函数。 3. **IPv4协议族的套接字创建**:`net/ipv4/af_inet.c`中的`inet_create`函数。 - 在此函数中,通过遍历`inetsw`数组(该数组在`af_inet.c`中初始化)来匹配类型和协议,找到对应的`struct proto`操作集。 - 对于SOCK_DGRAM类型,通常匹配到UDP协议(`IPPROTO_UDP`),其操作集为`udp_prot`(定义在`net/ipv4/udp.c`)。 因此,对于用户的问题,我们可以这样回答: 在Linux内核源码中,创建SOCK_DGRAM类型套接字的主要实现位于`net/ipv4/af_inet.c`的`inet_create`函数中。该函数根据套接字类型(SOCK_DGRAM)和协议号(如IPPROTO_UDP)来初始化套接字,并设置对应的协议操作函数集(如UDP的操作集为`udp_prot`)。 另外,对于其他协议族(如AF_UNIX)的SOCK_DGRAM套接字,实现位于`net/unix/af_unix.c`中。 下面,我们给出一个简化的代码路径: 1. 系统调用入口:`__sys_socket` (net/socket.c) 2. 调用链:`__sys_socket` -> `sock_create` -> `__sock_create` 3. 在`__sock_create`中,根据协议族调用对应的create函数: - 对于AF_INET:调用`inet_create` (net/ipv4/af_inet.c) 4. 在`inet_create`中,根据type(如SOCK_DGRAM)和protocol(如IPPROTO_UDP)选择对应的协议操作集(prot): ```c // 在inet_create函数中 list_for_each_entry_rcu(answer, &inetsw[sock->type], list) { // 匹配协议 } // 然后初始化sock ``` 因此,具体的SOCK_DGRAM套接字的创建逻辑在`inet_create`函数中。 注意:用户可能还关心其他协议族,但根据问题上下文(上一次问题是关于ICMP的,但这次是查找SOCK_DGRAM的通用实现),我们主要关注AF_INET。 相关问题: 1. Linux内核中如何添加新的套接字类型支持? 2. UDP协议在Linux内核中是如何实现的? 3. 不同协议族的套接字创建过程有何异同?</think>### Linux 内核源码中创建 `SOCK_DGRAM` 套接字的核心实现位置 在 Linux 内核中,`SOCK_DGRAM` 套接字的创建实现主要分布在以下关键位置: #### 1. **系统调用入口** 位于 `net/socket.c` 中的 `__sys_socket` 函数,这是所有套接字类型创建的通用入口: ```c // net/socket.c int __sys_socket(int family, int type, int protocol) { struct socket *sock; int retval; ... retval = sock_create(family, type, protocol, &sock); // 核心创建函数 ... } ``` [^1] #### 2. **协议无关的套接字创建** 在 `__sock_create` 函数中实现通用套接字初始化逻辑(同文件): ```c int __sock_create(struct net *net, int family, int type, int protocol, struct socket **res, int kern) { struct socket *sock; const struct net_proto_family *pf; ... sock = sock_alloc(); // 分配套接字结构体 sock->type = type; // 设置类型为 SOCK_DGRAM ... pf = rcu_dereference(net_families[family]); // 获取协议族操作集 err = pf->create(net, sock, protocol, kern); // 调用协议族特定创建函数 ... } ``` [^1] #### 3. **IPv4 协议族的实现** 对于 `AF_INET` 族,具体实现在 `net/ipv4/af_inet.c` 的 `inet_create` 函数: ```c // net/ipv4/af_inet.c static int inet_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; ... // 匹配套接字类型和协议 list_for_each_entry_rcu(answer, &inetsw[sock->type], list) { if (protocol == answer->protocol) { if (protocol != IPPROTO_IP) break; } } ... // 对 SOCK_DGRAM 的特殊处理 if (sock->type == SOCK_DGRAM) { sk->sk_prot = answer->prot; // 设置协议操作集(如 UDP) sk->sk_no_check_tx = 1; // 禁用发送校验和 } ... } ``` [^2] #### 4. **UDP 协议操作集** 当协议为 `IPPROTO_UDP` 时,内核会关联 `udp_prot` 操作集(定义在 `net/ipv4/udp.c`): ```c // net/ipv4/udp.c struct proto udp_prot = { .name = "UDP", .owner = THIS_MODULE, .close = udp_lib_close, .connect = ip4_datagram_connect, .sendmsg = udp_sendmsg, // 数据报发送函数 .recvmsg = udp_recvmsg, // 数据报接收函数 ... }; ``` #### 关键代码路径总结: ``` __sys_socket() // 入口 (net/socket.c) └── sock_create() └── __sock_create() └── pf->create() = inet_create() // AF_INET 协议族 (net/ipv4/af_inet.c) ├── 匹配 SOCK_DGRAM 类型 └── 关联 udp_prot 操作集 // UDP 实现 (net/ipv4/udp.c) ``` ### 验证方法 1. **调试代码**:在内核添加打印语句跟踪 `sock->type == SOCK_DGRAM` 分支 2. **动态追踪**:使用 `ftrace` 跟踪函数调用栈: ```bash echo function > /sys/kernel/debug/tracing/current_tracer echo __sys_socket > /sys/kernel/debug/tracing/set_ftrace_filter cat /sys/kernel/debug/tracing/trace ``` ### 典型工作流程 ```mermaid sequenceDiagram User->>Kernel: socket(AF_INET, SOCK_DGRAM, 0) Kernel->>__sys_socket: 系统调用入口 __sys_socket->>inet_create: 调用 AF_INET 创建函数 inet_create->>UDP: 关联 udp_prot 操作集 UDP-->>User: 返回套接字描述符 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值