分类目录归档:LINUX C & ARM & C51

IP 总体结构 详解

1.中心环节

IP是整个协议软件的中心环节,不要把IP软件简单的分割成输入和输出两部分,因为输入和输出的界限很模糊.以下把重点放在网关上.

2. IP软件设计思想

 * 统一的输入队列以及统一的选路过程
 * 独立的IP进程
 * 本地主机接口

3. IP软件结构和数据报流程

4. IP软件结构和数据报流程

4.1 选择传入数据报的策略
挑选数据报并为其选择路由的IP程序段实现了一个重要策略:它决定了数据报来源的相对优先级.
公平分配优先权,例用策略为循环法(round-robin)

/* ipgetp.c – ipgetp */

#include <conf.h>
#include <kernel.h>
#include <network.h>
#include <proc.h>

static    int    ifnext = NI_LOCAL;        //接口索引

/*————————————————————————
 * ipgetp  —  choose next IP input queue and extract a packet
 *————————————————————————
 */
struct ep *
ipgetp(int *pifnum)
{
    struct    ep    *pep;                        //以太帧结构体
    int        i;

    recvclr();                            /* 确保没有旧分组正在等待 */
    while (TRUE) {
        for (i=0; i < Net.nif; ++i, ++ifnext) {        //遍历每个接口
            if (ifnext >= Net.nif)            //等于或大于最大接口数
                ifnext = 0;
            if (nif[ifnext].ni_state == NIS_DOWN)    //此接口关闭
                continue;
            if (pep = NIGET(ifnext)) {            /*NIGET宏,提取并返回第一个数据*/
                *pifnum = ifnext;            //返回接口的序号
                return pep;                //返回以太帧的结构体指针
            }
        }
        ifnext = receive();    //如没有数据则阻塞,当有数据到达时,返回数据报到达的接口指向
    }
    /* can't reach here */
}

4.2 IP使用的常量定义

/* ip.h – IP_HLEN, IP_CLASS{A,B,C,D,E} */

/* 互联网协议(IP)的常量和数据报格式        */

#define    IP_ALEN    4        /* 以字节为单位的IP地址的长度(字节)    */
typedef    unsigned long IPaddr;    /* 互联网地址                */

#if    BYTE_ORDER == BIG_ENDIAN
#define    IP_CLASSA(x) (((x) & 0x80000000) == 0)        /* A类的IP */
#define    IP_CLASSB(x) (((x) & 0xc0000000) == 0x80000000)    /* B类的IP */
#define    IP_CLASSC(x) (((x) & 0xe0000000) == 0xc0000000)    /* C类的IP */
#define    IP_CLASSD(x) (((x) & 0xf0000000) == 0xe0000000)    /* D类的IP */
#define    IP_CLASSE(x) (((x) & 0xf8000000) == 0xf0000000)    /* E类的IP */
#else    /* BYTE_ORDER */
#define    IP_CLASSA(x)    (((x) & 0x80) == 0x00)    /* A类的IP地址    */
#define    IP_CLASSB(x)    (((x) & 0xc0) == 0x80)    /* B类的IP地址    */
#define    IP_CLASSC(x)    (((x) & 0xe0) == 0xc0)    /* C类的IP地址    */
#define    IP_CLASSD(x)    (((x) & 0xf0) == 0xe0)    /* D类的IP地址    */
#define    IP_CLASSE(x)    (((x) & 0xf8) == 0xf0)    /* E类的IP地址    */
#endif    /* BYTE_ORDER */

/* 部分分配协议编号 */

#define    IPT_ICMP    1    /* ICMP数据包协议类型    */
#define    IPT_IGMP    2    /* IGMP数据包协议类型    */
#define    IPT_TCP        6    /* TCP数据包协议类型    */
#define IPT_EGP        8    /* EGP数据包协议类型    */
#define    IPT_UDP        17    /* UDP数据包的协议类型    */
#define    IPT_OSPF    89    /* OSPF数据包协议类型    */

struct    ip    {
    u_char    ip_verlen;    /*  IP版本及头长度 (in longs)*/
    u_char    ip_tos;        /* 服务类型    */
    u_short    ip_len;        /* 包总长度(字节)    */
    short    ip_id;        /* 数据包的id                */
    short     ip_fragoff;    /* 碎片偏移(8字节的)    */
    u_char    ip_ttl;        /* 生存时间,在网关的跳    */
    u_char    ip_proto;    /*  IP协议(见IPT_ *)*/
    short    ip_cksum;    /* 头校验和             */
    IPaddr    ip_src;        /* 源IP地址            */
    IPaddr    ip_dst;        /* 目的地IP地址        */
    u_char    ip_data[1];    /* 可变长度的数据            */
};

#define    IP_VERSION    4    /* 当前版本值        */
#define    IP_MINHLEN    5    /* 最小的IP头长度 (in longs)    */
#define    IP_TTL        255    /* 生存时间初始值        */

#define    IP_MF        0x2000    /* 更多的碎片位            */
#define    IP_DF        0x4000    /* 不分片位            */
#define    IP_FRAGOFF    0x1fff    /* 碎片偏移 fragment offset mask*/
#define    IP_PREC        0xe0    /* 优先的服务 precedence portion of TOS*/

/* IP优先值 */

#define    IPP_NETCTL    0xe0    /* 网络控制            */
#define    IPP_INCTL    0xc0    /* Internet控制            */
#define    IPP_CRIT    0xa0    /* 关键 critical                */
#define    IPP_FLASHO    0x80    /* flash over-ride            */
#define    IPP_FLASH    0x60    /* flash                 */
#define    IPP_IMMED    0x40    /* immediate                */
#define    IPP_PRIO    0x20    /* 优先                */
#define    IPP_NORMAL    0x00    /* 正常                */

/*宏来计算一个数据报的报头的长度(字节)        */
#define    IP_HLEN(pip)    ((pip->ip_verlen & 0xf)<<2)
#define    IPMHLEN        20    /* 最小的IP头长度(字节)    */

/* IP选项 */
#define    IPO_COPY    0x80    /* 分片复制 copy on fragment mask        */
#define IPO_CLASS    0x60    /* 选项类 option class                */
#define    IPO_NUM        0x17    /* 选项编号option number        */

#define    IPO_EOOP    0x00    /* 选项结束            */
#define    IPO_NOP        0x01    /* 没有操作 no operation        */
#define    IPO_SEC        0x82    /* DoD security/compartmentalization    */
#define    IPO_LSRCRT    0x83    /* 松散源路由 loose source routing        */
#define    IPO_SSRCRT    0x89    /* 严格的源路由strict source routing        */
#define    IPO_RECRT    0x07    /* 记录路线                */
#define    IPO_STRID    0x88    /* 流编号                */
#define    IPO_TIME    0x44    /* 互联网时间戳            */

#define    IP_MAXLEN    BPMAXB-EP_HLEN    /*  IP数据报的最大长度    */

/*IP进程的信息 */

extern    PROCESS        ipproc();
#define    IPSTK        512    /* IP过程堆栈大小        */
#define    IPPRI        100    /* IP运行在高优先级        */
#define    IPNAM        "ip"    /* IP过程的名字            */
#define    IPARGC        0    /* count of args to IP             */

extern IPaddr    ip_maskall;    /* = 255.255.255.255            */
extern IPaddr    ip_anyaddr;    /* = 0.0.0.0                */
extern IPaddr    ip_loopback;    /* = 127.0.0.1                */

extern    int    ippid, gateway;

struct ip *iph2net(struct ip *), *ipnet2h(struct ip *);
unsigned short cksum(WORD *, unsigned);
int ipsend(IPaddr, struct ep *, unsigned, u_char, u_char, u_char);
int ipputp(unsigned, IPaddr, struct ep *);
Bool isbrc(IPaddr);

4.3 IP进程的结构 (IP进程允许被阻塞)

/* ipproc.c – ipproc */

#include <conf.h>
#include <kernel.h>
#include <network.h>

struct    ep    *ipgetp(int *);
struct    route    *rtget(IPaddr, Bool);

/*————————————————————————
 *  ipproc  –   处理一个从网络中来的IP数据包
 *————————————————————————
 */
PROCESS
ipproc(void)
{
    struct    ep    *pep;
    struct    ip    *pip;
    struct    route    *prt;
    Bool        nonlocal;
    int        ifnum;

    ippid = getpid();                        /* 得到进程的ID    */

    signal(Net.sema);                        /* 信号进行初始化*/

    while (TRUE) {
        pep = ipgetp(&ifnum);                /* 选取一个以太网数据报*/
        pip = (struct ip *)pep->ep_data;            /* 得到此报包含的更高级报地址*/

        if ((pip->ip_verlen>>4) != IP_VERSION) {        /* 判断是否为IP数据报*/
            IpInHdrErrors++;
            freebuf(pep);
            continue;
        }
        if (IP_CLASSE(pip->ip_dst)) {            /* 判断是否为E类地址*/
            IpInAddrErrors++;
            freebuf(pep);
            continue;
        }
        if (ifnum != NI_LOCAL) {                /* 此报不是本机产生的*/
            if (cksum((WORD *)pip, IP_HLEN(pip))) {    /* 计算检验和*/
                IpInHdrErrors++;
                freebuf(pep);
                continue;
            }
            ipnet2h(pip);
            pep->ep_order |= EPO_IP;
        }
        prt = rtget(pip->ip_dst, (ifnum == NI_LOCAL));    /* 选择路由*/

        if (prt == NULL) {                    /* 如果路由不存在*/
            if (gateway) {                /* 如网关存在发送ICMP不可达*/
                iph2net(pip);
                pep->ep_order &= ~EPO_IP;
                icmp(ICT_DESTUR, ICC_NETUR,
                        pip->ip_src, pep, 0);
            } else {
                IpOutNoRoutes++;
                freebuf(pep);
            }
            continue;
        }
        nonlocal = ifnum != NI_LOCAL && prt->rt_ifnum != NI_LOCAL; /* 不是本地地址*/
        if (!gateway && nonlocal) {                /* 不是网关也不是本地地址*/
            IpInAddrErrors++;
            freebuf(pep);
            rtfree(prt);
            continue;
        }
        if (nonlocal)
            IpForwDatagrams++;
        /* 如果我们发件人,填写在源IP */

        if (ifnum == NI_LOCAL) {                /* 如果网络接口为本地*/
            if (pip->ip_src == ip_anyaddr)       
                if (prt->rt_ifnum == NI_LOCAL)    /* 路由的接口为本地*/
                    pip->ip_src = pip->ip_dst;
                else
                    pip->ip_src =
                        nif[prt->rt_ifnum].ni_ip;
        } else if (–(pip->ip_ttl) == 0 &&        /* ttl 为零,且路由接口不为本地*/
                prt->rt_ifnum != NI_LOCAL) {
            IpInHdrErrors++;
            iph2net(pip);
            pep->ep_order &= ~EPO_IP;
            icmp(ICT_TIMEX, ICC_TIMEX, pip->ip_src, pep, 0);    /*发送ICMP超时报文*/
            rtfree(prt);
            continue;
        }
        ipdbc(ifnum, pep, prt);    /* 处理定向广播    */
        ipredirect(pep, ifnum, prt); /* 做重定向,如果需要的话*/
        if (prt->rt_metric != 0)
            ipputp(prt->rt_ifnum, prt->rt_gw, pep);
        else
            ipputp(prt->rt_ifnum, pip->ip_dst, pep);
        rtfree(prt);
    }
}

int    ippid, gateway, bsdbrc;

4.4 校验和的计算

/* cksum.c – cksum */

