精华内容
下载资源
问答
  • Linux网络编程——原始套接字编程

    万次阅读 多人点赞 2015-03-27 17:47:16
    原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个...另外,必须管理员权限下才能使用原始套接字。原始套接字的创建int socket ( int family, int type, int protocol );参数:family:...

    原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据。区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包)另外,必须在管理员权限下才能使用原始套接字。


    原始套接字的创建

    int socket ( int family, int type, int protocol );

    参数:

    family:协议族 这里写 PF_PACKET

    type:  套接字类,这里写 SOCK_RAW

    protocol:协议类别,指定可以接收或发送的数据包类型,不能写 “0”,取值如下,注意,传参时需要用 htons() 进行字节序转换。

    ETH_P_IP:IPV4数据包

    ETH_P_ARP:ARP数据包

    ETH_P_ALL:任何协议类型的数据包

    返回值:

    成功( >0 ):套接字,这里为链路层的套接字

    失败( <0 ):出错


    实例如下:

    // 所需头文件
    #include <sys/socket.h>
    #include <netinet/ether.h>
    #include <stdio.h>  // perror
    
    int main(int argc,char *argv[])
    {
    	int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL) );
    
    	if(sock_raw_fd < 0){
    		perror("socket");
    		return -1;
    	}
    	
    	return 0;
    }


    获取链路层的数据包

    ssize_t recvfrom(  int sockfd, 

    void *buf, 

    size_t nbytes,

    int flags,

    struct sockaddr *from, 

    socklen_t *addrlen );

    参数:

    sockfd:原始套接字

    buf接收数据缓冲区

    nbytes:接收数据缓冲区的大小

    flags套接字标志(常为0)

    from这里没有用,写 NULL

    addrlen:这里没有用,写 NULL

    返回值:

    成功:接收到的字符数

    失败:-1


    实例如下:

    #include <stdio.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <netinet/ether.h>
    
    int main(int argc,char *argv[])
    {
    	unsigned char buf[1024] = {0};
    	int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    
    	//获取链路层的数据包
    	int len = recvfrom(sock_raw_fd, buf, sizeof(buf), 0, NULL, NULL);
    	printf("len = %d\n", len);
    
    	return 0;
    }

    混杂模式

    默认的情况下,我们接收数据,目的地址是本地地址,才会接收。有时候我们想接收所有经过网卡的所有数据流,而不论其目的地址是否是它,这时候我们需要设置网卡为混杂模式


    网卡的混杂模式一般在网络管理员分析网络数据作为网络故障诊断手段时用到,同时这个模式也被网络黑客利用来作为网络数据窃听的入口。在 Linux 操作系统中设置网卡混杂模式时需要管理员权限。在 Windows 操作系统和 Linux 操作系统中都有使用混杂模式的抓包工具,比如著名的开源软件 Wireshark。


    通过命令给 Linux 网卡设置混杂模式(需要管理员权限)

    设置混杂模式:ifconfig eth0 promisc



    取消混杂模式:ifconfig eth0 -promisc



    通过代码给 Linux 网卡设置混杂模式



    代码如下:

    struct ifreq req;	//网络接口地址
    	
    strncpy(req.ifr_name, "eth0", IFNAMSIZ);			//指定网卡名称
    if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, &req))	//获取网络接口
    {
    	perror("ioctl");
    	close(sock_raw_fd);
    	exit(-1);
    }
    
    req.ifr_flags |= IFF_PROMISC;
    if(-1 == ioctl(sock_raw_fd, SIOCSIFINDEX, &req))	//网卡设置混杂模式
    {
    	perror("ioctl");
    	close(sock_raw_fd);
    	exit(-1);
    }

    发送自定义的数据包:

    ssize_t sendto(   int sockfd,

    const void *buf,

    size_t nbytes,int flags,

    const struct sockaddr *to,        

    socklen_t addrlen );

    参数:

    sockfd原始套接字

    buf发送数据缓冲区

    nbytes:发送数据缓冲区的大小

    flags一般为 0

    to本机网络接口,指发送的数据应该从本机的哪个网卡出去,而不是以前的目的地址

    addrlen:to 所指向内容的长度

    返回值:

    成功:发送数据的字符数

    失败: -1


    本机网络接口的定义



    发送完整代码如下:

    struct sockaddr_ll sll;					//原始套接字地址结构
    struct ifreq req;					//网络接口地址
    
    strncpy(req.ifr_name, "eth0", IFNAMSIZ);			//指定网卡名称
    if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, &req))	//获取网络接口
    {
    	perror("ioctl");
    	close(sock_raw_fd);
    	exit(-1);
    }
    
    /*将网络接口赋值给原始套接字地址结构*/
    bzero(&sll, sizeof(sll));
    sll.sll_ifindex = req.ifr_ifindex;
    
    // 发送数据
    // send_msg, msg_len 这里还没有定义,模拟一下
    int len = sendto(sock_raw_fd, send_msg, msg_len, 0 , (struct sockaddr *)&sll, sizeof(sll));
    if(len == -1)
    {
    	perror("sendto");
    }

    这里头文件情况如下:

    #include <net/if.h>// struct ifreq
    #include <sys/ioctl.h> // ioctl、SIOCGIFADDR
    #include <sys/socket.h> // socket
    #include <netinet/ether.h> // ETH_P_ALL
    #include <netpacket/packet.h> // struct sockaddr_ll


    展开全文
  • unix域套接字的UDP网络编程

    千次阅读 2014-10-31 19:07:38
    unix域套接字的UDP网络编程

    unix域套接字的UDP网络编程,服务端如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/un.h>
    
    #define SA struct sockaddr
    #define PATHNAME "/tmp/unixudp"
    
    void err_sys(const char *errmsg);
    
    int main(void)
    {
        int sockfd;
        struct sockaddr_un servaddr, cliaddr;
        char buf[BUFSIZ];
        ssize_t n;
        socklen_t len = sizeof(cliaddr);
    
        if ((sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1)
            err_sys("socket");
    
        bzero(&servaddr, sizeof(servaddr));
        unlink(PATHNAME);
        servaddr.sun_family = AF_LOCAL;
        strncpy(servaddr.sun_path, PATHNAME, sizeof(servaddr.sun_path) - 1);
    
        if (bind(sockfd, (SA *)&servaddr, sizeof(servaddr)) == -1)
            err_sys("bind");
    
        for(;;){
            n = recvfrom(sockfd, buf, sizeof(buf), 0, (SA *)&cliaddr, &len);
            if (n == -1)
                err_sys("recvfrom");
            else
                printf("%s\n", buf);
    
            if (sendto(sockfd, buf, n, 0, (SA *)&cliaddr, sizeof(cliaddr)) != n)
                err_sys("sendto");
    
        }
        exit(0);
    }
    
    void err_sys(const char *errmsg)
    {
        perror(errmsg);
        exit(1);
    }

    客户端程序:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/un.h>
    
    #define SA struct sockaddr
    #define PATHNAME "/tmp/unixudp"
    
    void err_sys(const char *errmsg);
    
    int main(void)
    {
        int sockfd;
        struct sockaddr_un servaddr, localaddr;
        char buf[BUFSIZ], s[] = "hello china";
        ssize_t n;
    
        if ((sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1)
            err_sys("socket");
    
        bzero(&localaddr, sizeof(localaddr));
        localaddr.sun_family = AF_LOCAL;
        strncpy(localaddr.sun_path, tmpnam(NULL), sizeof(localaddr.sun_path) - 1);
    
        if (bind(sockfd, (SA *)&localaddr, sizeof(localaddr)) == -1)
            err_sys("bind");
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sun_family = AF_LOCAL;
        strncpy(servaddr.sun_path, PATHNAME, sizeof(servaddr.sun_path) - 1);
    
        for(;;){
            if (sendto(sockfd, s, strlen(s), 0, (SA *)&servaddr, sizeof(servaddr)) == -1)
                err_sys("sendto");
            if ((n = read(sockfd, buf, sizeof(buf))) == -1)
                err_sys("read");
            else 
                printf("%s\n", buf);
            usleep((rand() % 10) * 10000);
        }
    
        exit(0);
    }
    
    void err_sys(const char *errmsg)
    {
        perror(errmsg);
        exit(1);
    }


    展开全文
  • C# 网络编程套接字编程基础知识

    千次阅读 2013-07-13 22:08:28
    最近阅读了周存杰编写的《C#网络编程实例教程》并阅读了很多相关方面的资料,同时自己也做了一些套接字编程方面的C#...套接字(Winsock)是一种独立于协议的网络编程接口,OSI中集中会话层和传输层 (补充知识) 简单

          最近阅读了周存杰编写的《C#网络编程实例教程》并阅读了很多相关方面的资料,同时自己也做了一些套接字编程方面的C#程序,所以根据它的知识总结了最近的套接字编程的一些知识点,方便自己的理解与他人的学习,同时也有一些自己以前学习的计算机网络、操作系统等相关知识。

    一.   套接字编程的概念

          套接字(Winsock)是一种独立于协议的网络编程接口,在OSI中集中在会话层和传输层。(补充知识)简单回归网络知识,计算机网络中的”五层协议的体系结构”和”OSI体系结构”如下图所示:

          其中它们每层实现的功能,构成的网络通信简单的示意图如下图所示:(“网际层”对应”网络层”,”网络接口层”对应底层的”数据链路层+物理层”)

          TCP/IP协议的第一个BSD UNIX提供了一个访问通信协议的调用——Socker。Socket类提供了对套接字的支持,提供了一整套属性和方法实现对端口的绑定、监听、连接、数据传输,其中套接字编程接口使用的命名空间为System.Net.Sockets

          (补充知识).Net网络中常用的类如下所示:
          Socket:实现构造一个新的套接字接口对象
          Dns:提供简单的域名解析功能
          IPAddress:提供网际协议(IP)地址的支持,其方法含定义获取主机的IP地址
          IPEndPoint:将网络端点表示为IP地址和端口号
          IPHostEntry:为网络主机地址信息提供容器类,构造一个主机对象
          NetworkStream:提供用于网络访问的基础数据流
          TcpLinster:从TCP网络客户端监听连接
          TcpClient:为TCP网络服务提供客户端连接
          UdpClient:提供用户数据报(UDP)网络服务

    二.Socket类属性

          Socket类常见属性如下所示:
          AddressFamily:定义套接字地址家族,常见的为InterNetwork
          Available:获取从网络已接受到的且可以读取的数据量的大小,存放网络缓冲区中还未处理的数据的大小
          Blocking:决定是否是套接字工作在阻塞模式,默认为False
          Connected:获取套接字是否连接成功的信息,True为连接成功,False为连接失败
          Handle:获取操作系统句柄
          LocalEndPoint:获取本地终端的信息
          ProtocolType:定义套接字使用的协议的类型,常见的为Tcp
          RemoteEndPoint:远程终端信息
          SocketType:定义套接字类型,数据流或数据包

          (补充知识)在网络编程中常使用的两种方法,socket类亦支持该两种基本模式——同步和异步。
          同步:对执行网络操作的函数的调用一只等到操作完成后才将控制返回给调用程序;
          异步:网络操作的函数调用立即返回。
          同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果,异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作。
    因为同步方法调用会导致程序流程中途等待,所以采用同步方法的情况下往往会导致程序执行的延迟,相比来说。在某些条件下选择异步方法调用就可能更好一些。

          举个例子:(引用别人的例子http://zhidao.baidu.com/question/97924815.html
          Socket传输中拿TCP传输为例。假设服务器A客户机B进行通信传输。
          首先需要在A机建立监听线程。监听某一端口,那么B机可以向A机发送通讯请求,B机连接到A机以后。A机可以从他的监听队列中取的一个监听对象。在A端拿到了这个Socket对象就可以进行接收跟发送数据了。这里问题就出现了。假如B端在请求A端的时候请求成功就发送一条数据。那么 A端就可以直接拿Socket对象得到他的信息。但是假如B端并没有在连接成功后直接发送信息而是在后来不确定的时间这内发送的信息。那么A端就无法得到这条信息。通常的做法是用一个定时器去不短的扫描这个数据缓存区。看是不是有数据存在这样效率非常低下。那么如何解决这个问题呢。
          就用到了我们的异步传输。异步传输的原理是:在A端得到这个SOCKET对象以后并不是直接去接收数据而是建立一个回调函数。回调函数是由系统维护的。他在指定的时间自动去扫描数据存储区。假如有数据他就把数据存储到指定的字节数组中,不用用户自己去关心。

    三.Socket编程常用方法

    这里主要介绍几种常用的套接字编程方法:

    1.构造方法
    构造一个新的套接字对象,该方法调用时前面须添加new,返回值为套接字对象实例.
    public Socket(
        AddressFamilyaddressFamily, //网络类型
        SocketTypesocketType,           //套接字类型
        ProtocolTypeprotocolType);     //使用的协议
    )

    2.Bind()方法
    绑定特殊端口用于监听
    public void Bind(EndPoint loadEP) //本地主机,如IPEndPoint对象

    3.Listen()方法
    该方法用于监听端口
    public void Listen(int bcaklog)  //最大可处理的连接数

    4.Accept()方法
    该方法用于决定套接字的状态,用于新建一个套接字处理新建的连接
    public Socket Accept()    //返回值为新的套接字对象实例

    5.BeginAccpet()和EndAccept()方法
    该方法用于异步处理连接请求,与Accept()方法区别是Accept()立即处理连接请求,它是异步处理.
    public IAsyncResult BeginAccept(
    AsyncCallBack callback,  //异步回调
    objecet state  //自定义对象
    )
    EndAccpet()用于结束一个处理异步连接请求和BeginAccept()方法配合使用.
    public socket EndAccept(IAsynResult asyncResult)  //为处理的套接字请求,即BeginAccept()方法的请求.

          由于作者还在学习当中,还有很多方法,下面只提供方法名,具体的方法参数自己再去查找.包括Connect()、BeginConnect()\EndConnect()、Close()、send()、BeginSend()\EndSend()、SendTo()、BeginSendTo()\EndSendTo()、Receive()、BeginReceive()\EndReceive()、ReceiveFrom()、GetSocketOption()等方法.

    四.总结

          上面的叙述就是我最近学习的C#网络编程关于套接字的一些基础知识的理解,其中主要的内容是根据阅读的书。同时参考了一些网上资料及自己学过的网络知识和操作系统知识,这篇总结对我以后的网络编程的学习很有帮组,希望也能帮组大家。同时如果有错误或不足之处,希望大家原谅!最后感谢我看的周存杰同志编写《C#网络编程实例教程》书籍。

        (BY:Eastmount 2013-7-13-夜10点http://blog.csdn.net/eastmount/

     

     

     

    展开全文
  • JAVA网络编程资料(1)-Socket套接字—Java套接字编程.chm
  • 网络编程》基本 TCP 套接字编程

    千次阅读 2014-12-10 18:59:10
    基于 TCP 的套接字编程的所有客户端和服务器端都是从调用 socket 开始,它返回一...套接字通常使用标准的 close 函数关闭,但是也可以使用 shutdown 函数关闭套接字。下面针对套接字编程实现过程中所调用的函数进程分析

            在进行套接字编程之前必须熟悉其地址结构,有关套接字的地址结构可参考文章《套接字编程简介》。基于 TCP 的套接字编程的所有客户端和服务器端都是从调用socket 开始,它返回一个套接字描述符。客户端随后调用connect 函数,服务器端则调用 bindlistenaccept 函数。套接字通常使用标准的close 函数关闭,但是也可以使用 shutdown 函数关闭套接字。下面针对套接字编程实现过程中所调用的函数进程分析。以下是基于 TCP 套接字编程的流程图:





    socket 函数

            套接字是通信端点的抽象,实现端对端之间的通信。与应用程序要使用文件描述符访问文件一样,访问套接字需要套接字描述符。任何套接字编程都必须调用socket 函数获得套接字描述符,这样才能对套接字进行操作。以下是该函数的描述:

    /* 套接字 */
    
    /*
     * 函数功能:创建套接字描述符;
     * 返回值:若成功则返回套接字非负描述符,若出错返回-1;
     * 函数原型:
     */
    #include <sys/socket.h>
    
    int socket(int family, int type, int protocol);
    /*
     * 说明:
     * socket类似与open对普通文件操作一样,都是返回描述符,后续的操作都是基于该描述符;
     * family 表示套接字的通信域,不同的取值决定了socket的地址类型,其一般取值如下:
     * (1)AF_INET         IPv4因特网域
     * (2)AF_INET6        IPv6因特网域
     * (3)AF_UNIX         Unix域
     * (4)AF_ROUTE        路由套接字
     * (5)AF_KEY          密钥套接字
     * (6)AF_UNSPEC       未指定
     *
     * type确定socket的类型,常用类型如下:
     * (1)SOCK_STREAM     有序、可靠、双向的面向连接字节流套接字
     * (2)SOCK_DGRAM      长度固定的、无连接的不可靠数据报套接字
     * (3)SOCK_RAW        原始套接字
     * (4)SOCK_SEQPACKET  长度固定、有序、可靠的面向连接的有序分组套接字
     *
     * protocol指定协议,常用取值如下:
     * (1)0               选择type类型对应的默认协议
     * (2)IPPROTO_TCP     TCP传输协议
     * (3)IPPROTO_UDP     UDP传输协议
     * (4)IPPROTO_SCTP    SCTP传输协议
     * (5)IPPROTO_TIPC    TIPC传输协议
     *
     */
    


    connect 函数

             在处理面向连接的网络服务时,例如 TCP ,交换数据之前必须在请求的进程套接字和提供服务的进程套接字之间建立连接。TCP 客户端可以调用函数connect 来建立与 TCP 服务器端的一个连接。该函数的描述如下:

    /*
     * 函数功能:建立连接,即客户端使用该函数来建立与服务器的连接;
     * 返回值:若成功则返回0,出错则返回-1;
     * 函数原型:
     */
    #include <sys/socket.h>
    
    int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
    /*
     * 说明:
     * sockfd是系统调用的套接字描述符,即由socket函数返回的套接字描述符;
     * servaddr是目的套接字的地址,该套接字地址结构必须包含目的IP地址和目的端口号,即想与之通信的服务器地址;
     * addrlen是目的套接字地址的大小;
     *
     * 如果sockfd没有绑定到一个地址,connect会给调用者绑定一个默认地址,即内核会确定源IP地址,并选择一个临时端口号作为源端口号;
     */
    
             TCP 客户端在调用函数 connect 前不必非得调用 bind 函数,因为内核会确定源 IP 地址,并选择一个临时端口作为源端口号。若 TCP 套接字调用 connect 函数将建立 TCP 连接(执行三次握手),而且仅在连接建立成功或出错时才返回,其中出错返回可能有以下几种情况:

    1. 若 TCP 客户端没有收到 SYN 报文段的响应,则返回 ETIMEOUT 错误;
    2. 若客户端的 SYN 报文段的响应是 RST (表示复位),则表明该服务器主机在我们指定的端口上没有进程在等待与之连接。只是一种硬错误,客户端一接收到 RST 就立即返回ECONNERFUSED 错误;
      • RST 是 TCP 在发生错误时发送的一种 TCP 报文段。产生 RST 的三个条件时:
        • 目的地为某端口的 SYN 到达,然而该端口上没有正在监听的服务器;
        • TCP 想取消一个已有连接;
        • TCP 接收到一个不存在的连接上的报文段;
    3. 若客户端发出的 SYN 在中某个路由器上引发一个目的地不可达的 ICMP 错误,这是一个软错误。客户端主机内核保存该消息,并在一定的时间间隔继续发送 SYN (即重发)。在某规定的时间后仍未收到响应,则把保存的消息(即 ICMP 错误)作为EHOSTUNREACHENETUNREACH 错误返回给进行。


    bind 函数

            调用函数 socket 创建套接字描述符时,该套接字描述符是存储在它的协议族空间中,没有具体的地址,要使它与一个地址相关联,可以调用函数 bind 使其与地址绑定。客户端的套接字关联的地址一般可由系统默认分配,因此不需要指定具体的地址。若要为服务器端套接字绑定地址,可以通过调用函数 bind 将套接字绑定到一个地址。下面是该函数的描述:

    /* 套接字的基本操作 */
    
    /*
     * 函数功能:将协议地址绑定到一个套接字;其中协议地址包含IP地址和端口号;
     * 返回值:若成功则返回0,若出错则返回-1;
     * 函数原型:
     */
    #include <sys/socket.h>
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    /*
     * 说明:
     * sockfd 为套接字描述符;
     * addr是一个指向特定协议地址结构的指针;
     * addrlen是地址结构的长度;
     */
    

            对于 TCP 协议,调用 bind 函数可以指定一个端口号,或指定一个 IP 地址,也可以两者都指定,还可以都不指定。若 TCP 客户端或服务器端不调用 bind 函数绑定一个端口号,当调用 connectlisten 函数时,内核会为相应的套接字选择一个临时端口号。一般 TCP 客户端使用内核为其选择一个临时的端口号,而服务器端通过调用 bind 函数将端口号与相应的套接字绑定。进程可以把一个特定的 IP 地址捆绑到它的套接字上,但是这个 IP 地址必须属于其所在主机的网络接口之一。对于 TCP 客户端,这就为在套接字上发送的 IP 数据报指派了源 IP 地址。对于 TCP 服务器端,这就限定该套接字只接收那些目的地为这个 IP 地址的客户端连接。TCP 客户端一般不把 IP 地址捆绑到它的套接字上。当连接套接字时,内核将根据所用外出网络接口来选择源 IP 地址,而所用外出接口则取决于到达服务器端所需的路径。若 TCP 服务器端没有把 IP 地址捆绑到它的套接字上,内核就把客户端发送的 SYN 的目的 IP 地址作为服务器端的源 IP 地址。

       在地址使用方面有下面一些限制:

    1. 在进程所运行的机器上,指定的地址必须有效,不能指定其他机器的地址;
    2. 地址必须和创建套接字时的地址族所支持的格式相匹配;
    3. 端口号必须不小于1024,除非该进程具有相应的特权(超级用户);
    4. 一般只有套接字端点能够与地址绑定,尽管有些协议允许多重绑定;

    listen 函数

            在编写服务器程序时需要使用监听函数 listen 。服务器进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。listen 函数描述如下:

    /*
     * 函数功能:接收连接请求;
     * 函数原型:
     */
    #include <sys/socket.h>
    
    int listen(int sockfd, int backlog);//若成功则返回0,若出错则返回-1;
    /*
     * sockfd是套接字描述符;
     * backlog是该进程所要入队请求的最大请求数量;
     */
    

    listen 函数仅由 TCP 服务器调用,它有以下两种作用:

    1. socket 函数创建一个套接字时,若它被假设为一个主动套接字,即它是一个将调用connect 发起连接的客户端套接字。listen 函数把一个未连接的套接字转换成一个被动套接字,指示内核应该接受指向该套接字的连接请求;
    2. listen 函数的第二个参数规定内核应该为相应套接字排队的最大连接个数;

    listen 函数一般应该在调用socketbind 这两个函数之后,并在调用accept 函数之前调用。 内核为任何一个给定监听套接字维护两个队列:

    1. 未完成连接队列,每个这样的 SYN 报文段对应其中一项:已由某个客户端发出并到达服务器,而服务器正在等待完成相应的 TCP 三次握手过程。这些套接字处于 SYN_REVD 状态;
    2. 已完成连接队列,每个已完成 TCP 三次握手过程的客户端对应其中一项。这些套接字处于 ESTABLISHED 状态;


    accept 函数

            accept 函数由 TCP 服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。该函数的返回值是一个新的套接字描述符,返回值是表示已连接的套接字描述符,而第一个参数是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字(表示 TCP 三次握手已完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。该函数描述如下:

    /* 函数功能:从已完成连接队列队头返回下一个已完成连接;若已完成连接队列为空,则进程进入睡眠;
     * 函数原型:
     */
    int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);//返回值:若成功返回套接字描述符,出错返回-1;
    /*
     * 说明:
     * 参数 cliaddr 和 addrlen 用来返回已连接的对端(客户端)的协议地址;
     *
     * 该函数返回套接字描述符,该描述符连接到调用connect函数的客户端;
     * 这个新的套接字描述符和原始的套接字描述符sockfd具有相同的套接字类型和地址族,而传给accept函数的套接字描述符sockfd没有关联到这个链接,
     * 而是继续保持可用状态并接受其他连接请求;
     * 若不关心客户端协议地址,可将cliaddr和addrlen参数设置为NULL,否则,在调用accept之前,应将参数cliaddr设为足够大的缓冲区来存放地址,
     * 并且将addrlen设为指向代表这个缓冲区大小的整数指针;
     * accept函数返回时,会在缓冲区填充客户端的地址并更新addrlen所指向的整数为该地址的实际大小;
     *
     * 若没有连接请求等待处理,accept会阻塞直到一个请求到来;
     */
    

    fork 和 exec 函数

    /* 函数功能:创建子进程;
     * 返回值:
     * (1)在子进程中,返回0;
     * (2)在父进程中,返回新创建子进程的进程ID;
     * (3)若出错,则范回-1;
     * 函数原型:
     */
    #include <unistd.h>
    pid_t fork(void);
    /* 说明:
     * 该函数调用一次若成功则返回两个值:
     * 在调用进程(即父进程)中,返回新创建进程(即子进程)的进程ID;
     * 在子进程返回值是0;
     * 因此,可以根据返回值判断进程是子进程还是父进程;
     */
    
    /* exec 序列函数 */
    
    /*
     * 函数功能:把当前进程替换为一个新的进程,新进程与原进程ID相同;
     * 返回值:若出错则返回-1,若成功则不返回;
     * 函数原型:
     */
    #include <unistd.h>
    int execl(const char *pathname, const char *arg, ...);
    int execv(const char *pathnam, char *const argv[]);
    int execle(const char *pathname, const char *arg, ... , char *const envp[]);
    int execve(const char *pathnam, char *const argv[], char *const envp[]);
    int execlp(const char *filename, const char *arg, ...);
    int execvp(const char *filename, char *const argv[]);
    /*  6 个函数的区别如下:
     * (1)待执行的程序文件是 文件名 还是由 路径名 指定;
     * (2)新程序的参数是 一一列出 还是由一个 指针数组 来引用;
     * (3)把调用进程的环境传递给新程序 还是 给新程序指定新的环境;
     */
    

      exec 6个函数在函数名和使用语法的规则上都有细微的区别,下面就从可执行文件查找方式、参数传递方式及环境变量这几个方面进行比较。

    1. 查找方式:前4个函数的查找方式都是完整的文件目录路径 pathname ,而最后两个函数(也就是以p结尾的两个函数)可以只给出文件名 filename,系统就会自动按照环境变量 “$PATH” 所指定的路径进行查找。
    2. 参数传递方式:exec 序列函数的参数传递有两种方式:一种是逐个列举的方式,而另一种则是将所有参数整体构造指针数组传递。在这里是以函数名的第5位字母来区分的,字母为 “l”(list)的表示逐个列举参数的方式,其语法为 const char *arg;字母为 “v”(vertor)的表示将所有参数整体构造指针数组传递,其语法为 char *const argv[]。读者可以观察 execl()、execle()、execlp() 的语法与 execv()、execve()、execvp() 的区别。这里的参数实际上就是用户在使用这个可执行文件时所需的全部命令选项字符串(包括该可执行程序命令本身)。要注意的是,这些参数必须以NULL结束。
    3. 环境变量:exec 序列函数可以默认系统的环境变量,也可以传入指定的环境变量。这里以 “e”(environment)结尾的两个函数 execle() 和 execve() 就可以在 envp[] 中指定当前进程所使用的环境变量。

    	表 1 exec 序列函数的总结	
    前4位	统一为:exec	
    第5位	l:参数传递为逐个列举方式	 execl、execle、execlp
    	 v:参数传递为构造指针数组方式	 execv、execve、execvp
    第6位	e:可传递新进程环境变量	 execle、execve
    	 p:可执行文件查找方式为文件名	 execlp、execvp
    

    其关系如下图:




    并发服务器

            当要求一个服务器同时为多个客户服务时,需要并发服务器。TCP 并发服务器,它们为每个待处理的客户端连接调用 fork 函数派生一个子进程。当一个连接建立时,accept 返回,服务器接着调用 fork 函数,然后由子进程服务客户端,父进程则等待另一个连接,此时,父进程必须关闭已连接套接字。


    close 和 shutdown 函数

            当要关闭套接字时,可使用 closeshutdown 函数,其描述如下:

    /* 函数功能:关闭套接字,若是在 TCP 协议中,并终止 TCP 连接;
     * 返回值:若成功则返回0,若出错则返回-1;
     * 函数原型:
     */
    #include <unistd.h>
    int close(int sockfd);
    
    /*
     * 函数功能:关闭套接字上的输入或输出;
     * 返回值:若成功则返回0,若出错返回-1;
     * 函数原型:
     */
    #include <sys/socket.h>
    int shutdown(int sockfd, int how);
    /*
     * 说明:
     * sockfd表示待操作的套接字描述符;
     * how表示具体操作,取值如下:
     * (1)SHUT_RD     关闭读端,即不能接收数据
     * (2)SHUT_WR     关闭写端,即不能发送数据
     * (3)SHUT_RDWR   关闭读、写端,即不能发送和接收数据
     *
     */
    


    getsockname 和 getpeername 函数

            为了获取已绑定到套接字的地址,我们可以调用函数 getsockname 来实现:

    /*
     * 函数功能:获取已绑定到一个套接字的地址;
     * 返回值:若成功则返回0,若出错则返回-1;
     * 函数原型:
     */
    #include <sys/socket.h>
    
    int getsockname(int sockfd, struct sockaddr *addr, socklen_t *alenp);
    /*
     * 说明:
     * 调用该函数之前,设置alenp为一个指向整数的指针,该整数指定缓冲区sockaddr的大小;
     * 返回时,该整数会被设置成返回地址的大小,如果该地址和提供的缓冲区长度不匹配,则将其截断而不报错;
     */
    /*
     * 函数功能:获取套接字对方连接的地址;
     * 返回值:若成功则返回0,若出错则返回-1;
     * 函数原型:
     */
    #include <sys/socket.h>
    
    int getpeername(int sockfd, struct sockaddr *addr, socklen_t *alenp);
    /*
     * 说明:
     * 该函数除了返回对方的地址之外,其他功能和getsockname一样;
     */
    

    参考资料:

    《Unix 网络编程》

    展开全文
  • 网络套接字编程(socket 详解)

    千次阅读 多人点赞 2019-06-09 22:54:39
    既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接...
  • 基于流式套接字网络编程

    万次阅读 2014-05-03 12:54:35
    网络编程的概念是:多个计算机通过软件编程的方式进行数据传输来达到数据交互的功能。
  • Linux 网络编程——套接字的介绍

    千次阅读 多人点赞 2015-04-14 20:45:58
    套接字是一种通信机制(通信的两方的一种约定),凭借这种机制,不同主机之间的进程可以进行通信。我们可以用套接字中的相关函数来完成通信过程。 流套接字(SOCK_STREAM): 流套接字用于提供面向连接、可靠的数据...
  • 网络编程套接字

    千次阅读 2018-05-08 17:11:11
    套接字,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。 非常非常简单的举例说明下:Socket=Ip...
  • 网络编程》基本 UDP 套接字编程

    千次阅读 2014-12-14 19:21:24
    本节是介绍了基于 UDP 套接字编程
  • 书中全面深入地介绍了如何使用套接字API进行网络编程。全书不但介绍了基本编程内容,还涵盖了与套接字编程相关的高级主题,对于客户/服务器程序的各种设计方法也作了完整的探讨,最后还深入分析了流这种设备驱动机制...
  • 套接字编程简介
  • 套接字基础一个通用套接字地址结构sockaddr:struct sockaddr { unsigned short sa_family; //套接字的协议簇地址类型,AF_XX char sa_data[14];//存储具体的协议地址 };填充特定协议地址时使用sockaddr_instruct ...
  • 书中全面深入地介绍了如何使用套接字API进行网络编程。全书不但介绍了基本编程内容,还涵盖了与套接字编程相关的高级主题,对于客户/服务器程序的各种设计方法也作了完整的探讨,最后还深入分析了流这种设备驱动机制...
  • Linux网络编程——Unix本地套接字

    万次阅读 2017-10-26 12:13:18
    概述 今天,给大家讲解网络编程中的一个内容——Unix 本地套接字。  发现很多人不知道或者不太了解 Unix 本地套接字这个概念,这也难怪,socket API 原本就是为多台主机之间网络通信设计的,并且这种网络 socket ...
  • 网络编程——原始套接字实现原理

    千次阅读 多人点赞 2018-10-26 22:25:10
     1.3、网络层原始套接字 2、原始套接字的实现 2.1 原始套接字报文收发流程 2.2链路层原始套接字的实现  2.2.1 套接字创建 2.2.2 报文接收 2.2.3 报文发送 2.2.4 其它  2.3 网络层原始套接字的实现 2.3.1...
  • Linux网络编程套接字

    千次阅读 2017-03-25 17:55:48
    套接字的本意是插座,在网络中用来描述计算机中不同程序与其他计算机程序的通信方式。  常用的套接字类型有3种:  1)流套接字(SOCK——STREAM):使用了面向连接的可靠的数据通信方式,即TCP套接字;  2)...
  • 通常情况下程序员接所接触到的套接字(Socket)为两类:(1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用;(2)数据报式套接字(SOCK_DGRAM):一种无连接的 Socket,对应于...
  • Linux网络编程:原始套接字编程及实例分析 一、原始套接字能干什么? 二、原始套接字编程 三、原始套接字实例:MAC头部报文分析
  • 通过《Linux网络编程——原始套接字编程》得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢?MAC 头部(有线局域网)注意:CRC、PAD 组包可以...
  • 网络编程>UDP套接字编程

    千次阅读 2019-04-21 14:45:00
    典型的UDP客户/服务器程序的函数调用如下: 1、缓冲区 发送缓冲区用虚线表示,任何UDP套接字都有发送缓冲区,不过该缓冲区仅能表示写到该套接字的UDP数据报的上限。如果应用进程写一个大于套接字缓冲区大小的数据...
  • [Linux网络编程]套接字编程

    千次阅读 2018-08-15 09:17:48
    最开始的学习疑问,网络编程上的这些乱七八糟的名词这些都是用来干什么的? 地址:为了使网络上的计算机通过唯一标识...套接字是什么 两个程序进行通讯连接的一个端点.是连接应用程序和网络驱动程序的桥梁. 网络应...
  • 原始套接字程序设计 能运用Winsock提供的API函数接口进行网络程序的编写。 理解原始套接字的工作原理。...局域网能使用原始套接字进行相关程序设计。 局域网中使用原始套接字对数据报进行捕获、监听。
  • C#套接字编程

    2008-06-10 20:48:39
    C#套接字编程,网络编程,QQ聊天模拟必备知识!
  • 计算机网络自顶向下方法套接字编程作业

    千次阅读 多人点赞 2018-11-30 22:34:02
    本博客是针对,《计算机网络自顶向下方法》一书第二章后面套接字编程作业, 所有代码均已上传至我的github:https://github.com/inspurer/ComputerNetwork 所有代码均本人亲自编写,有问题欢迎评论交流; 如需转载请...
  • 1. 熟悉流式套接字socket函数的使用方法,包括建立连接、数据发送/接收和关闭连接等; 2. 客户端可向服务端发送任意字符串,服务端接收到该字符串后,回送给客户端; 3. 服务端接收到新连接后,能够打印出...
  • 1、套接字、ip、端口介绍 1)、套接字 源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器...或者说,套接字,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机
  • linux套接字编程

    千次阅读 2013-08-18 14:41:41
    套接字编程的各级要素: `主机接口:网络地址ip `协议:特定的协议(TCP & UDP) `端口:client或server的进程终点 套接字 简单的说,套接字就是两个应用程序之间通信管道的终点,这个管道可以唯一的标志一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 169,990
精华内容 67,996
关键字:

在使用套接字进行网络编程时