日志标签 ‘TCP/IP’

网络协议栈入门知识

2012年8月9日

Linux内核将一切都看作为文件,因此网络协议栈以文件系统sockfs的形式存在于内核中,该文件系统中的文件则是我们熟知的套接字。稍微熟知Linux文件系统的人都会知道内核中将所有文件系统进行抽象,通过虚拟文件系统(VFS)来统一管理。网络文件系统也是如此,也就是说超级块,索引节点,目录项等概念在网路文件系统中也存在。

在proc文件系统下,我们可以通过下面的方式得到内核当前所有文件系统的名称:

[root@TENCENT64 ~]# cat /proc/filesystems
nodev   sysfs
nodev   rootfs
nodev   bdev
nodev   proc
nodev   tmpfs
nodev   debugfs
nodev   sockfs
nodev   pipefs
nodev   anon_inodefs
nodev   rpc_pipefs
nodev   inotifyfs
…………
        ext3
        ext2
        ext4

网络文件系统和ext3这样的文件系统有什么区别?前者只存在于内存中,而后者则存在于磁盘中。比如上述信息中的sockfs和tmpfs,甚至是proc,在第一列均显示nodev,也就是说这些文件系统并没有存储在磁盘这样的块设备中。相反,它们存储在内存中,即采用的是一种非持久性的存储方式。

这里应该注意的是sockfs的“虚拟”和VFS的“虚拟”是两个不同的概念。前者表示sockfs这种虚拟文件系统不同于ext2这样的磁盘文件系统,它们存储在内存中,是一种非持久的存储,只要系统重启信息全部丢失。而虚拟文件系统中的“虚拟”表示的是对所有文件系统的一种抽象,它将所有不同种类文件系统的共同信息放入同一组数据结构当中,为上层用户提供了统一的接口。

VFS中用于描述文件系统类型的数据结构为file_system_type,所有文件系统都对应该类型的变量:

static struct file_system_type sock_fs_type = {
        .name =         "sockfs",
        .mount =        sockfs_mount,
        .kill_sb =      kill_anon_super,
};

static struct file_system_type proc_fs_type = {
        .name           = "proc",
        .mount          = proc_mount,
        .kill_sb        = proc_kill_sb,
};

可以看到,/proc/filesystem中的文件系统名称即从file_system_type结构的实例中获得。

socket结构

内核中通过socket结构体来表示套接字。该结构中包含的字段比较少,比如描述套接字状态的state,套接字标志位flags,等待队列wp以及与该套接字关联的文件对象。

 struct socket {
         socket_state            state;
         kmemcheck_bitfield_begin(type);
         short                   type;
         kmemcheck_bitfield_end(type);
         unsigned long           flags;
         struct socket_wq __rcu  *wq;
         struct file             *file;
         struct sock             *sk;
         const struct proto_ops  *ops;
 };

在socket结构中包含了一个与它比较相似的结构sock,该结构也是对套接字进行描述的。

sock结构体

sock结构中包含大量的字段,这些字段更多的是用来描述协议的通用属性,而socket结构则是更多的描述与应用程序相关的部分。

sk_buff结构体

sk_buff用于对网络协议中的数据包进行描述,不管该数据包位于那一层协议,都会有一个sk_buff结构与之对应。而sk_buff结构通过其内部的几个指针字段就可以轻松完成加包头去包头的操作。

三者的关系

每个socket结构都有一个sk字段指向sock结构,而sock结构中通过sk_socket字段反指向socket结构。sock结构通过sk_receive_queue和sk_write_queue等字段指向数据包头结构(struct sk_buff_head),而这个结构内部又通过prev和next两个字段指向真正的数据包结构sk_buff。

基于Socket API的C/S通信:以树形结构显示服务器端目录(2)

2011年8月11日

3.客户端的实现

由于客户端采用面向对象的Qt来编码,因此客户端比较容易实现。客户端的主要功能是:以列表形式显示指定目录下的文件以及相关属性;当用户点击文件列表中的某个目录时进入该子目录,并显示子目录下的文件列表;可以返回到当前目录的父目录。

根据上述需求,客户端的界面设计可以参考下图,即通过一个QTableWidget来显示某个目录下的所有文件,该部件的使用方法可以参考Qt Assitant。

接下来重点说明通信部分的功能实现。在服务器程序中,服务器向客户端发送的数据有两种,一种即为文件数,另一种则为所有文件信息形成的字节流。那么在客户端中则相应的有接受函数recvDirInfo()。

void configShowUI::recvDirInfo()
{
    //1.先接受指定目录下文件的数目
    int fileNum = 0;
    if (cli.recvData(&fileNum, sizeof(int)) == 0) {
        ui->statusLabel->setText("ERROR:receive fileNum error!");
    }

    //2.接收指定目录下的文件名序列
    char *dirbuf = NULL;
    if ((dirbuf = (char *)malloc(DATA_MAX * 10)) == NULL) {
        ui->statusLabel->setText("ERROR:malloc error!");
    }
    memset(dirbuf, 0, DATA_MAX * 10);
    if (cli.recvData(dirbuf, DATA_MAX * 10) == 0) {
        ui->statusLabel->setText("ERROR:receive dir info error!");
    } else {
        ui->statusLabel->setText("receive dir info success!");
        this->printCurDir(QString(dirbuf), fileNum);
        free(dirbuf);
    }
}

该函数有两种使用场景,其一为客户端与服务器连接成功时,此时服务器要向客户端发送指定目录下的文件列表(服务器主动发送),因此应该调用该函数。其二为当用户在客户端的文件列表中点击某个子目录时,客户端此时主动要求服务器发送指定子目录下的文件列表(服务器被动发送),此时应该调用该函数。

第二种情景可以采用Qt中的信号–槽机制(可以参考Qt Assitant了解相关概念),当用户点击了文件列表中的某个子目录时,该列表部件(fileWidget)就发送cdNextDir(QString)信号(该信号在),包含列表部件的当前部件(this)接收到这个信号后就做出相应操作,即执行槽函数cdNextDirOperation(QString)。这对信号和槽之间还传递了pathname这个变量,该变量存储了子目录的完整路径。下面的连接函数应该在当前部件的构造函数中完成。

    connect(this->fileWidget, SIGNAL(cdNextDir(QString)), this, SLOT(cdNextDirOperation(QString)));

槽函数cdNextDirOperation(QString)指定的动作很简单,即先向服务器发送“读取下级目录”的指令,再将该子目录的完整路径发送到服务器端,接着再调用recvDirInfo()。可以看到客户端和服务器之间的数据交互是同步发生的。

void configShowUI::cdNextDirOperation(QString pathname)
{
    char cd[] = "cd";
    if (cli.sendData(cd, strlen(cd)) == 0) {
        ui->statusLabel->setText("ERROR: send cd command error!");
        exit(1);
    }

    if (cli.sendData(pathname.toUtf8().data(), pathname.size()) == 0) {
        ui->statusLabel->setText("ERROR: send pathname error!");
        exit(1);
    }

    this->recvDirInfo();
}

在上述的recvDirInfo()中,如果客户端成功接收了服务器发送的字节流数据,那么就调用printCurDir()对字节流进行解析,然后以列表形式显示在客户端。这里的解析过程由具体的需求而定,再次不再赘述。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key