/*————————————————————————
 *  cksum  –  Return 16-bit ones complement of 16-bit ones complement sum
 *————————————————————————
 */
unsigned short
cksum(buf, nbytes)
unsigned short    *buf;
int        nbytes;
{
    unsigned long    sum;
    unsigned short    tmp;
    int        nwords;

    nwords = nbytes / 2;            /* 将字节转成字,也就是16个的个数 */
    for (sum=0; nwords>0; nwords–)    /* 将16位为单位做累加*/
        sum += *buf++;
    if (nbytes & 1) {            /* 如长度不是16位的倍数则补齐*/
        tmp = *(unsigned char *)buf;
        sum += tmp;
    }
    sum = (sum >> 16) + (sum & 0xffff);    /* 高位低位相加   */
    sum += (sum >> 16);            /* 上一步溢出时,将溢出位也加到sum中 */
    return (unsigned short)~sum;        /* 返回sum的反码*/
}

4.5 处理定向广播

主要做了两件事,复制一个副本,然后正常发送
/* ipdbc.c – ipdbc */

#include <conf.h>
#include <kernel.h>
#include <network.h>

struct    route *rtget(IPaddr, Bool);

/*————————————————————————
 * ipdbc – handle IP directed broadcast copying
 *————————————————————————
 */
void
ipdbc(unsigned ifnum, struct ep *pep, struct route *prt)
{
    struct    ip    *pip = (struct ip *)pep->ep_data;
    struct    ep    *pep2;
    struct    route    *prt2;
    int        len;

    if (prt->rt_ifnum != NI_LOCAL)
        return;            /* 不是我们        */
    if (!isbrc(pip->ip_dst))
        return;            /* 不是广播    */

    prt2 = rtget(pip->ip_dst, RTF_LOCAL);    /* 选择一个路由 */
    if (prt2 == NULL)
        return;
    if (prt2->rt_ifnum == ifnum) {    /* 没有针对接口        */
        rtfree(prt2);
        return;
    }

    /* 定向广播,建立一个副本 */

    /* len = ether header + IP packet */

    len = EP_HLEN + pip->ip_len;
    if (len > EP_MAXLEN)                    /* 为副本分配缓冲区*/
        pep2 = (struct ep *)getbuf(Net.lrgpool);
    else
        pep2 = (struct ep *)getbuf(Net.netpool);
    if (pep2 == (struct ep *)SYSERR) {
        rtfree(prt2);
        return;
    }
    memcpy(pep2, pep, len);                /* 复制副本*/
    /* 发送一份拷贝到网络 */

    ipputp(prt2->rt_ifnum, pip->ip_dst, pep2);
    rtfree(prt2);

    return;        /* continue; "pep" goes locally in IP    */
}

4.6 识别一个广播地址

/* isbrc.c – isbrc */

#include <conf.h>
#include <kernel.h>
#include <sleep.h>
#include <network.h>

/*————————————————————————
 *  isbrc  –  Is "dest" a broadcast address?
 *————————————————————————
 */
Bool
isbrc(IPaddr dest)
{
    int    inum;

    /* 所有的0和全1的是广播地址 */

    if (dest == ip_anyaddr || dest == ip_maskall)    /* 如果为全0 或 全1 则返回真*/
        return TRUE;

    /* 检查真正的广播地址和BSD风格的网和子网 */

    for (inum=0; inum < Net.nif; ++inum)
        if (dest == nif[inum].ni_brc ||
            dest == nif[inum].ni_nbrc ||
            dest == nif[inum].ni_subnet ||
            dest == nif[inum].ni_net)
            return TRUE;

    return FALSE;
}

5. IP首部中的字节顺序

/* iph2net.c – iph2net */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 *  iph2net –  iph2net – 转换成一个IP数据包头从主机到网络字节顺序
 *————————————————————————
 */
struct ip *
iph2net(struct ip *pip)
{
    /* 注:不包括IP选项    */

    pip->ip_len = hs2net(pip->ip_len);
    pip->ip_id = hs2net(pip->ip_id);
    pip->ip_fragoff = hs2net(pip->ip_fragoff);
    return pip;
}

/* ipnet2h.c – ipnet2h */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 *  ipnet2h – 转换成一个IP数据包头从网络到主机字节顺序
 *————————————————————————
 */
struct ip *
ipnet2h(struct ip *pip)
{
    /* 注:不包括IP选项    */

    pip->ip_len = net2hs(pip->ip_len);
    pip->ip_id = net2hs(pip->ip_id);
    pip->ip_fragoff = net2hs(pip->ip_fragoff);
    return pip;
}

6. 向IP发送数据报

6.1 发送本地生成的数据报

/* ipsend.c – ipsend */

#include <conf.h>
#include <kernel.h>
#include <network.h>
#include <proc.h>

static ipackid = 1;            /* 静态变量 标识*/

/*————————————————————————
 *   ipsend – 发送一个IP数据报到指定地址
 *————————————————————————
 */
int
ipsend(IPaddr faddr, struct ep *pep, unsigned datalen,
    u_char proto, u_char ptos, u_char ttl)
{
    struct    ip *pip = (struct ip *) pep->ep_data;

    pep->ep_type = EPT_IP;                    /* 类型为网际协议*/
    pep->ep_order |= EPO_IP|EPO_NET;                /* 网络层 协议等级为1*/
    pip->ip_verlen = (IP_VERSION<<4) | IP_MINHLEN;        /* 协议版本 最小的IP头长度*/
    pip->ip_tos = ptos;                    /* 设置服务类型与优先级*/
    pip->ip_len = datalen+IP_HLEN(pip);            /* IP分组的长度*/
    pip->ip_id = ipackid++;                    /* 分组的标识*/
    pip->ip_fragoff = 0;                    /* 分片的偏移*/
    pip->ip_ttl = ttl;                    /* 生存时间*/
    pip->ip_proto = proto;                    /* 协议类型*/
    pip->ip_dst = faddr;                    /* 目标地址*/

    /*
     * 特殊情况的ICMP协议,因此源匹配目的地
     * on multi-homed hosts. 多穴主机??
     */
    if (pip->ip_proto != IPT_ICMP)                /* 不是ICMP分组*/
        pip->ip_src = ip_anyaddr;            /* IP的源地址为任意地址*/

    if (enq(nif[NI_LOCAL].ni_ipinq, pep, 0) < 0) {        /* 将分组放入本地主机接口队列中*/
        freebuf(pep);
        IpOutDiscards++;
    }
    send(ippid, NI_LOCAL);                    /* IP数据报阻塞并等待,发送报文给IP进程*/
    IpOutRequests++;
    return OK;
}
/* 特殊的IP地址 */

IPaddr    ip_anyaddr = 0;
#if    BYTE_ORDER == LITTLE_ENDIAN
IPaddr    ip_loopback = 0x0100007F;    /* 127.0.0.1 */
#else    /* BYTE_ORDER */
IPaddr    ip_loopback = 0x7F000001;    /* 127.0.0.1 */
#endif    /* BYTE_ORDER */

6.2 发送传入数据报

/* ip_in.c – ip_in */

#include <conf.h>
#include <kernel.h>
#include <network.h>
#include <proc.h>

/*————————————————————————
 *  ip_in – IP input function
 *————————————————————————
 */
int
ip_in(struct netif *pni, struct ep *pep)
{
    struct    ip    *pip = (struct ip *)pep->ep_data;

    IpInReceives++;
    if (enq(pni->ni_ipinq, pep, pip->ip_tos & IP_PREC) < 0) {
        IpInDiscards++;                        /* 如队列满则记录溢出差错并丢则报文*/
        freebuf(pep);
    }
    send(ippid, (pni-&nif[0]));                    /* 向IP进程发送消息*/
    return OK;
}

7. 表格的维护

/* slowtimer.c – slowtimer */

#include <conf.h>
#include <kernel.h>
#include <proc.h>
#include <sleep.h>
#include <date.h>
#include <network.h>

#define    STGRAN    1        /* 定时器粒度(延迟秒)    */

void arptimer(unsigned), ipftimer(unsigned), rttimer(unsigned);
#ifdef OSPF
void ospftimer(unsigned);
#endif /*OSPF*/

/*————————————————————————
 * slowtimer – 处理长期定期维护网络表
 *————————————————————————
 */
PROCESS
slowtimer(void)
{
    unsigned long    lasttime, now;    /* prev and current times (secs)*/
    int        delay;        /* 几秒钟内实际延迟    */

    signal(Net.sema);

    gettime(&lasttime);
    while (1) {
        sleep(STGRAN);        /* 延时*/
        gettime(&now);
        delay = now – lasttime;
        if (delay <= 0 || delay > 4*STGRAN)
            delay = STGRAN;    /* 时钟复位-likely clock reset */
        lasttime = now;
        arptimer(delay);
        ipftimer(delay);
        rttimer(delay);
#ifdef OSPF
        ospftimer(delay);
#endif /* OSPF */
    }
}
 

ARP 协议 代码 解释

1. ARP高速缓存的数据结构

/* arp.h – SHA, SPA, THA, TPA */

/* 互联网地址解析协议  (see RFCs 826, 920)    */

#define    AR_HARDWARE    1    /* 以太网硬件类型代码    */

/* Definitions of codes used in operation field of ARP packet */
    
#define    AR_REQUEST    1    /* ARP请求解决地址 ARP request to resolve address    */
#define    AR_REPLY    2    /* 回答解决的请求 reply to a resolve request    */

#define    RA_REQUEST    3    /* 反向ARP请求 reverse ARP request (RARP packets)*/
#define    RA_REPLY    4    /* 答复反向请求 reply to a reverse request (RARP ")*/

struct    arp    {
    u_short    ar_hwtype;    /* 硬件类型    */
    u_short    ar_prtype;    /* 协议类型    */
    u_char    ar_hwlen;    /* 硬件地址的长度    */
    u_char    ar_prlen;    /* 协议地址的长度    */
    u_short    ar_op;        /* ARP协议运作(见上述列表)    */
    u_char    ar_addrs[1];    /* 发送者和目标硬件及原地址    */
/*    char    ar_sha[???];     – 发件人的物理硬件地址    */
/*    char    ar_spa[???];     – 发送者的协议地址(IP地址)    */
/*    char    ar_tha[???];     – 目标物理硬件地址    */
/*    char    ar_tpa[???];     – 目标的协议地址(IP)    */
};

#define    SHA(p)    (&p->ar_addrs[0])                /*计算源物理地址*/
#define    SPA(p)    (&p->ar_addrs[p->ar_hwlen])            /*计算源协议地址*/
#define    THA(p)    (&p->ar_addrs[p->ar_hwlen + p->ar_prlen])    /*计算目标物理地址*/
#define    TPA(p)    (&p->ar_addrs[(p->ar_hwlen*2) + p->ar_prlen])    /*计算目标协议地址*/

#define    MAXHWALEN    EP_ALEN    /* 以太网地址最大长度            */
#define    MAXPRALEN    IP_ALEN    /* IP 地址最大长度            */

#define ARP_HLEN    8    /* ARP协议头长度            */

#define    ARP_TSIZE    50    /*  ARP缓存大小            */
#define    ARP_QSIZE    10    /*  ARP协议端口队列的大小        */

/* 缓存超时 cache timeouts */

#define ARP_TIMEOUT    600        /* 10 minutes                */
#define    ARP_INF        0x7fffffff    /* “无限”超时值 "infinite" timeout value    */
#define    ARP_RESEND    1    /* 如果没有在1秒内回复,重新发送            */
#define    ARP_MAXRETRY    4    /* 多少秒后放弃?? give up after ~30 seconds    */

