• Socket 相关的知识


    1、关于PF_INET和AF_INET的区别  

    在写网络程序的时候,建立TCP socket:
     sock = socket(PF_INET, SOCK_STREAM, 0);
    然后在绑定本地地址或连接远程地址时需要初始化sockaddr_in结构,其中指定address family时一般设置为AF_INET,即使用IP。

    相关头文件中的定义:AF = Address Family
                                PF = Protocol Family
                                AF_INET = PF_INET

    在windows中的Winsock2.h中,

                               #define AF_INET 0
                               #define PF_INET AF_INET

    所以在windows中AF_INET与PF_INET完全一样.  

        而在Unix/Linux系统中,在不同的版本中这两者有微小差别.对于BSD,是AF,对于POSIX是PF.

        理论上建立socket时是指定协议,应该用PF_xxxx,设置地址时应该用AF_xxxx。当然AF_INET和PF_INET的值是相同的,混用也不会有太大的问题。也就是说你socket时候用PF_xxxx,设置的时候用AF_xxxx也是没关系的,这点随便找个TCPIP例子就可以验证出来了。如下,不论是AF_INET还是PF_INET都是可行的,只不过这样子的话,有点不符合规范。

     /* 服务器端开始建立socket描述符 */
    // if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) 
     if((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1) 
     {
         fprintf(stderr,"Socket error:%s
    a",strerror(errno));
         exit(1);
     }
    
     /* 服务器端填充 sockaddr结构 */ 
     bzero(&server_addr,sizeof(struct sockaddr_in));
     server_addr.sin_family=AF_INET;
     //server_addr.sin_family=PF_INET;
     server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
     server_addr.sin_port=htons(portnumber);
    在函数socketpair与socket的domain参数中有AF_UNIX,AF_LOCAL,AF_INET,PF_UNIX,PF_LOCAL,PF_INET.
    这几个参数有AF_UNIX=AF_LOCAL, PF_UNIX=PF_LOCAL, AF_LOCAL=PF_LOCAL, AF_INET=PF_INET.
    但是对于socketpair与socket的domain参数,使用PF_LOCAL系列,
    而在初始化套接口地址结构时,则使用AF_LOCAL.
    例如:
         z = socket(PF_LOCAL, SOCK_STREAM, 0);
         adr_unix.sin_family = AF_LOCAL;
    Linux 下的address family定义
    /* Supported address families. */
    #define AF_UNSPEC    0
    #define AF_UNIX        1    /* Unix domain sockets         */
    #define AF_LOCAL    1    /* POSIX name for AF_UNIX    */
    #define AF_INET        2    /* Internet IP Protocol     */
    #define AF_AX25        3    /* Amateur Radio AX.25         */
    #define AF_IPX        4    /* Novell IPX             */
    #define AF_APPLETALK    5    /* AppleTalk DDP         */
    #define AF_NETROM    6    /* Amateur Radio NET/ROM     */
    #define AF_BRIDGE    7    /* Multiprotocol bridge     */
    #define AF_ATMPVC    8    /* ATM PVCs            */
    #define AF_X25        9    /* Reserved for X.25 project     */
    #define AF_INET6    10    /* IP version 6            */
    #define AF_ROSE        11    /* Amateur Radio X.25 PLP    */
    #define AF_DECnet    12    /* Reserved for DECnet project    */
    #define AF_NETBEUI    13    /* Reserved for 802.2LLC project*/
    #define AF_SECURITY    14    /* Security callback pseudo AF */
    #define AF_KEY        15      /* PF_KEY key management API */
    #define AF_NETLINK    16
    #define AF_ROUTE    AF_NETLINK /* Alias to emulate 4.4BSD */
    #define AF_PACKET    17    /* Packet family        */
    #define AF_ASH        18    /* Ash                */
    #define AF_ECONET    19    /* Acorn Econet            */
    #define AF_ATMSVC    20    /* ATM SVCs            */
    #define AF_RDS        21    /* RDS sockets             */
    #define AF_SNA        22    /* Linux SNA Project (nutters!) */
    #define AF_IRDA        23    /* IRDA sockets            */
    #define AF_PPPOX    24    /* PPPoX sockets        */
    #define AF_WANPIPE    25    /* Wanpipe API Sockets */
    #define AF_LLC        26    /* Linux LLC            */
    #define AF_CAN        29    /* Controller Area Network      */
    #define AF_TIPC        30    /* TIPC sockets            */
    #define AF_BLUETOOTH    31    /* Bluetooth sockets         */
    #define AF_IUCV        32    /* IUCV sockets            */
    #define AF_RXRPC    33    /* RxRPC sockets         */
    #define AF_ISDN        34    /* mISDN sockets         */
    #define AF_PHONET    35    /* Phonet sockets        */
    #define AF_IEEE802154    36    /* IEEE802154 sockets        */
    #define AF_CAIF        37    /* CAIF sockets            */
    #define AF_MAX        38    /* For now.. */
    
    /* Protocol families, same as address families. */
    #define PF_UNSPEC    AF_UNSPEC
    #define PF_UNIX        AF_UNIX
    #define PF_LOCAL    AF_LOCAL
    #define PF_INET        AF_INET
    #define PF_AX25        AF_AX25
    #define PF_IPX        AF_IPX
    #define PF_APPLETALK    AF_APPLETALK
    #define PF_NETROM    AF_NETROM
    #define PF_BRIDGE    AF_BRIDGE
    #define PF_ATMPVC    AF_ATMPVC
    #define PF_X25        AF_X25
    #define PF_INET6            AF_INET6
    #define PF_ROSE        AF_ROSE
    #define PF_DECnet    AF_DECnet
    #define PF_NETBEUI    AF_NETBEUI
    #define PF_SECURITY    AF_SECURITY
    #define PF_KEY        AF_KEY
    #define PF_NETLINK    AF_NETLINK
    #define PF_ROUTE    AF_ROUTE
    #define PF_PACKET    AF_PACKET
    #define PF_ASH        AF_ASH
    #define PF_ECONET    AF_ECONET
    #define PF_ATMSVC    AF_ATMSVC
    #define PF_RDS        AF_RDS
    #define PF_SNA        AF_SNA
    #define PF_IRDA        AF_IRDA
    #define PF_PPPOX            AF_PPPOX
    #define PF_WANPIPE    AF_WANPIPE
    #define PF_LLC        AF_LLC
    #define PF_CAN        AF_CAN
    #define PF_TIPC        AF_TIPC
    #define PF_BLUETOOTH    AF_BLUETOOTH
    #define PF_IUCV        AF_IUCV
    #define PF_RXRPC    AF_RXRPC
    #define PF_ISDN        AF_ISDN
    #define PF_PHONET    AF_PHONET
    #define PF_IEEE802154    AF_IEEE802154
    #define PF_CAIF        AF_CAIF
    #define PF_MAX        AF_MAX

    2、linux之shutdown()与close()函数

    1.close()函数

    int close(int sockfd);     //返回成功为0,出错为-1.

    close 一个套接字的默认行为是把套接字标记为已关闭,然后立即返回到调用进程,该套接字描述符不能再由调用进程使用,也就是说它不能再作为readwrite的第一个参数,然而TCP将尝试发送已排队等待发送到对端的任何数据,发送完毕后发生的是正常的TCP连接终止序列。

        在多进程并发服务器中,父子进程共享着套接字,套接字描述符引用计数记录着共享着的进程个数,当父进程或某一子进程close掉套接字时,描述符引用计数会相应的减一,当引用计数仍大于零时,这个close调用就不会引发TCP的四路握手断连过程。

    2.shutdown()函数

    int shutdown(int sockfd,int howto);  //返回成功为0,出错为-1.

    该函数的行为依赖于howto的值

        1.SHUT_RD:值为0,关闭连接的读这一半。

        2.SHUT_WR:值为1,关闭连接的写这一半。

        3.SHUT_RDWR:值为2,连接的读和写都关闭。

        终止网络连接的通用方法是调用close函数。但使用shutdown能更好的控制断连过程(使用第二个参数)。

    3.两函数的区别

        closeshutdown的区别主要表现在:

        close函数会关闭套接字ID,如果有其他的进程共享着这个套接字,那么它仍然是打开的,这个连接仍然可以用来读和写,并且有时候这是非常重要的,特别是对于多进程并发服务器来说。

       shutdown会切断进程共享的套接字的所有连接,不管这个套接字的引用计数是否为零,那些试图读得进程将会接收到EOF标识,那些试图写的进程将会检测到SIGPIPE信号,同时可利用shutdown的第二个参数选择断连的方式。

        下面将展示一个客户端例子片段来说明使用closeshutdown所带来的不同结果:

         客户端有两个进程,父进程和子进程,子进程是在父进程和服务器建连之后fork出来的,子进程发送标准输入终端键盘输入数据到服务器端,知道接收到EOF标识,父进程则接受来自服务器端的响应数据。

       s=connect(...); 
    
       if( fork() ){    
           while( gets(buffer) >0) 
               write(s,buf,strlen(buffer)); 
    close(s); exit(
    0); } else { while( (n=read(s,buffer,sizeof(buffer)){ do_something(n,buffer);
    wait(
    0); exit(0); }

    对于这段代码,我们所期望的是子进程获取完标准终端的数据,写入套接字后close套接字,并退出,服务器端接收完数据检测到EOF(表示数据已发送完),也关闭连接,并退出。接着父进程读取完服务器端响应的数据,并退出。然而,事实会是这样子的嘛,其实不然!子进程close套接字后,套接字对于父进程来说仍然是可读和可写的,尽管父进程永远都不会写入数据。因此,此socket的断连过程没有发生,因此,服务器端就不会检测到EOF标识,会一直等待从客户端来的数据。而此时父进程也不会检测到服务器端发来的EOF标识。这样服务器端和客户端陷入了死锁(deadlock)。如果用shutdown代替close,则会避免死锁的发生。

    if( fork() ) {   
          while( gets(buffer) 
             write(s,buffer,strlen(buffer)); 
    
          shutdown(s,1);  
          exit(0); 
     } 

     3、当客户端保持着与服务器端的连接,这时服务器端断开,再开启服务器时会出现: Address already in usr
          可以用netstat -anp | more 可以看到客户端还保持着与服务器的连接(还在使用服务器bind的端口)。这是由于client没有执行close,连接还会等待client的FIN包一段时间。解决方法是使用setsockopt,使得socket可以被重用,是最常用的服务器编程要点。具体的做法为是,在socket调用和bind调用之间加上一段对socket的设置:

    int opt = 1;
    setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,
    &opt,sizeof(opt));

    4、 指定网卡IP信息设置

    /*----------------------------------------------------------------------------
     Network Info
    -----------------------------------------------------------------------------*/
    static int gateway_info(char *dev, char *gateway, int set)
    {
        FILE *fp;
        unsigned char buf[128], gate[16];
        unsigned char *find;
    
        //# get gateway
        sprintf(buf, "route -n | grep 'UG[ 	]' | grep %s | awk '{print $2}'", dev);
    
        fp = popen(buf, "r");
        if(NULL == fp) {
            eprintf("popen error (%s)
    ", buf);
            return -1;
        }
    
        if(!fgets(gate, 16, fp))    {
            strcpy(gate, "0.0.0.0");
        }
        else {
            find = strchr(gate,'
    ');    //# remove '
    '
            if(find) *find='';
        }
        pclose(fp);
    
        if(set)    //# set gateway
        {
            if(strcmp(gate, "0.0.0.0")) {
                sprintf(buf, "route del default gw %s %s", gate, dev);
                system_user(buf);
            }
            sprintf(buf, "route add default gw %s %s", gateway, dev);
            system_user(buf);
        }
        else
        {
            strcpy(gateway, gate);
        }
    
        return 0;
    }
    
    int get_net_info(int devno, dvr_net_info_t *inet)
    {
        int ret, fd;
        char dev[8];
        struct ifreq ifr;
    
        fd = socket(AF_INET, SOCK_DGRAM, 0);
    
        /* I want to get an IPv4 IP address */
        ifr.ifr_addr.sa_family = AF_INET;
    
        /* I want IP address attached to "eth0" */
        sprintf(dev, "eth%d", devno);
        strncpy(ifr.ifr_name, dev, IFNAMSIZ-1);
    
        //# check up/down
        ioctl(fd, SIOCGIFFLAGS, &ifr);
        inet->state = ifr.ifr_flags & IFF_UP;
    
        #if 1
        if(!inet->state) {    //# down
            close(fd);
            strcpy(inet->ip, "0.0.0.0");
            strcpy(inet->mask, "255.255.255.0");
            strcpy(inet->gate, "0.0.0.0");
            return 0;
        }
        #endif
    
        ret = ioctl(fd, SIOCGIFADDR, &ifr);
        if(ret<0)    
            strcpy(inet->ip, "0.0.0.0");
        else    
            sprintf(inet->ip, "%s", inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
    
        ret = ioctl(fd, SIOCGIFNETMASK, &ifr);
        if(ret<0)    
            strcpy(inet->mask, "255.255.255.0");
        else    
            sprintf(inet->mask, "%s", inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
    
        close(fd);
    
        gateway_info(dev, inet->gate, 0);
    
        return 0;
    }
    
    int set_net_info(int devno, dvr_net_info_t *inet)
    {
            int ret, fd;
            char dev[8], cmd[32];
            struct ifreq ifr;
            struct sockaddr_in sin;
    
            if(!strcmp(inet->ip, "0.0.0.0"))
                    return -1;
    
            sprintf(dev, "eth%d", devno);
    
            fd = socket(AF_INET, SOCK_DGRAM, 0);
            strncpy(ifr.ifr_name, dev, IFNAMSIZ);
    
            //# check up/down
            ioctl(fd, SIOCGIFFLAGS, &ifr);
            inet->state = ifr.ifr_flags & IFF_UP;
            if(!inet->state) {    //# down
                    ifr.ifr_flags |= IFF_UP;
                    ioctl(fd, SIOCSIFFLAGS, &ifr);
                    sleep(1);
            }
    
            if(inet->type == NET_DHCP)
            {
                    sprintf(cmd, "udhcpc -n -i %s", dev);
                    ret = system_user(cmd);
            }
            else if(inet->type == NET_STATIC)
            {
                    //memset(&sin, 0, sizeof(struct sockaddr));
                    sin.sin_family = AF_INET;
                    sin.sin_addr.s_addr = inet_addr(inet->ip);
                    memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
                    ioctl(fd, SIOCSIFADDR, &ifr);
    
                    sin.sin_addr.s_addr = inet_addr(inet->mask);
                    memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
                    ret = ioctl(fd, SIOCSIFNETMASK, &ifr);
                    if(ret < 0)
                            dprintf("netmask: Invalid argument
    ");
    
                    gateway_info(dev, inet->gate, 1);
            }
    
            close(fd);
    
            return ret;
    }

    5、recv和recvform

      recv和recvfrom都可用TCP或者UDP,只是习惯性TCP用recv,因为基于连接的对方socket是已知的,UDP用recvfrom,因为一般用没有bind远端socket,接受到本地端口的所有数据,需要recvfrom识别远端地址。

      recv的recvfrom是可以替换使用的,只是recvfrom多了两个参数,可以用来接收对端的地址信息,这个对于udp这种无连接的,可以很方便地进行回复。而换过来如果你在udp当中也使用recv,那么就不知道该回复给谁了,如果你不需要回复的话,也是可以使用的。另外就是对于tcp是已经知道对端的,就没必要每次接收还多收一个地址,没有意义,要取地址信息,在accept当中取得就可以加以记录了

     6、gethostname和gethostbyname

      gethostname() -- 获取进程所在机器的计算机的名字。
      gethostbyname() -- 用域名或主机名获取IP地址,这个域名或主机名可以是本地机器的主机名/域名;也可以是远端节点的域名。

     7、Linux下端口复用(SO_REUSEADDR与SO_REUSEPORT) 

      只考虑AF_INET的情况(同一端口指ip地址与端口号都相同)
      1.freebsd支持SO_REUSEPORT和SO_REUSEADDR选项,而linux只支持SO_REUSEADDR选项。
      2.freebsd下,使用SO_REUSEPORT选项,两个tcp的socket可以绑定同一个端口;同样,使用SO_REUSEPORT选项,两个udp的socket可以绑定同一个端口。
      3.linux下,两个tcp的socket不能绑定同一个端口;而如果使用SO_REUSEADDR选项,两个udp的socket可以绑定同一个端口。
      4.freebsd下,两个tcp的socket绑定同一端口,只有第一个socket获得数据。
      5.freebsd下,两个udp的socket绑定同一端口,如果数据包的目的地址是单播地址,则只有第一个socket获得数据,而如果数据包的目的地址是多播地址,则两个socket同时获得相同的数据。
      6.linux下,两个udp的socket绑定同一端口,如果数据包的目的地址是单播地址,则只有最后一个socket获得数据,而如果数据包的目的地址是多播地址,则两个socket同时获得相同的数据。

      SO_REUSEADDR提供如下四个功能:
      1.SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。 
      2.SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。 
      3.SO_REUSEADDR允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。 
      4.SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播)。 

      SO_REUSEPORT选项有如下语义:
      此选项允许完全重复捆绑,但仅在想捆绑相同IP地址和端口的套接口都指定了此套接口选项才性。 
      如果被捆绑的IP地址是一个多播地址,则SO_REUSEADDR和SO_REUSEPORT等效。 

      使用这两个套接口选项的建议
      在所有TCP服务器中,在调用bind之前设置SO_REUSEADDR套接口选项; 
      当编写一个同一时刻在同一主机上可运行多次的多播应用程序时,设置SO_REUSEADDR选项,并将本组的多播地址作为本地IP地址捆绑。

    8、UDP 调用 connect的作用 

      1.UDP中可以使用connect系统调用

      2.UDP中connect操作与TCP中connect操作有着本质区别。
        TCP中调用connect会引起三次握手,client与server建立连结.UDP中调用connect内核仅仅把对端ip&port记录下来。

      3.UDP中可以多次调用connect,TCP只能调用一次connect。
      UDP多次调用connect有两种用途:
        1,指定一个新的ip&port连结,指定新连结,直接设置connect第二个参数即可。
        2,断开和之前的ip&port的连结,断开连结,需要将connect第二个参数中的sin_family设置成 AF_UNSPEC即可。

      4.UDP中使用connect可以提高效率.原因如下:
      普通的UDP发送两个报文内核做了如下:
        #1:建立连结
        #2:发送报文
        #3:断开连结
        #4:建立连结
        #5:发送报文
        #6:断开连结
      采用connect方式的UDP发送两个报文内核如下处理:
        #1:建立连结
        #2:发送报文
        #3:发送报文另外一点, 每次发送报文内核都由可能要做路由查询。

      5.采用connect的UDP发送接受报文可以调用send,write和recv,read操作.当然也可以调用sendto,recvfrom.
      调用sendto的时候第五个参数必须是NULL,第六个参数是0,调用recvfrom,recv,read系统调用只能获取到先前connect的ip&port发送的报文。

      6.UDP中使用connect的好处:
      1:会提升效率.前面已经描述了.
      2:高并发服务中会增加系统稳定性.原因:
      假设client A 通过非connect的UDP与server B,C通信.B,C提供相同服务。为了负载均衡,我们让A与B,C交替通信。A 与 B通信IPa:PORTa <----> IPb:PORTb;A与 C通信IPa:PORTa' <---->IPc:PORTc 。假设PORTa与PORTa'相同了(在大并发情况下会发生这种情况),那么就有可能出现A等待B的报文,却收到了C的报文.导致收报错误.解决方法内就是采用connect的UDP通信方式.在A中创建两个udp,然后分别connect到B,C。

    9、getsockname与getpeername

      getsockname()是返回套接口关联的本地协议地址。
      getpeername()是返回套接口关联的远程协议地址。

      getsockname和getpeername调度时机很重要,如果调用时机不对,则无法正确获得地址和端口。
      TCP:
      1>对于服务器来说,在bind以后就可以调用getsockname来获取本地地址和端口,虽然这没有什么太多的意义。getpeername只有在链接建立以后才调用,否则不能正确获得对方地址和端口,所以他的参数描述字一般是链接描述字而非监听套接口描述字。
      2>对于客户端来说,在调用socket时候内核还不会分配IP和端口,此时调用getsockname不会获得正确的端口和地址(当然链接没建立更不可能调用getpeername),当然如果调用了bind 以后可以使用getsockname。想要正确的到对方地址(一般客户端不需要这个功能),则必须在链接建立以后,同样链接建立以后,此时客户端地址和端口就已经被指定,此时是调用getsockname的时机。
      UDP:
      UDP分为链接和没有链接2种:
      1>没有链接的UDP不能调用getpeername,但是可以调用getsockname,和TCP一样,他的地址和端口不是在调用socket就指定了,而是在第一次调用sendto函数以后
      2>已经链接的UDP,在调用connect以后,这2个函数都是可以用的(同样,getpeername也没太大意义。如果你不知道对方的地址和端口,不可能会调用connect)。

    10、用域名取得主机的ip地址(gethostbyname)

      使用gethostbyname()函数包含2个头文件:

     #include <netdb.h>
     #include <sys/socket.h>

      gethostbyname()函数定义:

    struct hostent *gethostbyname(const char *name);

      这个函数的传入值是域名或者主机名,例如"www.google.com","wpc"等等;传出值,是一个hostent的结构(如下)。如果函数调用失败,将返回NULL

      hostent结构体定义:

    struct hostent {
         char *h_name; //表示的是主机的规范名。例如www.google.com的规范名其实是www.l.google.com。
         char **h_aliases; // 表示的是主机的别名。www.google.com就是google他自己的别名。有的时候,有的主机可能有好几个别名,这些,其实都是为了易于用户记忆而为自己的网站多取的名字。
         int h_addrtype; // 表示的是主机ip地址的类型,到底是ipv4(AF_INET),还是ipv6(AF_INET6)
         int h_length; //表示的是主机ip地址的长度
         char **h_addr_list; //表示的是主机的ip地址,注意,这个是以网络字节序存储的。千万不要直接用printf带%s参数来打这个东西,会有问题的哇。所以到真正需要打印出这个IP的话,需要调用inet_ntop()。
    };

      inet_ntop()函数定义:

    const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) :

      这个函数,是将类型为af的网络地址结构src,转换成主机序的字符串形式,存放在长度为cnt的字符串中。
      这个函数,其实就是返回指向dst的一个指针。如果函数调用错误,返回值是NULL。

      下面是例程,就是通过给定一个主机名,然后调用gethostbyname(hostname),返回一个struct hostent类型的数据结构其中包含 char **h_addr_list(为ip地址列表),然后调用inet_ntoa(struct in_addr in)打印出ip地址。

    #include<stdio.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<netdb.h>
    
    main(int argc,const char **argv)
    {
        long addr;
        struct hostent *hp;
        char **p;
    
        hp=gethostbyname(argv[1]); /* 调用gethostbyname()。调用结果都存在hp中 注意有的获取IP地址的时候,需要去掉“://”前面的和“/”后面的*/
        if(hp==NULL)
        {
             (void)printf("host information for %s not found
    ",argv[1]);
             exit(2);
        }
    for(p=hp->h_addr_list;*p!=0;p++) { struct in_addr in; char **q;
    memcpy(&in.s_addr,*p,sizeof(in.s_addr));
    printf("%s %s",inet_ntoa(in),hp->h_name);/* 将刚才得到的所有地址都打出来。其中调用了inet_ntoa()函数 */ for(q=hp->h_aliases;*q!=0;q++) printf("%s",*q); putchar(' '); } exit(0); }

     11.udp数据发送

      udp数据发送的目标地址,一个固定值,一个接受到的地址,如果经过了路由器,那么这个接受地址变成了路由器地址,再次发送就不是已经的目标地址,而tcp的面向对象连接,建立了链路,所以远程的时候或者访问上级子网的时候,要么采取tcp的,要么采取udp 固定ip地址发送。

  • 相关阅读:
    选择筛选
    添加标签2 jquery 和JS
    最全的常用正则表达式大全
    Javascript和ECMAScript二三事
    display:none和visibility: hidden二三事
    说人话
    渐进增强和优雅降级
    css的优先级以及!important的使用
    解决VS如何同时打开两个工程(xp和win7)
    jQuery验证控件jquery.validate.js使用说明+中文API
  • 原文地址:https://www.cnblogs.com/cslunatic/p/3653828.html
Copyright © 2020-2023  润新知