`
mylxiaoyi
  • 浏览: 328371 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Linux Socket学习(三)

阅读更多
 无名套接口
套接口并不总是需要有一个地址。例如, socketpair函数创建了两个彼此相连的两个套接口,但是却没有地址。实际上,他们是无名套接口。想像一下冷战期间美国总统与苏联之间的红色电话。 他们任何一端并不需要电话号码,因为他们是直接相连的。同样,socketpair函数也是直接相连的,也并不需要地址。
匿名调用
有时在实际上,连接中的两个套接口中的一个也没有地址。对于要连接的远程套接口,他必须要有一个地址来标识。然而,本地套接口是匿名的。建立起来的连接具有一个有地址的远程套接口和另一个无地址的套接口。

生成地址
有 时我们并不会介意我们的本地址是什么,但是我们需要一个来进行通信。这对于需要连接到一个服务器(例如一个RDBMS数据服务)的程序来说通常是正确的。 他们的本地地址仅为持续的连接所需要。分配确定的地址也可以完成,但是这增加了网络管理的工作。相应的,当地址可用时才会生成地址。
理解域
当Berkeley开发组正在构思BSD套接口接口时,TCP/IP仍在开发之中。与此同时,有一些其他的即将完成的协议正在为不同的组织所使用,例如X.25协议。其他的协议也正在研究之中。
我 们在上一章所见的socketpair函数,以及我们将会看到的socket函数,很明智的允许了其他协议需不是TCP/IP也许会用到的可能性。 socketpair函数的domain参数允许这种约束。为了讨论的方便,让我们先来回顾一下socketpair函数的概要:
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);
通常,protocol参数指定为0。0允许操作系统选择我们所选择的domain的所用的默认协议。对于这些规则有一些例外,但是这超出了我们讨论的范围。
现在我们要来解释一下domain参数。对于socketpair函数,这个值必须为AF_LOCAL或者AF_UNIX。在上一章,我们已经指出AF_UNIX宏与旧版的AF_LOCAL等同。然而AF_LOCAL意味着什么?他选择了什么呢?
常量的AF_前缘指明了地址族。domain参数选择要使用的地址族。

格式化套接口地址
每 一个通信协议指明了他自己的网络地址的格式。相应的,地址族用来指明要使用哪种类型的地址。常量AF_LOCAL(AF_UNIX)指明了地址将会按照本 地(UNIX)地址规则来格式化。常量AF_INET指明了地址将会符合IP地址规则。在一个地址族中,可以有多种类型。
在下面的部分中,我们将会检测各种地址族的格式以及物理布局。这是需要掌握的重要的一部分。人们使用BSD套接口接口时所遇到的困难,很多与地址初始化相关。

检测通常的套接口地址
因为BSD套接口地址的开发早于ANSI C标准,所以没有(void *)数据指针来接受任何结构地址。相应的BSD的解决选择是定义一个通用的地址结构。通用的地址结构是用下面的C语言语句来定义的:
#include <sys/socket.h>
struct sockaddr {
    sa_family_t sa_family; /* Address Family */
    char        sa_data[14]; /* Address data. */
};
这里的sa_family_t数据类型是一个无符号短整数,在Linux下为两个字节。整个结构为16个字节。结构元素的sa_data[14]代表了地址信息的其余14个字节。
下图显示了通用地址结构的物理布局:

通用套接口地址结构对于程序而言并不是那样有用。然而,他确实提供了其他地址结构必须适合的引用模型。例如,我们将会了解到所有地址必须在结构中的同样的位置定义一个sa_family成员,因为这个元素决定了地址结构的剩余字节数。

格式化本地地址
这个地址结构用在我们的本地套接口中(我们的运行Linux的PC)。例如,当我们使用lpr命令排除要打印的文件时,他使用一个本地套接口与我们的PC上假脱机服务器进行通信。虽然也可以用TCP/IP协议来进行本地通信,但是事实证明这是低效的。
传统上,本地地址族已经被称这为AF_UNIX域。这是因为这些地址使用本地UNIX文件来作为套接口名字。
AF_LOCAL或者AF_UNIX的地址结构名为sockaddr_un。这个结构是通过在我们的C程序中包含下面的语句来定义的:
#include <sys/un.h>
sockaddr_un的地址结构:
struct sockaddr_un {
    sa_family_t sun_family;/* Address Family */
    char         sun_path[108]; /* Pathname */
};
结构成员sun_family的值必须为AF_LOCAL或者AF_UNIX。这个值表明这个结构是通过sockaddr_un结构规则来进行格式化的。
结构成员sun_path[108]包含一个可用的UNIX路径名。这个字符数组并不需要结尾的null字节。
在下面的部分中,我们将会了解到如何来初始化一个AF_LOCAL地址与定义他的长度。

格式化传统本地地址
传统本地地址的地址名空间为文件系统路径名。一个进程也许会用任何可用的路径名来命名他的本地套接口。然则为了可用,命名套接口的进程必须可以访问路径名的所有目录组件,并且有权限来在指定的目录中创建最终的套接口对象。
一些程序员喜欢在填充地址结构之前将其全部初始化为0。这通常是通过memset函数来做到的,并且这是一个不错的主意。
struct sockaddr_un uaddr;
memset(&uaddr,0,sizeof uaddr);
这个函数会为我们将这个地址结构的所有字节设置为0。
下面的例子演示了一个简单的初始化sockaddr_un结构的C程序,然后调用netstat命令来证明他起到了作用。在这里我们要先记住在socket与bind上的程序调用,这是两个我们还没有涉及到的函数。
/*****************************************
 *
 * af_unix.c
 *
 * AF_UNIX Socket Example:
 *
 * ******************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>

/*
 * This function reports the error and
 * exits back to the shell:
 */
static void bail(const char *on_what)
{
    perror(on_what);
    exit(1);
}

int main(int argc,char **argv,char **envp)
{
    int z;        /* Status return code */
    int sck_unix;    /* Socket */
    struct sockaddr_un adr_unix;    /* AF_UNIX */
    int len_unix;    /* length */
    const char pth_unix[] = "/tmp/my_sock";    /* pathname */

    /*
     * Create a AF_UNIX (aka AF_LOCAL) socket:
     */
    sck_unix = socket(AF_UNIX,SOCK_STREAM,0);

    if(sck_unix == -1)
        bail("socket()");

    /*
     * Here we remove the pathname for the
     * socket,in case it existed from a
     * prior run.Ignore errors (it maight
     * not exist).
     */
    unlink(pth_unix);

    /*
     * Form an AF_UNIX Address:
     */
    memset(&adr_unix,0,sizeof adr_unix);

    adr_unix.sun_family = AF_LOCAL;
    strncpy(adr_unix.sun_path,pth_unix,
            sizeof adr_unix.sun_path-1)
        [sizeof adr_unix.sun_path-1] = 0;
    len_unix = SUN_LEN(&adr_unix);

    /*
     * Now bind the address to the socket:
     */
    z = bind(sck_unix,
            (struct sockaddr *)&adr_unix,
            len_unix);
    if(z == -1)
        bail("bind()");
    /*
     * Display all of our bound sockets
     */
    system("netstat -pa --unix 2>/dev/null |"
            "sed -n '/^Active UNIX/,/^Proto/P;"
            "/af_unix/P'");
    /*
     * Close and unlink our socket path:
     */
    close(sck_unix);
    unlink(pth_unix);

    return 0;
}
上面的这个例子的步骤如下:
1 在第28行定义了sck_unix来存放创建的套接口文件描述符。
2 在第29行定义了本地地址结构并且命名为adr_unix。这个程序将会用一个AF_LOCAL套接口地址来处理这个结构。
3 通过调用socket函数来在第37行创建了一个套接口。在第39行检测错误并报告。
4 在第48行调用unlink函数。因为AF_UNIX地址将会创建一个文件系统对象,如果不再需要必须进行删除。如果这个程序最后一次运行时没有删除,这条语句会试着进行删除。
5 在第53行adr_unix的地址结构被清0。
6 在第55行将地址族初始化为AF_UNIX。
7 第57行到第59行向地址结构中拷贝路径名"/tmp/my_sock"。在这里使用代码在结构中添加了一个null字节,因为在第61行Linux提供了宏SUN_LEN()需要他。
8 在第61行计算地址的长度。这里的程序使用了Linux提供的宏。然而这个宏依赖于adr_unix.sun_path[]结构成员的一个结束字符。
9 在第66行到68行调用bind函数,将格式化的地址赋值给第37行创建的套接口。
10 在第76行调用netstat命令来证明我们的地址已绑定到了套接口。
11 在第83 行关闭套接口。
12 当调用bind函数时为套接口所创建的UNIX路径名在第66行被删除。
在 第61行将长度赋值给len_unix,在这里使用了SUN_LEN()宏,但是并不会计算拷贝到adr_unix.sun_path[]字符数组中的空 字节。然而放置一个空字节是必要的,因为SUN_LEN()宏会调用strlen函数来计算UNIX路径名的字符串长度。
程序的执行结果如下:
$ ./af_unix
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags    Type     State I-Node PID/Program  name Path
unix 0      []        STREAM          104129 800/af_unix      /tmp/my_sock
$

格式化抽象本地地址
传统AF_UNIX套接口名字的麻烦之一就在于总是调用文件系统对象。这不是必须的,而且也不方便。如果原始的文件系统对象并没有删除,而在bind调用时使用相同的文件名,名字赋值就会失败。
Linux 2.2内核使得为本地套接口创建一个抽象名了成为可能。他的方法就是使得路径名的第一个字节为一个空字节。在路径名中空字节之后的字节才会成为抽象名字的一部分。下面的这个程序是上一个例子程序的修改版本。这个程序采用了一些不同的方法来创建一个抽象的名字。
/*****************************************
 * af_unix2.c
 *
 * AF_UNIX Socket Example
 * Create Abstract Named AF_UNIX/AF_LOCAL
 * ******************************************/
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>

/*
 * This function reports the error and
 * exits back to the shell:
 */
static void bail(const char *on_what)
{
    perror(on_what);
    exit(1);
}

int main(int argc,char **argv,char **envp)
{
    int z;        /* Status return code */
    int sck_unix;    /* Socket */
    struct sockaddr_un adr_unix;    /* AF_UNIX */
    int len_unix;    /* length */
    const char pth_unix[]    /* Abs .Name */
        = "Z*MY-SOCKET*";

    /*
     * Create an AF_UNIX (aka AF_UNIX) socket:
     */
    sck_unix = socket(AF_UNIX,SOCK_STREAM,0);
    if(sck_unix == -1)
        bail("socket()");

    /*
     * Form an AF_UNIX Address
     */
    memset(&adr_unix,0,sizeof adr_unix);
    adr_unix.sun_family = AF_UNIX;
    strncpy(adr_unix.sun_path,pth_unix,
            sizeof adr_unix.sun_path-1)
        [sizeof adr_unix.sun_path-1] = 0;
    len_unix = SUN_LEN(&adr_unix);

    /*
     * Now make first byte null
     */
    adr_unix.sun_path[0] = 0;
    
    z = bind(sck_unix,(struct sockaddr *)&adr_unix,len_unix);
    if(z == -1)
        bail("bind()");
    /*
     * Display all of our bound sockets:
     */
    system("netstat -pa --unix 2>/dev/null |"
            "sed -n '/^Active UNIX/,/^Proto/P;"
            "/af_unix/P'");
    /*
     * Close and unlink our socket path:
     */
    close(sck_unix);
    return 0;
    /*
     * Now bind the address to the socket:
     */
}
这个程序的运行结果如下:
$ ./af_unix2
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags    Type     State I-Node PID/Program name Path
unix 0       []       STREAM         104143 5186/af_unix2    @*MY- SOCKET*
$
从这个输出结果中我们可以看到,套接口地址是以 @*MYSOCKET*的名字出现的。开头的@标志是为netstat命令用来标识抽象UNIX套接口名字。其余的字符是拷贝到字符数组剩余位置的字符。注意@字符出现在我们的Z字符应出现的地方。
整个程序的步骤与前一个程序的相同。然而,地址的初始化步骤有一些不同。这些步骤描述如下:
1 在第31行和第32行定义了套接口抽象名字的字符串。注意字符串的第一个字符为Z。在这个字符串这个多余的字符只是起到占位的作用,因为实际上他会在第6步被一个空字节代替。
2 在第45行通过调用memset函数将整个结构初始经为0。
3 在第47行将地址族设置为AF_UNIX。
4 在第49行使用strncpy函数将抽象名字拷贝到adr_unix.sun_path中。在这里要注意,为了SUN_LEN()宏的使用在目的字符数组的放置了一个结束的空字节。否则就不需要这个结束的空字节。
5 在第53通过Linux所提供的SUN_LEN() C 宏来计算地址的长度。这个宏会在sun_path[]上调用strlen函数,所以需要提供了一个结束字符。
6 这一步是新的:sun_path[]数组的第一个字节被设置为空字节。如果使用SUN_LEN()宏,必须最后执行这一步。
在这一部分,我们了解了如何来创建AF_LOCAL和AF_UNIX的套接口地址。为了计算套接口地址的长度,我们使用SUN_LEN()宏。然而,当计算抽象套接口名字时,我们要十分注意。
 
分享到:
评论

相关推荐

    Linux Socket教程.zip

    Linux Socket学习(十六).txt和Linux Socket学习(十七).txt可能涉及到了更高级的主题,如多路复用I/O,如select()、poll()或epoll(),这些工具可以帮助程序同时处理多个Socket连接。 Linux Socket学习(十四)....

    实战Linux socket编程Linux Socket Programming By Example

    《实战Linux Socket编程》是...总之,《实战Linux Socket编程》的配套源代码是一个宝贵的资源,它使学习者能够动手实践,从而更好地掌握网络编程的核心概念和技术,对于提升Linux系统下的网络编程能力具有重要意义。

    LinuxSocket示例代码

    在IT行业中,Linux Socket是进行网络通信的重要工具,尤其对于系统和网络程序员来说,理解和掌握Linux Socket编程至关重要。本示例代码提供了客户端(client)和服务器端(server)的实现,帮助初学者深入理解如何在...

    实战Linux Socket编程

    在IT领域,Linux Socket编程是网络通信的核心技术之一,它为开发者提供了在Linux操作系统上实现进程间通信(IPC)和网络通信的接口。本实战指南将深入探讨这一主题,帮助你掌握如何在Linux环境中构建高效的网络应用...

    socket_test.zip_Linux下的socket_linux socket_linux socket server_l

    在Linux操作系统中,Socket是一种进程间通信机制,它允许不同进程或者不同计算机之间的通信。本教程将深入探讨Linux下的socket编程,包括服务器和客户端的实现。我们主要关注以下几个知识点: 1. **Socket基本概念*...

    linux socket学习.pdf

    本篇文章将根据给定文件“linux socket学习.pdf”的内容进行展开,重点讨论 socket 的定义、如何创建 socket 以及 socket 与其他常见 I/O 操作的区别。 #### 二、理解 Socket ##### 2.1 定义 Socket Socket 可以...

    linuxsocket.zip

    本压缩包“linuxsocket.zip”包含了基于TCP/IP协议的socket通信测试代码,是学习Linux应用编程的一个实用资源。这里我们将深入探讨Linux TCP/IP socket编程的相关知识点。 1. **TCP/IP协议栈**:TCP/IP协议栈是...

    《实战 Linux Socket编程》练习代码

    通过本书的学习,读者可以掌握如何在Linux环境中使用Socket进行网络通信,构建高性能、稳定的网络应用。下面将根据提供的压缩包文件名“实战 Linux Socket编程代码”来解析其中可能包含的知识点。 1. **Socket基础...

    实战Linux Socket编程.rar

    Linux Socket编程是网络编程的重要组成部分,它为开发者提供...通过这个实战教程,开发者将学习如何在Linux环境中创建和管理Socket,实现网络通信功能,这对于开发服务器应用、网络工具或分布式系统是至关重要的技能。

    Linux Socket

    Linux Socket是Linux操作系统中用于进程间通信(IPC)的一种接口,它允许程序通过网络协议进行数据传输。在本文中,我们将深入探讨Linux ...通过IBM技术论坛的文章,你可以更深入地了解和学习Linux Socket的各种细节。

    linux socket网络驱动深度分析

    在Linux操作系统中,Socket接口是应用程序与网络协议交互的主要接口,它允许程序通过网络发送和接收数据。在本文中,我们将深入探讨“Linux Socket网络驱动深度分析”这一主题,特别是当应用尝试创建一个用于捕获...

    Linux Socket Programming (Linux 套接字编程)

    ### Linux Socket Programming (Linux 套接字编程) #### 知识点概览: 1. **Socket编程基础** ...通过以上知识点的学习,读者可以深入了解Linux环境下的Socket编程,并能够掌握如何设计和实现网络通信程序。

    linux socket 实战编程pdf及源码

    Linux Socket实战编程是深入理解网络通信机制的...总的来说,这份资源对于想要深入理解和精通Linux Socket编程的IT从业者来说是一份宝贵的资料,通过学习和实践,可以显著提升网络编程能力,为职业发展打下坚实基础。

    linux socket programming

    Gay撰写,为读者提供了丰富的实例和深入的理论知识,是学习Linux Socket编程的绝佳资源。 **二、基本Socket概念** ### 1. Socket简介 在Linux环境下,Socket是一种进程间通信(IPC)的方式,它提供了一种在两个...

    Linux Socket两则示例

    在IT行业中,网络通信是至关重要的部分,而Linux Socket编程是实现这一目标的关键技术。Socket是一种接口,允许应用程序通过网络发送和接收数据。本篇将深入探讨标题为"Linux Socket两则示例"的资源,其中包括`echo_...

    linux下的socket聊天室程序

    10. **学习资源**:学习Socket编程,可以参考《UNIX Network Programming》等经典书籍,同时网上有许多关于Linux Socket编程的教程和示例代码可供学习。 总之,"Linux下的socket聊天室程序"是一个很好的实践项目,...

    Linux Socket Programming By Example

    《Linux Socket编程示例》...以上仅为《Linux Socket编程示例》部分章节的知识点总结,实际书中还包含了更多深入的技术细节和实战案例,对于希望掌握Linux环境下网络编程技术的学习者而言,是一本不可多得的参考书籍。

    实战Linux Socket 编程.rar

    本资源“实战Linux Socket编程”旨在帮助你深入理解并掌握这一关键技能,尤其对于那些希望在嵌入式领域有所建树的开发者来说,它是不可或缺的学习资料。 在Linux系统中,Socket编程主要涉及以下几大知识点: 1. **...

    linux socket 客户端和服务器的源代码

    在Linux操作系统中,Socket是一种进程间通信(IPC)机制,广泛用于网络编程,它提供了标准接口,使得应用程序能够发送和接收数据。本篇将详细解析Linux Socket客户端和服务器的源代码,帮助理解其工作原理。 一、...

Global site tag (gtag.js) - Google Analytics