struct    arpentry {        /* ARP缓存中的条目格式    */
    short    ae_state;    /* 本条目状态(见下文)    */
    short    ae_hwtype;    /* 硬件类型            */
    short    ae_prtype;    /* 协议类型            */
    char    ae_hwlen;    /* 硬件地址的长度        */
    char    ae_prlen;    /* 协议地址的长度        */
    struct netif *ae_pni;    /* 指向接口结构体的指针    */
    int    ae_queue;    /* 队列的数据包地址    */
    int    ae_attempts;    /* 重试,到目前为止 number of retries so far    */
    int    ae_ttl;        /* 生存时间                */
    u_char    ae_hwa[MAXHWALEN];    /* 硬件地址         */
    u_char    ae_pra[MAXPRALEN];    /* 协议地址         */
};

#define    AS_FREE        0    /* 表项空闲    */
#define    AS_PENDING    1    /* 表项使用中,但未绑定        */
#define    AS_RESOLVED    2    /* 表项正确绑定         */

/* RARP变量 */

extern int    rarppid;    /* 进程ID为RARP等待答复    */
extern int    rarpsem;    /* RARP服务获取信号量    */

/*  ARP协议的变量 */

extern struct    arpentry    arptable[ARP_TSIZE];

/*  ARP协议函数声明 */

int         arp_in(struct netif *, struct ep *);
struct arpentry *arpfind(u_char *, u_short, struct netif *);
struct arpentry *arpadd(struct netif *, struct arp *);


2. 搜索ARP高速缓存

/* arpfind.c – arpfind */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 * arpfind – find an ARP entry given a protocol address and interface
 *————————————————————————
 */
struct arpentry *
arpfind(u_char *pra, u_short prtype, struct netif *pni)    /*参数:协议地址,协议类型,接口结构指针*/
{
    struct arpentry    *pae;                /*定义ARP缓存结构体指针*/
    int        i;

    for (i=0; i<ARP_TSIZE; ++i) {            /*遍历ARP的缓存*/
        pae = &arptable[i];            /*缓存数组*/
        if (pae->ae_state == AS_FREE)        /*缓存为空闲接找下一个*/
            continue;
        if (pae->ae_prtype == prtype &&    
            pae->ae_pni == pni &&
            BLKEQU(pae->ae_pra, pra, pae->ae_prlen))
            return pae;            /*如果缓存中的协议类型与参数相同,并且接口与协议地址全相同,则返回这个ARP条目的指针。BLKEQU的定义 #define    BLKEQU(b1, b2, len)    (!memcmp((b1), (b2), len))*/
    }
    return 0;
}
 


3. ARP请求广播分组

/* arpsend.c – arpsend */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 * arpsend – broadcast an ARP request
 *    N.B. Assumes interrupts disabled
 *————————————————————————
 */
int
arpsend(struct arpentry *pae)                    /*指向ARP表项的指针*/
{
    struct    netif    *pni = pae->ae_pni;            /*指向接口结构指针*/
    struct    ep    *pep;                    /*贞结构指针*/
    struct    arp    *parp;                    /*ARP分组指针*/
    int        arplen;

    pep = (struct ep *) getbuf(Net.netpool);        /*为分组分配一个缓存区*/
    if ((int)pep == SYSERR)
        return SYSERR;
    memcpy(pep->ep_dst, pni->ni_hwb.ha_addr, pae->ae_hwlen);/*目的地播为物理广播地址*/
    pep->ep_type = EPT_ARP;                    /*帧类型*/
    pep->ep_order = EPO_NET;                /*网络工作层*/
    
    /*填写ARP包*/
    parp = (struct arp *) pep->ep_data;            /*以太网帧中ARP的数据*/
    parp->ar_hwtype = hs2net(pae->ae_hwtype);        /*硬件类型*/        
    parp->ar_prtype = hs2net(pae->ae_prtype);        /*协议类型*/
    parp->ar_hwlen = pae->ae_hwlen;                /*硬件地址长度*/
    parp->ar_prlen = pae->ae_prlen;                /*协议地址长度*/
    parp->ar_op = hs2net(AR_REQUEST);            /*操作字段,请求*/
    memcpy(SHA(parp), pni->ni_hwa.ha_addr, pae->ae_hwlen);    /*发送方硬件地址*/
    memcpy(SPA(parp), &pni->ni_ip, pae->ae_prlen);        /*发送方协议地址*/
    memset(THA(parp), 0, pae->ae_hwlen);            /*目标硬件地址清空*/
    memcpy(TPA(parp), pae->ae_pra, pae->ae_prlen);        /*目标协议地址*/
    arplen = ARP_HLEN + 2*(parp->ar_hwlen + parp->ar_prlen);/*ARP的总长度*/
    write(pni->ni_dev, pep, EP_HLEN+arplen);        /*发送分组*/
    return OK;
}


4. ARP输出过程

/* netwrite.c – netwrite */

#include <conf.h>
#include <kernel.h>
#include <network.h>

#include <ospf.h>

struct    arpentry    *arpalloc();
struct    arpentry     *arpfind(u_char *, u_short, struct netif *);
int    arpsend(struct arpentry *);
int    local_out(struct ep *);        //用于将目的是本地机的数据包通过loopback 发送到指定本机端口

/*#define    DEBUG */

/*————————————————————————
 * netwrite – write a packet on an interface, using ARP if needed
 *————————————————————————
 */
int
netwrite(struct netif *pni, struct ep *pep, unsigned len)
{
    struct    arpentry     *pae;            //ARP项目指针
    STATWORD        ps;            //状态字

    if (pni->ni_state != NIS_UP) {            
        freebuf(pep);
        return SYSERR;
    }                        //接口未打开则退出
    pep->ep_len = len;                //帧长度
#ifdef    DEBUG                        
if (pni != &nif[NI_LOCAL])
{
struct ip *pip = (struct ip *)pep->ep_data;
    if (pip->ip_proto == IPT_OSPF) {
        struct ospf *po = (struct ospf *)pip->ip_data;
/*        if (po->ospf_type != T_HELLO) { */
{
            kprintf("netwrite(pep %X, len %d)n", pep, len);
            pdump(pep);
        }
    }
}
#endif    /* DEBUG */
    if (pni == &nif[NI_LOCAL])        //如果是本地接口,用local_out 发送
        return local_out(pep);
    else if (isbrc(pep->ep_nexthop)) {    /*否则,首先检查下一跳是否是广播地址*/
        memcpy(pep->ep_dst, pni->ni_hwb.ha_addr, EP_ALEN);//填写目的物理地址为广播物理地址
        write(pni->ni_dev, pep, len);    //发送
        return OK;
    }
    /* 否则,该协议地址查找… */

    disable(ps);    /*如果下一跳地址不是广播地址,则先在arp 缓存表中查找是否有绑定的物理地址,有则直接发送,没有则先做arp 地址绑定。*/
    pae = arpfind((u_char *)&pep->ep_nexthop, pep->ep_type, pni);
    if (pae && pae->ae_state == AS_RESOLVED) {
        memcpy(pep->ep_dst, pae->ae_hwa, pae->ae_hwlen);
        restore(ps);
        return write(pni->ni_dev, pep, len);
    }
    if (IP_CLASSD(pep->ep_nexthop)) {    /*下一跳地址为d 类地址则直接返回*/
        restore(ps);
        return SYSERR;
    }
    if (pae == 0) {                //没有绑定,做arp 解析
        pae = arpalloc();        //分配空间
        pae->ae_hwtype = AR_HARDWARE;    //硬件类型
        pae->ae_prtype = EPT_IP;    //协议类型
        pae->ae_hwlen = EP_ALEN;    //硬件地址长度
        pae->ae_prlen = IP_ALEN;    //协议地址长度
        pae->ae_pni = pni;        //接口
        pae->ae_queue = EMPTY;        //队列地址
        memcpy(pae->ae_pra, &pep->ep_nexthop, pae->ae_prlen);    //协议地址
        pae->ae_attempts = 0;        //重试
        pae->ae_ttl = ARP_RESEND;    //生存时间
        arpsend(pae);            //发送
    }
    /*将等待ARP绑定的数据包存放在和ARP表项相关的队列中,如果队列为空,则分配一个。*/
    if (pae->ae_queue == EMPTY)    
        pae->ae_queue = newq(ARP_QSIZE, QF_NOWAIT);    //如没有队列,建立一个新队列
    if (enq(pae->ae_queue, pep, 0) < 0)    //将分组放入队列中,
        freebuf(pep);
    restore(ps);
    return OK;    //如果队列满,则失丢这个分组
}


5. ARP的输入处理

/* arpadd.c – arpadd */

#include <conf.h>
#include <kernel.h>
#include <network.h>

struct arpentry *arpalloc(void);

/*————————————————————————
 * arpadd – Add a RESOLVED entry to the ARP cache
 *     N.B. Assumes interrupts disabled
 *————————————————————————
 */
struct    arpentry *
arpadd(struct netif *pni, struct arp *parp)
{
    struct    arpentry    *pae;            //ARP缓存表项指针

    pae = arpalloc();                //为指针分配内存空间
    //从收到的ARP包中填写到缓存中
    pae->ae_hwtype = parp->ar_hwtype;        //硬件类型        
    pae->ae_prtype = parp->ar_prtype;        //协议类型
    pae->ae_hwlen = parp->ar_hwlen;            //硬件地址长度
    pae->ae_prlen = parp->ar_prlen;            //协议地址长度
    pae->ae_pni = pni;                //接口
    pae->ae_queue = EMPTY;                //队列地址
    memcpy(pae->ae_hwa, SHA(parp), parp->ar_hwlen);    //源物理地址
    memcpy(pae->ae_pra, SPA(parp), parp->ar_prlen);    //源协议地址
    pae->ae_ttl = ARP_TIMEOUT;            //生存时间
    pae->ae_state = AS_RESOLVED;            //表项的状态
    return pae;
}


6. 发送等待发送的分组

/* arpqsend.c – arpqsend */

#include <conf.h>
#include <kernel.h>
#include <network.h>

int netwrite(struct netif *, struct ep *, unsigned);

/*————————————————————————
 * arpqsend – write packets queued waiting for an ARP resolution
 *————————————————————————
 */
void
arpqsend(struct arpentry *pae)
{
    struct    ep    *pep;
    struct    netif    *pni;

    if (pae->ae_queue == EMPTY)        //待发送队列为空则返回
        return;

    pni = pae->ae_pni;            //接口
    while (pep = (struct ep *)deq(pae->ae_queue))    //遍历队列并发送,为空则释放队列
        netwrite(pni, pep, pep->ep_len);
    freeq(pae->ae_queue);
    pae->ae_queue = EMPTY;
}


7. ARP输入过程

/* arp_in.c – arp_in */

#include <conf.h>
#include <kernel.h>
#include <network.h>

void arpqsend(struct arpentry *);

/*————————————————————————
 *  arp_in  –  handle ARP packet coming in from Ethernet network
 *    N.B. – Called by ni_in– SHOULD NOT BLOCK
 *————————————————————————
 */
int
arp_in(struct netif *pni, struct ep *pep)
{
    struct    arp        *parp = (struct arp *)pep->ep_data;
    struct    arpentry    *pae;
    int            arplen;
    /*从网络字节顺序转换成主机字节顺序*/
    parp->ar_hwtype = net2hs(parp->ar_hwtype);
    parp->ar_prtype = net2hs(parp->ar_prtype);
    parp->ar_op = net2hs(parp->ar_op);
    //如果硬件类型或协议类型不匹配,则丢充此分组并返回
    if (parp->ar_hwtype != pni->ni_hwtype ||
        parp->ar_prtype != EPT_IP) {
        freebuf(pep);
        return OK;
    }
    //如果ARP表项中有此IP,就更新之
    if (pae = arpfind(SPA(parp), parp->ar_prtype, pni)) {
        memcpy(pae->ae_hwa, SHA(parp), pae->ae_hwlen);
        pae->ae_ttl = ARP_TIMEOUT;
    }
    //如果此分组不是发往此接口的,则丢弃之
    if (memcmp(TPA(parp), &pni->ni_ip, IP_ALEN)) {
        freebuf(pep);
        return OK;
    }
    //如果是发往本接口并没有此表项,则加入一个新表项
    if (pae == 0)
        pae = arpadd(pni, parp);
    //如果表项状态为正在绑定中,则发送队列中的分组
    if (pae->ae_state == AS_PENDING) {
        pae->ae_state = AS_RESOLVED;
        arpqsend(pae);
    }
    //如果收到的分组操作类型为请求ARP,则填写ARP包并发送,否则丢充分组
    if (parp->ar_op == AR_REQUEST) {
        parp->ar_op = AR_REPLY;
        memcpy(TPA(parp), SPA(parp), parp->ar_prlen);
        memcpy(THA(parp), SHA(parp), parp->ar_hwlen);
        memcpy(pep->ep_dst, THA(parp), EP_ALEN);
        memcpy(SHA(parp), pni->ni_hwa.ha_addr,
            pni->ni_hwa.ha_len);
        memcpy(SPA(parp), &pni->ni_ip, IP_ALEN);

        parp->ar_hwtype = hs2net(parp->ar_hwtype);
        parp->ar_prtype = hs2net(parp->ar_prtype);
        parp->ar_op = hs2net(parp->ar_op);

        arplen = ARP_HLEN + 2*(parp->ar_prlen + parp->ar_hwlen);

        write(pni->ni_dev, pep, arplen);
    } else
        freebuf(pep);
    return OK;
}


8. ARP高速缓存的管理

/* arpalloc.c – arpalloc */

#include <conf.h>
#include <kernel.h>
#include <proc.h>
#include <network.h>

void arpdq(struct arpentry *);

/*————————————————————————
 * arpalloc – allocate an entry in the ARP table
 *    N.B. Assumes interrupts DISABLED
 *————————————————————————
 */
struct arpentry *arpalloc()
{
    static    int    aenext = 0;    /*静态变量*/
    struct    arpentry *pae;
    int    i;
    //找空表项
    for (i=0; i<ARP_TSIZE; ++i) {
        if (arptable[aenext].ae_state == AS_FREE)
            break;
        aenext = (aenext + 1) % ARP_TSIZE;    //实现静态变量循环计数
    }
    pae = & arptable[aenext];
    aenext = (aenext + 1) % ARP_TSIZE;
    //如没有空表项,则使用这个当前的表项,判断为使用中但未绑定,并且有队列,如果有则丢弃队列中的分组
    if (pae->ae_state == AS_PENDING && pae->ae_queue >= 0)
        arpdq(pae);
    pae->ae_state = AS_PENDING;
    return pae;
}


9. 高速缓存的定期维护管理

/* arptimer.c – arptimer */

#include <conf.h>
#include <kernel.h>
#include <network.h>

void    arpdq(struct arpentry *);
int    arpsend(struct arpentry *);

/*————————————————————————
 * arptimer – Iterate through ARP cache, aging (possibly removing) entries
 *————————————————————————
 *    gran    – time since last iteration
 */
void
arptimer(int gran)
{
    struct arpentry *pae;
    STATWORD    ps;
    int        i;

    disable(ps);    /* 互斥量 */

    for (i=0; i<ARP_TSIZE; ++i) {
        if ((pae = &arptable[i])->ae_state == AS_FREE)
            continue;                /*空闲的条目*/
        if (pae->ae_ttl == ARP_INF)
            continue;                 /*不超时永久条目*/
        if ((pae->ae_ttl -= gran) <= 0)            /*寿命为0或负*/
            if (pae->ae_state == AS_RESOLVED)    /*条目绑定中但未正确*/
                pae->ae_state = AS_FREE;    /*仅改变状态为空闲*/
            else if (++pae->ae_attempts > ARP_MAXRETRY) {    /*超过最大广播次数*/
                pae->ae_state = AS_FREE;
                arpdq(pae);            /*清空队列*/
            } else {
                pae->ae_ttl = ARP_RESEND;    /*等于重发时间*/    
                arpsend(pae);            /*重新广播分组*/
            }
    }
    restore(ps);
}


10. 释放队列中的分组

/* arpdq.c – arpdq */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 * arpdq – destroy an arp queue that has expired
 *————————————————————————
 */
void
arpdq(struct arpentry *pae)
{
    struct    ep    *pep;
    struct    ip    *pip;

    if (pae->ae_queue < 0)        /* 队列为空,直接返回 */
        return;

    while (pep = (struct ep *)deq(pae->ae_queue)) {
        if (gateway && pae->ae_prtype == EPT_IP) {/*本机是网关并且数据报为IP数据报*/
            pip = (struct ip *)pep->ep_data;    //生成ICMP"目的站不可达"信息
            icmp(ICT_DESTUR, ICC_HOSTUR, pip->ip_src, pep, 0);
        } else
            freebuf(pep);            //释放帧
    }
    freeq(pae->ae_queue);                //释放队列
    pae->ae_queue = EMPTY;
}


11. ARP初始化

/* arpinit.c – arpinit */

#include <conf.h>
#include <kernel.h>
#include <proc.h>
#include <network.h>

/*————————————————————————
 *  arpinit  –  initialize data structures for ARP processing
 *————————————————————————
 */
void arpinit()
{
    int    i;

    rarpsem = screate(1);            /*创建一个与RARP一起使用的互斥型信号量*/
    rarppid = BADPID;            /*RARP进程号初始为-1*/

    for (i=0; i<ARP_TSIZE; ++i)
        arptable[i].ae_state = AS_FREE;    /*将状态全部置为AS_FREE*/
}

int    rarpsem;
int    rarppid;

struct    arpentry    arptable[ARP_TSIZE];    /*定义ARP高速缓存*/

网络接口层 使用的数据结构

1. 接口数据结构

/* netif.h – NIGET */

#define    NI_MAXHWA 14            /* 任何硬件最大尺寸 max size of any hardware*/
                    /* (物理)网络地址 */
struct    hwa    {            /* *硬件地址    */
    int        ha_len;        /* 地址长度    */
    unsigned char    ha_addr[NI_MAXHWA];     /* 地址    */
};

#define    NI_INQSZ    30        /* 接口输入队列大小    */
#define    NETNLEN        30        /* 网络名称长度    */

#define    NI_LOCAL    0        /* 本地接口序号index of local interface    */
#define    NI_PRIMARY    1        /* 主要接口序号index of primary interface    */

#define    NI_MADD        0        /* 添加多播add multicast (ni_mcast)    */
#define    NI_MDEL        1        /* 删除多播 multicast (ni_mcast)    */

/* 接口状态 */

#define    NIS_UP        0x1
#define    NIS_DOWN    0x2
#define    NIS_TESTING    0x3

/* 网络接口结构 (one per interface)    */

struct    netif {                /* 网络接口信息    */
    char        ni_name[NETNLEN]; /* 接口的域名    */
    char        ni_state;    /* 接口的状态 取上边的三个值    */
    IPaddr        ni_ip;        /* 接口的IP地址*/
    IPaddr        ni_net;        /* 接口的网络IP地址        */
    IPaddr        ni_subnet;    /* 子网的IP地址    */
    IPaddr        ni_mask;    /* 接口的子网掩码    */
    IPaddr        ni_brc;        /* IP广播地址        */
    IPaddr        ni_nbrc;    /* IP的网络广播地址    */
    unsigned int    ni_mtu;        /* 最大传输单元(字节)    */
    unsigned int    ni_hwtype;    /* 硬件类型 (for ARP)    */
    struct    hwa    ni_hwa;        /* 接口的硬件地址*/
    struct    hwa    ni_hwb;        /* 硬件广播地址    */
                    /* 多播添加/删除函数    */
    int        (*ni_mcast)(int op,int dev,Eaddr hwa,IPaddr ipa);
    Bool        ni_ivalid;    /* 接口的IP地址是否有效        */
    Bool        ni_nvalid;    /* 接口的域名是否有效        */
    Bool        ni_svalid;    /* 子网的IP地址是否有效        */
    int        ni_dev;        /* 设备描述符    */
    int        ni_ipinq;    /*  IP输入队列        */
    int        ni_outq;    /* (设备)的输出队列    */
    /* 接口信息管理库- MIB */
    char        *ni_descr;    /* 硬件的文字描述 */
    int        ni_mtype;    /* MIB类型        */
    long        ni_speed;    /* 速率 bit/s        */
    char        ni_admstate;    /* 管理状态 (NIS_*)*/
    long        ni_lastchange;    /* 最后一个状态变化 (1/100 sec)*/
    long        ni_ioctets;    /* # 收到的字节        */
    long        ni_iucast;    /* # 收到的单播字节    */
    long        ni_inucast;    /* # 收到的非单播字节    */
    long        ni_idiscard;    /* # 丢弃 – 输出队列满*/
    long        ni_ierrors;    /* # 输入包错误    */
    long        ni_iunkproto;    /* # in packets for unk. protos */
    long        ni_ooctets;    /* # 发送的字节        */
    long        ni_oucast;    /* # 发送的单播字节        */
    long        ni_onucast;    /* # 发送的非单播字节    */
    long        ni_odiscard;    /* # 输出的数据包被丢弃    */
    long        ni_oerrors;    /* # 发送的包错误    */
    long        ni_oqlen;    /* 输出队列长度        */
    long        ni_maxreasm;    /* 可以重新组装的最大数据包的长度    */
};

#define    NIGET(ifn)    ((struct ep *)deq(nif[ifn].ni_ipinq))

#define    NIF    Neth+Noth+1        /* # of interfaces, +1 for local*/

extern struct netif    nif[];

int netwrite(struct netif *, struct ep *, unsigned);
 


2. 以太网的基本定义

/* ether.h */

/* 以太网的定义和常量 */

#define    EP_MAXMULTI      10    /* 多播地址表的大小        */

#define    EP_MINLEN      60    /* 最小数据包长度        */
#define    EP_DLEN        1500    /* 数据字段长度        */
#define    EP_HLEN          24    /* 头长度    */
#define    EP_CRC           4    /* 效检位长度 ether CRC (trailer)    */
#define    EP_MAXLEN    EP_HLEN+EP_DLEN
#define    EP_ALEN    6        /* 物理地址的字节数    */
typedef    unsigned char    Eaddr[EP_ALEN]; /* 物理地址    */
#define    EP_RETRY    3    /* number of times to retry xmit errors    */
#define    EP_BRC    "377377377377377377"/* 以太网广播地址    */
#define EP_RTO 300        /* time out in seconds for reads    */

#define EP_NUMRCV 16        /* number LANCE recv buffers (power 2)    */
#define EP_NUMRCVL2 4        /* log2 of the number of buffers    */

#define    EPT_LOOP    0x0060        /* type: Loopback 回环        */
#define    EPT_ECHO    0x0200        /* type: Echo    应答        */
#define    EPT_PUP        0x0400        /* type: Xerox PUP        */
#define    EPT_IP        0x0800        /* type: Internet Protocol    */
#define    EPT_ARP        0x0806        /* type: ARP            */
#define    EPT_RARP    0x8035        /* type: Reverse ARP        */

struct    eh {            /* 以太网报头            */
    Eaddr    eh_dst;        /* 目标主机地址        */
    Eaddr    eh_src;        /* 源主机地址            */
    unsigned short    eh_type;/* 以太网数据包类型(见下文)    */
};

struct    ep    {        /* 结构完整的以太网数据包*/
    u_long    ep_nexthop;    /* niput() uses this            */
    short    ep_ifn;        /* 原接口序号originating interface number*/
    short    ep_len;        /* 数据包的长度            */
    short    ep_order;    /* 字节顺序 (for debugging)    */
    struct    eh ep_eh;    /* 以太网报头            */
    char    ep_data[EP_DLEN];    /* 数据        */
};

/* 数据包字节顺序的常量(设置方法主机顺序) */

#define    EPO_NET        1    /* 网络层 network layer        */
#define    EPO_IP        2    /* level 1 protocols…        */
#define    EPO_ARP        2
#define    EPO_RARP    2
#define    EPO_ICMP    4    /* level 2 protocols        */
#define    EPO_IGMP    4
#define    EPO_TCP        4
#define    EPO_UDP        4
#define    EPO_OSPF    4
#define    EPO_DNS        5    /* 应用协议 application protocols*/

/* these allow us to pretend it's all one big happy structure */

#define    ep_dst    ep_eh.eh_dst
#define    ep_src    ep_eh.eh_src
#define    ep_type    ep_eh.eh_type

#if    Noth > 0
/* this is for Othernet simulation */

struct    otblk {
    Bool    ot_valid;    /* these entries are valid?        */
    Eaddr    ot_paddr;    /* the Othernet physical address    */
    Eaddr    ot_baddr;    /* the Othernet broadcast address    */
    int    ot_rpid;    /* id of process reading from othernet    */
    int    ot_rsem;    /* mutex for reading from the othernet    */
    int    ot_pdev;    /* Physical device devtab index        */
    int    ot_intf;    /* the associate interface        */
    char    *ot_descr;    /* text description of the device    */
};
#endif    /* Noth */

#define    ETOUTQSZ    30

/* 以太网控制模块的描述 */

struct    etblk    {
    int    etxpending;    /* 号的数据包等待输出number of packets pending output*/
    int    etrpending;    /* 正在等待已接收1 => a receive is pending already*/
    int    etwtry;        /* 重试num. of times to retry xmit errors    */
    char    *etwbuf;    /* 当前发送缓冲区的指针    */
    short    etnextbuf;    /* 检查缓冲区循环for checking buffers round robin    */
#if    Noth > 0
    struct    otblk    *etoth[Noth];    /* eaddr to oaddr translations    */
#endif    /* Noth */
};

/* 以太网功能的代码 */

#define    EPC_PROMON    1        /* 打开混杂模式    */
#define    EPC_PROMOFF    2        /* 关闭混杂模式    */
#define    EPC_MADD    3        /* 添加多播地址    */
#define    EPC_MDEL    4        /* 删除多播地址    */

struct utdev {
    unsigned int     ud_iomem;
    unsigned int     ud_iosize;
    struct devsw    *ud_pdev;
    Eaddr         ud_paddr;
    Eaddr         ud_bcast;
    char        *ud_descr;
    int         ud_outq;
    int         ud_ifnum;
    int         ud_nextbuf;
    int         ud_xpending;
    int         ud_xmaddr;    /* 发送基地缓冲区transmit base buf #    */
    int         ud_wretry;
    int         ud_rmin;    /* 接收环形缓冲区基地址receive ring buffer base addr*/
    int         ud_rmax;    /* 环形缓冲区最大接收地址receive ring buffer max addr    */
};

struct etdev {
    unsigned int     ed_iomem;
    unsigned int     ed_iosize;
    struct devsw    *ed_pdev;
    Eaddr         ed_paddr;
    Eaddr         ed_bcast;
    char        *ed_descr;
    int         ed_outq;
    int         ed_ifnum;
    int         ed_wretry;
    struct scb    *ed_scb;
    struct cbl    *ed_cbl;
    struct rfd    *ed_rfd;    /* first in RFD ring        */
    struct rfd    *ed_rfdend;    /* last in RFDring        */
    struct rbd    *ed_rbd;    /* first in RBD ring        */
    struct rbd    *ed_rbdend;    /* last in RBD ring        */
    struct tbd    *ed_tbd;    /* transmit buffer descriptor    */
    unsigned char    *ed_xmbuf;    /* transmit buffer base        */
    unsigned char     ed_irq;    /* SIRQ reg value        */
    int         ed_mcc;    /* # valid mca's        */
    Eaddr         ed_mca[EP_MAXMULTI]; /* multicast addr. table    */
/* XXX */
    int        ed_xpending;
};

extern struct utdev    ue[];
extern struct etdev    ee[];
 


3. 传入分组的多路分解

/* ni_in.c – ni_in */

#include <conf.h>
#include <kernel.h>
#include <network.h>

#include <ospf.h>

int arp_in(struct netif *, struct ep *);
int rarp_in(struct netif *, struct ep *);
int ip_in(struct netif *, struct ep *);

/*————————————————————————
 *  ni_in – network interface input function 网络接口输入功能
 *————————————————————————
 */
int
ni_in(struct netif *pni, struct ep *pep, unsigned len)
{
    int    rv;

    pep->ep_ifn = pni – &nif[0];    /* 记录原接口序号 # */

    pni->ni_ioctets += len;        /*收到的字节累加*/
    if (!memcmp(pni->ni_hwa.ha_addr, pep->ep_dst, EP_ALEN))
        pni->ni_iucast++; /*比较帧的目的地址与接口的物理地址,相同则单播字节累加*/
    else
        pni->ni_inucast++;    /*非单播字节累加*/
    switch (pep->ep_type) {        /*分类*/
    case EPT_ARP:    rv = arp_in(pni, pep);    break;
    case EPT_RARP:    rv = rarp_in(pni, pep);    break;
    case EPT_IP:    rv = ip_in(pni, pep);    break;
    default:
        pni->ni_iunkproto++;
        freebuf(pep);
        rv = OK;
    }
    return rv;
}

extern    struct    etblk    eth[];

自己写的一个linux 远程控制工具

学习linux有些时日了,这个就是我的一第一真正的作品,发表一下作品,
希望其中的一些技巧对朋友们有所帮助,也希望达人指正一下其中的不足。

主要应用知识:
linux的网络通信
文件的属性及操作
多进程的应用
进程的回收
信号的应用

下面是客户端
文件的意义:
command.c 命令模块 ,common.h 公共声明, input.c 分割命令, main.c 主程序 ,
Makefile make工具用的,ssjio.c ssjio.h 是我自己的模块

main.c

#include "common.h"

int main(void)
{
char cline[COMMAND_LINE]="123";
struct command_line command;
int sock_fd;
struct sockaddr_in sin;

printf("ssj-soft$: ");
fflush(stdout);

while(fgets(cline,COMMAND_LINE,stdin)!=NULL)
{ //printf("%x,%x,%x,%xn",cline[0],cline[1],cline[2],cline[3]);fflush(stdout);
if(cline[0]==0xa){cline[1]=0xa;cline[2]='';}
if(split(&command,cline)==-1)
exit(1);

if(strcasecmp(command.name,"get")==0){
if(do_get(command.argv[1],command.argv[2],sock_fd)==-1)
exit(1);
}else if(strcasecmp(command.name,"put")==0){
if(do_put(command.argv[1],command.argv[2],sock_fd)==-1)
exit(1);
}else if(strcasecmp(command.name,"cd")==0){
if(do_cd(command.argv[1])==-1)
exit(1);
}else if(strcasecmp(command.name,"!cd")==0){
if(do_serv_cd(command.argv[1],sock_fd)==-1)
exit(1);
}else if(strcasecmp(command.name,"ls")==0){
if(do_ls(command.argv[1])==-1)
exit(1);
}else if(strcasecmp(command.name,"!ls")==0){
if(do_serv_ls(command.argv[1],sock_fd)==-1)
exit(1);
}else if(strcasecmp(command.name,"connect")==0){
if(do_connect(command.argv[1],&sin,&sock_fd)==-1)
exit(1);
}else if(strcasecmp(command.name,"RUN")==0){
if(do_serv_command(command.argv[1],sock_fd)==-1)
exit(1);
}else if(strcasecmp(command.name,"bye")==0){
if(do_bye(sock_fd)==-1)
exit(1);
break;
}else{
printf("wrong commandn");
printf("usage:connect <ip>n");
printf(" run <command>n");
printf(" cd <path>n");
printf(" !cd <path>n");
printf(" get <srcpathfile> <dstpath>n");
printf(" put <srcpathfile> <dstpath>n");
printf(" ls <path>n");
printf(" !ls <path>n");
printf(" byen");
}
printf("ssj-soft$: ");
fflush(stdout);
}
if(close(sock_fd)==-1)
{perror("Fail to close");exit(1);}

return 0;
}

command.c

#include "common.h"

//================connect==================
int do_connect(char *ip,struct sockaddr_in *sin,int *sock_fd)
{
int sfd;

bzero(sin,sizeof(struct sockaddr_in));
sin->sin_family=AF_INET;
if(inet_pton(AF_INET,ip,&sin->sin_addr)==-1)
{perror("wrong format of ip address");return -1;}

sin->sin_port=htons(PORT);
if((sfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{perror("fail to create socket");return -1;}

if(connect(sfd,(struct sockaddr *)sin,sizeof(struct sockaddr_in))==-1)
{perror("Fail to connetc");return -1;}

*sock_fd=sfd;
return 0;
}

//=====================get_file=======================
int do_get(const char *src,const char *dst,int sock_fd)
{
char *dst_file,*p,buf[MAX_LINE];
struct stat statbuf;
int n,fd,len,res=-1;

if(src==NULL || dst==NULL)
{printf("wrong commandn");return -1;}

if (src[strlen(src)-1]=='/')
{printf("source file should be a regular filen");return -1;}

if((dst_file=(char *)malloc(strlen(dst)+strlen(src)))==NULL)
{perror("Fail to malloc");return -1;}

strcpy(dst_file,dst);

if(dst_file[strlen(dst_file)-1]!='/')
strcat(dst_file,"/");
p=rindex(src,'/');
strcat(dst_file,p+1);

if((fd=open(dst_file,O_WRONLY|O_CREAT|O_TRUNC,0664))==-1)
{perror("Fail to open dst_file");goto end2;}

if(fstat(fd,&statbuf)==-1)
{perror("Fail to stat dst_file");goto end1;}

if(!S_ISREG(statbuf.st_mode))
{printf("dst_file should be a regular file");goto end1;}

sprintf(buf,"GET %s",src);

if(my_write(sock_fd,buf,strlen(buf)+1)==-1)
goto end1;

if((n=my_read(sock_fd,buf,MAX_LINE))<=0)
goto end1;

if(buf[0]=='E')
{write(STDOUT_FILENO,buf,n);res=0;goto end1;}

len=atoi(&buf[3]);
printf("len atoi:%dn",len);//no
if(my_write(sock_fd,"RDY",4)==-1)
{perror("Fail to write RDY");goto end1;}
printf("while……n");fflush(stdout);
while(1){
if(len!=0)
n=my_read(sock_fd,buf,MAX_LINE);
printf("read up len:%d,n:%d",len,n);fflush(stdout);
if(n>0)
{write(fd,buf,n);printf("len:%d,n:%d",len,n);fflush(stdout);len-=n;n=0;}
else if(len==0)
{printf("OKn");break;}
else
goto end1;
}
res=0;

end1:
close(fd);
end2:
free(dst_file);
return res;
}

//=====================!cd=======================
int do_serv_cd(char *path,int sock_fd)
{
char buf[MAX_LINE];
int n;
sprintf(buf,"CD %s",path);

if(my_write(sock_fd,buf,strlen(buf)+1)==-1)
{return -1;}

if((n=my_read(sock_fd,buf,MAX_LINE))<=0)
{return -1;}

if(buf[0]=='E')
write(STDOUT_FILENO,buf,n);

return 0;
}

//==================!ls=======================
int do_serv_ls(char *path,int sock_fd)
{
char buf[MAX_LINE];
int len,n;

sprintf(buf,"LS %s",path);

if(my_write(sock_fd,buf,strlen(buf)+1)==-1)
return -1;

if((n=my_read(sock_fd,buf,MAX_LINE))<=0)
return -1;

if(buf[0]=='E')
{write(STDOUT_FILENO,buf,n);return 0;}

len=atoi(&buf[3]);

if(my_write(sock_fd,"RDY",4)==-1)
return -1;

while(1)
{
if(len!=0)
n=my_read(sock_fd,buf,MAX_LINE);
if(n>0)
{write(STDOUT_FILENO,buf,n);len-=n;n=0;}
else if(len==0)
{printf("OKn");break;}
else
return -1;
}
return 0;
}

//================bye======================
int do_bye(int sock_fd)
{
if(my_write(sock_fd,"BYE",4)==-1)
return -1;

return 0;
}

//===============put=====================
int do_put(const char *src,const char *dst,int sock_fd)
{
char *dst_file,buf[MAX_LINE],*p;
struct stat statbuf;
int n,fd,res=-1;

if(src==NULL || dst==NULL)
{printf("wrong commandn");return -1;}

if(src[strlen(src)-1]=='/')
{printf("source file should be a regular filen");return -1;}

if((dst_file=malloc(strlen(src)+strlen(dst)+2))==NULL)
{perror("Fail to malloc");return -1;}

strcpy(dst_file,dst);
if(dst_file[strlen(dst_file)-1]!='/')
strcat(dst_file,"/");
p=rindex(src,'/');

strcat(dst_file,p+1);

if((fd=open(src,O_RDONLY))==-1)
{perror("Fail to open src_file");goto end1;}

if(fstat(fd,&statbuf)==-1)
{perror("Fail to stat src_file");goto end2;}
if(!S_ISREG(statbuf.st_mode))
{fputs("src_file should be a regular filen",stderr);goto end2;}

sprintf(buf,"PUT %d %s",statbuf.st_size,dst_file);
if(my_write(sock_fd,buf,strlen(buf)+1)==-1)
{goto end2;}
if((n=my_read(sock_fd,buf,MAX_LINE))<=0)
goto end2;
if(buf[0]=='E')
{write(STDOUT_FILENO,buf,n);goto end2;}

while(1)
{
n=read(fd,buf,MAX_LINE);

if(n>0)
{if(my_write(sock_fd,buf,n)==-1)
goto end2;}
else if(n==0)
{printf("OKn");break;}
else
{perror("Fail to readn");goto end2;}
}
res=0;
end2:
close(fd);
end1:
free(dst_file);
return res;
}

//===================cd=================
int do_cd(char *path)
{
if(chdir(path)==-1)
{perror("Fail to change directoryn");return -1;}

return 0;
}

//===================ls=================
int do_ls(char *path)
{
char cmd[128],buf[NAME_LEN];
FILE *fp;

sprintf(cmd,"ls %s > temp.txt",path);
system(cmd);

fp=fopen("temp.txt","r");
if(fp==NULL)
{perror("Fail to ls");return -1;}

while(fgets(buf,NAME_LEN,fp)!=NULL)
printf("%s",buf);

fclose(fp);
return 0;
}

//================command======================
int do_serv_command(char *path,int sock_fd)
{
char buf[MAX_LINE];
int len,n;

sprintf(buf,"RUN %s",path);

if(my_write(sock_fd,buf,strlen(buf)+1)==-1)
return -1;

if((n=my_read(sock_fd,buf,MAX_LINE))<=0)
return -1;

if(buf[0]=='E')
{write(STDOUT_FILENO,buf,n);return 0;}

len=atoi(&buf[3]);

if(my_write(sock_fd,"RDY",4)==-1)
return -1;

while(1)
{
if(len!=0)
n=my_read(sock_fd,buf,MAX_LINE);
if(n>0)
{write(STDOUT_FILENO,buf,n);len-=n;n=0;}
else if(len==0)
{printf("OKn");break;}
else
return -1;
}
return 0;
}

input.c

#include "common.h"
//去除命令之前的空格等
void del_blank(int *pos,char cline[])
{
while(*(cline+*pos)!=''&&(*(cline+*pos)==' '||*(cline+*pos)=='t'))
{(*pos)++;}
}

//得到参数部分,写入结构体
void get_arg(char arg[],int *pos,char cline[])
{
int i=0;
while(*(cline+*pos)!='' && *(cline+*pos)!=' ' && *(cline+*pos)!='t')
{*(arg+i)=*(cline+*pos);i++;(*pos)++;}
*(arg+i)='';
}

int split(struct command_line *command,char cline[])
{
int i=0,pos=0;
cline[strlen(cline)-1]=''; //fgets得到的字符串中有一个n,把它变成

del_blank(&pos,cline);
i=0;
while(cline[pos]!='')
{
if((command->argv[i]=(char *)malloc(MAX_LENGTH))==NULL)
{perror("Fail malloc");exit(-1);}
get_arg(command->argv[i],&pos,cline);
i++;
del_blank(&pos,cline);
}

if(strncasecmp(command->argv[0],"RUN",3)==0)
{strcpy(command->argv[1],cline+4);
i=2;}
command->argv[i]=NULL;
command->name=command->argv[0];
return i;
}

服务器端:

main.c

#include "common.h"
static void handler(int signo)
{
pid_t pid;
pid = waitpid(-1,NULL, WNOHANG);
printf("process id:%d closedn",pid);
}

int main(void)
{
signal(SIGCHLD,handler); //子程序退出的信号
//signal(SIGINT,handler); //CTRL-C的信号
//signal(SIGTERM,handler);

struct sockaddr_in sin;
struct sockaddr_in cin;
int lfd, cfd;
socklen_t len = sizeof(struct sockaddr_in);
char buf[MAX_LINE];
char str[ADDR_LEN];
int sock_opt = 1;
int n;
pid_t pid;

if(init(&sin, &lfd, sock_opt) == -1)
exit(1);

printf("waiting connections …n");

while(1){
if((cfd = accept(lfd, (struct sockaddr *)&cin, &len)) == -1){
perror("fail to accept");
exit(1);
}

if( (pid = fork()) < 0){
perror("fail to fork");
exit(1);
}else if(pid == 0){
close(lfd);

while(1){
if(my_read(cfd, buf, MAX_LINE) == -1)
exit(1);

if(strstr(buf, "GET") == buf)
{if(do_put(cfd, &buf[4]) == -1)
printf("error occours while puttingn");
else
printf("GET successful!n");
}
else if(strstr(buf, "PUT") == buf)
{if(do_get(cfd, &buf[4]) == -1)
printf("error occours while gettingn");}
else if(strstr(buf, "CD") == buf)
{if(do_cd(cfd, &buf[3]) == -1)
printf("error occours while changing directoryn");}
else if(strstr(buf, "LS") == buf)
{if(do_ls(cfd, &buf[3]) == -1)
printf("error occours while listingn");else printf("LS OKn");}
else if(strstr(buf, "RUN") == buf)
{if(do_command(cfd, &buf[4]) == -1)
printf("error occours while listingn");else printf("RUN OKn");}
else if(strstr(buf, "BYE") == buf)
break;
else{
printf("wrong commandn");
exit(1);
}
}

close(cfd);
exit(0);
}else
close(cfd);
}
return 0;
}

command.c

#include "common.h"

//================socket,bind,listen======================
int init(struct sockaddr_in *sin,int *lfd,int sock_opt)
{
int tfd;
bzero(sin,sizeof(struct sockaddr_in));
sin->sin_family=AF_INET;
sin->sin_addr.s_addr=INADDR_ANY;
sin->sin_port=htons(PORT);

if((tfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{perror("Fail to creat socket");return -1;}

setsockopt(tfd,SOL_SOCKET,SO_REUSEADDR,&sock_opt,sizeof(int));

if(bind(tfd,(struct sockaddr *)sin,sizeof(struct sockaddr_in))==-1)
{perror("Fail to bind");return -1;}

if(listen(tfd,20)==-1)
{perror("Fail to listen");return -1;}

*lfd=tfd;
return 0;
}

//=====================put==========================
int do_put(int cfd,char *file)
{
struct stat statbuf;
int n,fd,res=-1;
char buf[MAX_LINE];

if((fd=open(file,O_RDONLY))==-1)
{my_write(cfd,"ERR open server filen",22);return res;}

if(fstat(fd,&statbuf)==-1)
{my_write(cfd,"ERR stat server filen",22);goto end;}
if(!S_ISREG(statbuf.st_mode))
{if(my_write(cfd,"ERR server path should be a regular filen",42)==-1)
goto end;
res=0;
goto end;
}

sprintf(buf,"OK %d",statbuf.st_size);
if(my_write(cfd,buf,strlen(buf)+1)==1)
goto end;
if(my_read(cfd,buf,MAX_LINE)<=0)
goto end;

while(1)
{
n=read(fd,buf,MAX_LINE);
printf("n:%d – ",n);
if(n>0)
{if((my_write(cfd,buf,n))==-1)
goto end;}
else if(n==0){
printf("OKn");break;}
else{
perror("Fail to read");goto end;}
}
res=0;
end:
close(fd);
retur

n res;
}

//===================get==============================
int do_get(int cfd,char *file)
{
struct stat statbuf;
int n,fd,len,res=-1;
char buf[MAX_LINE],c[MAX_LINE],*save=file;
char *lenc=c;
printf("%sn",file);
while(*file!=' ')
{*lenc++ = *file++;*lenc='';}
len=atoi(c);
printf("lenc:%s,file:%s,len:%dn",c,file,len);
if((fd=open(file+1,O_WRONLY|O_CREAT|O_TRUNC,0644))==-1)
{
if(errno==EISDIR)
{
if(my_write(cfd,"ERR server has a dir with the same namen",41)==-1)
goto end;
res=0;
goto end;
}else{
my_write(cfd,"ERR open server filen",22);
goto end;
}
}

if(fstat(fd,&statbuf)==-1)
{my_write(cfd,"ERR stat server filen",22);goto end;}

if(!S_ISREG(statbuf.st_mode))
{
if(my_write(cfd,"ERR server path should be a regular filen",42)==-1)
goto end;
res=0;
goto end;
}

if(my_write(cfd,"OK",3)==-1)
goto end;

while(1)
{
if(len!=0)
n=my_read(cfd,buf,MAX_LINE);

if(n>0)
{write(fd,buf,n);len-=n;n=0;}
else if(len==0)
{printf("OKn");break;}
else
goto end;
}
res=0;
end:
close(fd);
return res;
}

//======================cd===========================
int do_cd(int cfd,char *path)
{
if(chdir(path)==-1)
{perror("Fail to change directoryn");
my_write(cfd,"ERR cat't change directoryn",28);return -1;
}
my_write(cfd,"OKn",4);
return 0;
}

//=====================ls===========================
int do_ls(int cfd,char *path)
{
char cmd[128],buf[NAME_LEN];
struct stat statbuf;
int n,fd,res=-1;

sprintf(cmd,"ls %s > temp.txt",path);
system(cmd);

if((fd=open("temp.txt",O_RDONLY))==-1)
{my_write(cfd,"ERR ls server filen",20);return res;}

if(fstat(fd,&statbuf)==-1)
{my_write(cfd,"ERR stat server filen",22);goto end;}

if(!S_ISREG(statbuf.st_mode))
{
if(my_write(cfd,"ERR server path should be a regular filen",42)==-1)
goto end;
res=0;
goto end;
}

sprintf(buf,"OK %d",statbuf.st_size);
if(my_write(cfd,buf,strlen(buf)+1)==-1)
goto end;

if(my_read(cfd,buf,MAX_LINE)<=0)
goto end;

while(1)
{
n=read(fd,buf,MAX_LINE);
printf("%d,%sn",n,buf);
if(n>0)
{ if(my_write(cfd,buf,n)==-1)
goto end;}
else if(n==0)
{printf("OKn");break;}
else
{perror("Fail to read");goto end;}

}

res=0;
end:
close(fd);
return res;
}

//==================command=========================
int do_command(int cfd,char *path)
{
char cmd[128],buf[MAX_LINE];
struct stat statbuf;
int n,fd,res=-1;

sprintf(cmd,"%s > temp.txt",path);
system(cmd);

if((fd=open("temp.txt",O_RDONLY))==-1)
{my_write(cfd,"ERR ls server filen",20);return res;}

if(fstat(fd,&statbuf)==-1)
{my_write(cfd,"ERR stat server filen",22);goto end;}

if(!S_ISREG(statbuf.st_mode))
{
if(my_write(cfd,"ERR server path should be a regular filen",42)==-1)
goto end;
res=0;
goto end;
}

sprintf(buf,"OK %d",statbuf.st_size);
if(my_write(cfd,buf,strlen(buf)+1)==-1)
goto end;

if(my_read(cfd,buf,MAX_LINE)<=0)
goto end;

while(1)
{
n=read(fd,buf,MAX_LINE);
printf("n:%d,buf:%sn",n,buf);fflush(stdout);
if(n>0)
{ if(my_write(cfd,buf,n)==-1)
goto end;n=0;}
else if(n==0)
{printf("OKn");break;}
else
{perror("Fail to read");goto end;}

}

res=0;
end:
close(fd);
return res;
}

以上是主要文件的内容,其它文件请下载压缩包。

点击下载

unix 域 套接字 示例

这个与AF_INET不同地方在于,客户端也要用bind绑定一下,不要靠connect分配,不然服务器是无法知道客户用的是哪个套接字文件

服务器端

[ssj@main test]$ cat AFunixserver.c
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>

#define PATH "/home/ssj/source/test/connect.socket"
#define STALE 30
#define MAX 1024

void my_turn(char *p)
{
if (p == NULL)return;
while( *p != '')
{
if( *p >= (char)0x61 && *p <= (char)0x7a)
*p=(char)((int)*p-(int)0x20);
p++;
}
}

int init(int *lfd,char *path)
{
int fd,len;
struct sockaddr_un un;

if((fd=socket(AF_UNIX,SOCK_STREAM,0))==-1)
{perror("Fail to socket");return -1;}

unlink(path);
memset(&un,0,sizeof(un));
un.sun_family=AF_UNIX;
strcpy(un.sun_path,path);

len=offsetof(struct sockaddr_un,sun_path)+strlen(path);
if(bind(fd,(struct sockaddr *)&un,len)==-1)
{perror("Fail to bind");goto err;}

if(listen(fd,10)==-1){perror("Fail to listen");goto err;}

*lfd=fd;
return 0;

err:
close(fd);return -1;
}

int main(void)
{
int lfd,cfd,len,n;
time_t staletime;
struct sockaddr_un un;
struct stat statbuf;
char buf[MAX];

if(init(&lfd,PATH)==-1)
exit(1);
printf("running……n");

while(1)
{
len=sizeof(struct sockaddr_un);
if((cfd=accept(lfd,(struct sockaddr *)&un,&len))==-1)
{perror("Fail to accept");exit(1);}

len-=offsetof(struct sockaddr_un,sun_path);
un.sun_path[len]='';

if(stat(un.sun_path,&statbuf)==-1){perror("Fail to get status");exit(1);}
if((statbuf.st_mode&(S_IRWXG|S_IRWXO))||(statbuf.st_mode&S_IRWXU)!=S_IRWXU)
{printf("wrong permissionsn");exit(1);}

staletime=time(NULL)-STALE;
if(statbuf.st_atime<staletime||statbuf.st_ctime<staletime||statbuf.st_mtime<staletime){printf("client is too oldn");close(cfd);break;}

if(unlink(un.sun_path)==-1){perror("Fail to unlink");exit(1);}

n=read(cfd,buf,MAX);
if (n==-1)
{
perror("Fail to read");
exit(1);
}
else if(n==0)
{
printf("the connect closedn");
close(cfd);
continue;
}

if(strcmp(buf,"Nothing")==0)
goto close;

my_turn(buf);
if(write(cfd,buf,n)==-1){perror("Fail to write");exit(1);}
close(cfd);

}

if(unlink(PATH)==-1){perror("Fail to unlink");exit(1);}

close(lfd);
return 0;

close:
write(cfd,"Server Close",13);
close(cfd);
close(lfd);
if(unlink(PATH)==-1){perror("Fail to unlink");exit(1);}
return -1;
}

客户端

[ssj@main test]$ cat AFunixclient.c
#include <stdio.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>

#define PATH "/home/ssj/source/test/connect.socket"
#define C_PATH "/home/ssj/source/test/"
#define MAX 1024

int main(int argc,char *argv[])
{
int cfd,len;
struct sockaddr_un un;
char buf[MAX],*str="Nothing";
if(argc>1)
str=argv[1];

if((cfd=socket(AF_UNIX,SOCK_STREAM,0))==-1)
{perror("Fail to socket");exit(1);}

memset(&un,0,sizeof(struct sockaddr_un));
un.sun_family=AF_UNIX;
sprintf(un.sun_path,"%s%d",C_PATH,getpid());
len=offsetof(struct sockaddr_un,sun_path)+strlen(un.sun_path);

unlink(un.sun_path);

if(bind(cfd,(struct sockaddr *)&un,len)==-1)
{perror("Fail to bind");exit(1);}

if(chmod(un.sun_path,S_IRWXU)<0){perror("Fail to chmod");exit(1);}

memset(&un,0,sizeof(struct sockaddr_un));
un.sun_family=AF_UNIX;
strcpy(un.sun_path,PATH);

len=offsetof(struct sockaddr_un,sun_path)+strlen(un.sun_path);

if(connect(cfd,(struct sockaddr *)&un,len)<0)
{perror("Fail to connect");exit(1);}

if(write(cfd,str,len)==-1){perror("Fail to write");exit(1);}

if(read(cfd,buf,MAX)==-1){perror("Fail to read");exit(1);}

printf("recive from server:%sn",buf);
close(cfd);
return 0;
}

非命名unix 域 套接字 示例

这个东西和匿名管道就没有什么区别呀

[ssj@main test]$ cat socketpair.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>

#define MAX 100

int main(void)
{
int sockfd[2],n;
pid_t pid;
char buf[MAX];
bzero(buf,sizeof(buf));

if(socketpair(AF_UNIX,SOCK_STREAM,0,sockfd)==-1)
{perror("Fail to socketpair");exit(1);}

pid=fork();
if(pid<0){perror("Fail to fork");exit(1);}
else if(pid==0)
{
close(sockfd[0]);
strcpy(buf,"hello parent");
n=strlen(buf);

if(write(sockfd[1],buf,n)==-1)
{
perror("Fail to write");
exit(1);
}
printf("child closen");

}
else
{
close(sockfd[1]);

if((n=read(sockfd[0],buf,MAX))==-1)
{
perror("Fail to read");exit(1);
}
//printf("%dn",n);
//n=strlen(buf);
//printf("%dn",n);
buf[n]='';
printf("from child:%sn",buf);

if(wait(NULL)==-1){perror("Fail to wait");exit(1);}

printf("the parent donen");
}
return 0;

}

在打印字符串之前的buf[n]='';只是为了测试,如果不给buf赋初值,打印结果会有所不同,注意字符串的‘’

I/O 多路选择示例 (select)

书中自有言如玉呀,select函数的使用方法,我在中间加入了一些printf语言,更方便理解程序的流程
客户端用之前的TCP例子就行了,见http://www.kumouse.com/article.asp?id=141

[ssj@main test]$ cat selectserver.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>

#define MAX 80

int port=65533;

void my_turn(char *p)
{sleep(5);
if (p == NULL)return;
while( *p != '')
{
if( *p >= (char)0x61 && *p <= (char)0x7a)
*p=(char)((int)*p-(int)0x20);
p++;
}
}

int main(void)
{
struct sockaddr_in sin,cin;
int lfd,cfd,sfd,rdy,client[FD_SETSIZE],maxi,maxfd;
fd_set rset,allset;
socklen_t addr_len;
char buf[MAX],str[INET_ADDRSTRLEN];
int i,n,len,opt=1;

bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=INADDR_ANY;
sin.sin_port=htons(port);

lfd=socket(AF_INET,SOCK_STREAM,0);
if(lfd==-1)
{perror("Fail to socket");exit(1); }

setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

if((n=bind(lfd,(struct sockaddr *)&sin,sizeof(sin)))==-1)
{perror("Fail to bind");exit(1);}

if((n=listen(lfd,20))==-1)
{perror("Fail to listen");exit(1);}

printf("running….n");

maxfd=lfd;
maxi=-1;

for(i=0;i<FD_SETSIZE;i++)
{client[i]=-1;}

FD_ZERO(&allset);
FD_SET(lfd,&allset);

while(1){
rset=allset;
rdy=select(maxfd+1,&rset,NULL,NULL,NULL);
printf("%dn",rdy);
if(FD_ISSET(lfd,&rset)){
printf("p1n");
addr_len=sizeof(sin);
if((cfd=accept(lfd,(struct sockaddr *)&cin,&addr_len))==-1)
{perror("Fail to accept");exit(1);}

for(i=0;i<FD_SETSIZE;i++)
if(client[i]<0){client[i]=cfd;break;}

if(i==FD_SETSIZE){printf("too many clientsn");exit(1);}

FD_SET(cfd,&allset);

if(cfd>maxfd)
maxfd=cfd;
if(i>maxi)
maxi=i;
if(–rdy<=0)
continue;
}

for(i=0;i<=maxi;i++){
if((sfd=client[i])<0)continue;

if(FD_ISSET(sfd,&rset)){
if((n=read(sfd,buf,MAX))==0){
printf("the other side has been close.n");
fflush(stdout);
close(sfd);
FD_CLR(sfd,&allset);
client[i]=-1;
}
else{
inet_ntop(AF_INET,&cin.sin_addr,str,sizeof(str));
printf("client IP:%s:%dn",str,ntohs(cin.sin_port));
my_turn(buf);
if((n=write(sfd,buf,n))==-1)exit(1);
}
if(–rdy<=0)break;
}
}
}
close(lfd);
return 0;
}

一个非阻塞方式的网络程序模型(UDP)

服务器端:

[ssj@main test]$ cat noneasyserver.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

#define MAX 80

void my_turn(char *p)
{
if (p == NULL)return;
while( *p != '')
{
if( *p >= (char)0x61 && *p <= (char)0x7a)
*p=(char)((int)*p-(int)0x20);
p++;
}
}

int main(void)
{
struct sockaddr_in sin,cin;
int sfd;
int port=65533;
socklen_t addr_len;
char buf[MAX];
char addr_p[INET_ADDRSTRLEN];
int n,n1,flags;

bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=INADDR_ANY;
sin.sin_port=htons(port);

sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
perror("Fail to socket");
exit(1);
}

if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))==-1)
{
perror("Fail to bind");
exit(1);
}

flags=fcntl(sfd,F_GETFL);
flags |= O_NONBLOCK;

if(fcntl(sfd,F_SETFL,flags)==-1)
{
perror("Fail to fcntl");
exit(1);
}

while(1)
{
sleep(5);
bzero(buf,sizeof(buf));
addr_len=sizeof(sin);

n=recvfrom(sfd,buf,MAX,0,(struct sockaddr *)&cin,&addr_len);
if(n==-1&&errno!=EAGAIN)
{
perror("Fail to receiven");
exit(1);
}else if(errno==EAGAIN)
printf("socket are not ready nown");

if(n>=0)
{
inet_ntop(AF_INET,&cin.sin_addr,addr_p,sizeof(addr_p));
printf("Client IP is %s:%dn",addr_p,ntohs(cin.sin_port));
printf("type:%sn",buf);
my_turn(buf);

n1=sendto(sfd,buf,n,0,(struct sockaddr *)&cin,addr_len);
if(n1==-1&&errno!=EAGAIN)
{
perror("Fail to sendto");exit(1);
}else if(errno==EAGAIN)
{
printf("socket ate not ready nown");
}
}

}
if(close(sfd)==-1)
{
perror("Fail to close");exit(1);
}
return 0;
}

客户端:

[ssj@main test]$ cat noneasyclient.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

#define MAX 80

int main(void)
{
struct sockaddr_in sin,cin;
int port=65533;
socklen_t addr_len;
int sfd;
char buf[MAX];
char add_p[INET_ADDRSTRLEN];
int n;

bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
inet_pton(AF_INET,"127.0.0.1",&sin.sin_addr);
sin.sin_port=htons(port);

sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1){perror("Fail to socket");exit(1);}

if(fgets(buf,MAX,stdin)==NULL){perror("Fail to fget");exit(1);}

n=sendto(sfd,buf,strlen(buf)+1,0,(struct sockaddr *)&sin,sizeof(sin));
if(n==-1){perror("Fail to send");exit(1);}

addr_len=sizeof(sin);

n=recvfrom(sfd,buf,MAX,0,(struct sockaddr *)&cin,&addr_len);
if(n==-1)
{
perror("Fail to receiven");
exit(1);
}else
printf("from server:%sn",buf);

if(close(sfd)==-1)
{
perror("Fail to close");exit(1);
}
return 0;

}

一个不完善的多线程服务器模型

根据:http://www.kumouse.com/article.asp?id=141
中的服务端改写而成,刚学不久,只是练练手的。有高人路过,还请指点
字符串长度n没有当参数传递,所以会是0,退出要第二次发数据才行,其实用消息处理函数,CTRL+C就可能搞定了
[ssj@main test]$ cat rs_easyserver.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#define MAX 200
#define BB 16
int k=0;
void my_turn(char *p)
{sleep(10);
if (p == NULL)return;
while( *p != '')
{
if( *p >= (char)0x61 && *p <= (char)0x7a)
*p=(char)((int)*p-(int)0x20);
p++;
}
}

typedef struct arg_struct ARG;
struct arg_struct{
int cfd;
};
void *f1(void *arg)
{
char buf[MAX];
char addr_p[BB];
int n,cfd;
ARG *p=(ARG *)arg;
cfd=p->cfd;
n=recv(cfd,buf,MAX,0);
if (n==-1)
{
perror("Fail to recv");
exit(1);
}
else if(n==0)
{
printf("the connect closedn");
close(cfd);
exit(1);
}
if (strcmp(buf,"close")==0){k=1;}
printf("type:%sn",buf);
my_turn(buf);
printf("turn type:%sn=================================n",buf);
n=send(cfd,buf,n,0);
if(n==-1){
perror("Fail to send");
exit(1);
}
if(close(cfd)==-1)
{
perror("Fail to close");
exit(1);
}

}

int main(void)
{
struct sockaddr_in sin,cin;
struct sockaddr *tmp;
int lfd,cfd;
socklen_t len;
char buf[MAX];
char addr_p[BB];
int n,err,port = 65533;
pthread_t tid;
ARG arg;
bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=INADDR_ANY;
sin.sin_port=htons(port);

if((lfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("Fail to create socket");
exit(1);
}

if(bind(lfd,(struct sockaddr *)&sin,sizeof(sin))==-1)
{
perror("Fail to bind");
exit(1);
}
if(listen(lfd,10)==-1)
{
perror("Fail to listen");
exit(1);
}
printf("running……….n");

while(1)
{
if((cfd=accept(lfd,(struct sockaddr *)&cin,&len))==-1)
{
perror("Fail to accept");
exit(1);
}else{
inet_ntop(AF_INET,&cin.sin_addr,addr_p,sizeof(addr_p));
// printf("client IP is:%s:%d size:%dn",addr_p,ntohs(cin.sin_port),n);
arg.cfd=cfd;
err=pthread_create(&tid,NULL,f1,(void *)&arg);
if (err!=0){printf("%sn",strerror(err));exit(1);}
}
printf("client IP is:%s:%d Pthread:%d size:%dn",addr_p,ntohs(cin.sin_port),tid,n);
if (k==1)
{close(cfd);close(lfd);exit(0);}
}

if(close(cfd)==-1)
{
perror("Fail to close");
exit(1);
}

if(close(lfd)==-1)
{
perror("fail close");
exit(1);
}
return 0;
}

注:编译时加-lpthread参数,pthread_create不在默认库里

一个服务器模型的测试(例子)

服务器端:
[ssj@main test]$ cat easyserver.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#define MAX 200 /*缓冲区大小*/
#define BB 16 /*IP地址长度 IPV4是16,IPV6是28*/
void my_turn(char *p) /*字符处理函数,小写变大写,linux下没有strupr了???*/
{
if (p == NULL)return;
while( *p != '')
{
if( *p >= (char)0x61 && *p <= (char)0x7a)
*p=(char)((int)*p-(int)0x20);
p++;
}
}

int main(void)
{
struct sockaddr_in sin,cin; /*sin本地IP地址结构,cin客户端IP地址结构*/
int lfd,cfd; /*lfd本地socket套接字描述符,cfd,由accept建立的*/
socklen_t len; /*客户端IP长度*/
char buf[MAX]; /*缓冲区*/
char addr_p[BB]; /*IP地址十进制字符串*/
int port = 65533; /*绑定的端口*/
int n; /*收到的数据长度*/
bzero(&sin,sizeof(sin)); /*初始化sin*/
sin.sin_family=AF_INET; /*定义为IPV4*/
sin.sin_addr.s_addr=INADDR_ANY; /*服务器可以接受任意地址*/
sin.sin_port=htons(port); /*把IP地址转有16位字节序,并赋值*/

lfd=socket(AF_INET,SOCK_STREAM,0); /*建立套接字*/
bind(lfd,(struct sockaddr *)&sin,sizeof(sin)); /*绑定端口与套接字*/
listen(lfd,10); /*监听,并设置最大队列为10*/
printf("running……….n");

while(1) /*循环处理客户的连接请求*/
{
cfd=accept(lfd,(struct sockaddr *)&cin,&len);
n=read(cfd,buf,MAX);
inet_ntop(AF_INET,&cin.sin_addr,addr_p,sizeof(addr_p));
printf("client IP is:%s:%d size:%dn",addr_p,ntohs(cin.sin_port),n);
if (strcmp(buf,"close")==0) /*是否退出*/
{
write(cfd,"server done",12);
close(cfd);
close(lfd);
printf("server done.n");
exit(0);
}
printf("type:%sn",buf);
my_turn(buf);
printf("turn type:%sn=================================n",buf);
write(cfd,buf,n);
close(cfd);
}
if(close(lfd)==-1) /*收尾,实际上,上边的while是个死循环,用ctrl+c退出,或用close退出,都执行不到这里*/
{
perror("fail close");
exit(1);
}
return 0;
}

客户端:
[ssj@main test]$ cat easyclient.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#define MAX 200

int main(int argc,char * argv[])
{
struct sockaddr_in pin;
struct sockaddr_in *apin=&pin;
char buf[MAX];
int sfd,port=65533;
char *str="ssj test";

if (argc>1)
{
str=argv[1];
}
bzero(&pin,sizeof(pin));
pin.sin_family=AF_INET;
inet_pton(AF_INET,"127.0.0.1",&pin.sin_addr);
pin.sin_port=htons(port);

sfd=socket(AF_INET,SOCK_STREAM,0);
connect(sfd,(struct sockaddr *)&pin,sizeof(pin));
write(sfd,str,strlen(str)+1);
read(sfd,buf,MAX);
printf("recive:%sn",buf);
close(sfd);
return 0;
}

======================================
更完善的一个版本(服务器)
[ssj@main test]$ cat easyserver.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include "ssjio.c"
#define MAX 200
#define BB 16
void my_turn(char *p)
{
if (p == NULL)return;
while( *p != '')
{
if( *p >= (char)0x61 && *p <= (char)0x7a)
*p=(char)((int)*p-(int)0x20);
p++;
}
}

int main(void)
{
struct sockaddr_in sin,cin;
int lfd,cfd;
socklen_t len;
char buf[MAX];
char addr_p[BB];
int port = 65533;
int n;
bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=INADDR_ANY;
sin.sin_port=htons(port);

if((lfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("Fail to create socket");
exit(1);
}

if(bind(lfd,(struct sockaddr *)&sin,sizeof(sin))==-1)
{
perror("Fail to bind");
exit(1);
}
if(listen(lfd,10)==-1)
{
perror("Fail to listen");
exit(1);
}
printf("running……….n");

while(1)
{
if((cfd=accept(lfd,(struct sockaddr *)&cin,&len))==-1)
{
perror("Fail to accept");
exit(1);
}

n=ssj_read(cfd,buf,MAX);
if (n==-1)
exit(1);
else if(n==0)
{
printf("the connect closedn");
close(cfd);
continue;
}

inet_ntop(AF_INET,&cin.sin_addr,addr_p,sizeof(addr_p));
printf("client IP is:%s:%d size:%dn",addr_p,ntohs(cin.sin_port),n);
if (strcmp(buf,"close")==0)
{
n=ssj_write(cfd,"server done",12);
if(n==-1)
exit(1);
close(cfd);
close(lfd);
printf("server done.n");
exit(0);
}
printf("type:%sn",buf);
my_turn(buf);
printf("turn type:%sn=================================n",buf);
n=ssj_write(cfd,buf,n);
if(n==-1)
exit(1);

if(close(cfd)==-1)
{
perror("Fail to close");
exit(1);
}
}
if(close(lfd)==-1)
{
perror("fail close");
exit(1);
}
return 0;
}

=========================
[ssj@main test]$ cat ssjio.c
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

ssize_t ssj_read(int fd,void *buf,size_t length)
{
ssize_t done=length;
done=read(fd,buf,length);
return done;
}

ssize_t ssj_write(int fd,void *buf,size_t length)
{
ssize_t done=length;
while(done>0)
{
done=write(fd,buf,length);
if(done!=length)
if (errno==EINTR)
done=length;
else{
perror("Fail to write");
return -1;
}
else
break;
}
return done;
}

客户端同理