已阅读5页,还剩23页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第10章 套接字编程基础网络编程有两种主要的编程接口,一种是Berkeley UNIX(用于BSD UNIX)的套接字编程接口,另一种是AT&T的TLI接口(用于UNIX System V)。本章主要介绍Berkeley 套接字编程接口的基本结构、常用的调用和程序以及使用方法。10.1 套接字概述 20世纪80年代早期,美国国防部高级研究计划署给加利福尼亚大学伯克利分校提供了资金,在UNIX系统上首次实现了TCP/IP。Socket编程界面是由4BSD UNIX首先提出,目的是解决互联网的进程通信问题。由于越来越多的计算机厂商采用了Berkeley UNIX,套接字接口被广泛认可并广泛采用,成为事实上的工业标准。目前的SYSV,BSD,OSF都将套接字接口作为系统的一部分。在当时设计如何支持TCP/IP协议时,有两种加入函数的方法,一种是直接加入支持TCP/IP协议的调用,另一种是加入支持一般网络协议的函数,而用参数来指定支持TCP/IP协议。Berkeley采用了后者,这样可以支持多协议族。10.1.1 套接字描述符 套接字使用UNIX文件描述符 (file descriptor) 作为与其他程序通信的方式。UNIX程序在执行任何形式的I/O的时候,程序是在读写一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数。这个文件可能是一个网络连接、队列、管道、终端、磁盘上的文件或者其他的设备。每个进程都有一个文件描述符表,该表中存放打开的文件描述符。用户使用open()等函数得到的文件描述符其实是文件描述符在该表中的索引号,该表项的内容是一个指向文件表的指针。应用程序只要使用该描述符就可以对指定文件进行操作。同样,套接字接口增加了网络通信操作的抽象定义,与文件操作一样,每个打开的套接字都对应一个整数,称它为套接字描述符,该整数也是套接字描述符在文件描述符表中的索引值。但套接字描述符在描述符表中的表项并不指向文件表,而是指向一个与该套接字有关的数据结构。BSD UNIX中新增加了一个socket()函数,应用程序可以调用它来新建一个套接字描述符,完成建立通信的初步工作,一旦建立了一个套接字,应用程序可以使用其他特定的调用来为它添加其他详细信息,以完成建立通信的过程。 socket实质上提供了进程通信的端点,进程通信之前双方必须各自创建一个端点。在互联网内部每一个socket用一个半相关描述:协议,本地地址,本地端口一个完整的socket连接则用一个相关描述:协议,本地地址,本地端口,远地地址,远地端口每一个socket有一个本地唯一的socket号,由操作系统分配。Socket是面向客户服务器模型而设计的,针对客户和服务器程序提供不同的socket调用,客户随机申请一个socket,系统为之分配一个socket号,服务器拥有全局公认的socket,客户可以向它发出连接请求和信息请求。10.1.2 客户服务器模式网络编程中最常见的是客户-服务器模式。以该模式编程时,服务器端有一个进程(或多个进程)首先启动,在指定的端口上监听,等待客户端的程序发来请求,客户端在需要时向服务器端发出的连接请求。一旦连接上之后,就可以按设计的数据交换方法和格式进行数据传输。在使用TCP协议时,一般服务器端进程先使用socket()函数调用得到一个描述符,然后使用bind()函数将一个名字与套接字描述符连接起来,对于Internet域就是将Internet地址绑定到套接字。之后,服务器端使用listen()函数指出等待服务请求队列的长度。然后就可以使用accept()函数等待客户端发起连接(一般以阻塞方式等待连接,也有使用非阻塞的方式),一旦有客户端发出请求,accept()函数返回客户端的地址信息,并返回一个新的套接字描述符,该描述符与原先的套接字有相同的特性,这时服务器端就可以使用这个新的套接字进行读写操作。一般服务端可能在accept()函数返回后创建一个新的进程与客户进行通信,父进程则再到accept()函数处等待另一个连接。客户端进程一般先使用socket()函数得到一个套接字描述符,然后使用connect()函数向指定的服务器上的指定端口发起连接,一旦连接成功返回,就说明已经建立了与服务器的连接,这时就可以通过套接字描述符进行读写操作了。图10.1是在客户端和服务器端使用TCP时,客户进程和服务器进程套接字建立连接的过程。图10.1面向连接的客户服务器模型时序图使用无连接的UDP协议时,服务器端进程先创建一个套接字,之后调用recvfrom()函数接收客户端的数据报,然后调用sendto()函数将要返回客户端的消息发送给客户进程。客户端也要先创建一个套接字,再使用sendto()函数向服务器端进程发出请求,使用recvfrom()函数得到返回的消息。 图10.2无连接的客户服务器模型时序图10.2 TCP初等网络函数 Linux系统是通过提供套接字来进行网络编程的。网络应用程序通过套接字和其他几个函数的调用,返回一个通信的文件描述符,可以将这个描述符看成普通的文件的描述符,通过向描述符读写操作以实现网络之间的数据交流。 10.2.1 服务器端的函数1. 建立服务socket()函数服务器要能够为客户端服务,必须提供服务程序。提供服务程序的过程首先必须先建立套接字以监听客户端的请求。socket()函数就完成这个工作。 socket( )调用格式如下:sockid = socket ( af, type, protocol)返回值sockid是一个整数,即socket号。因此,创建一个socket实际上是向系统申请一个属于自己的socket号,socket( )一共有三个参数。其中:af(address family):地址族。指出本socket所用的地址类型,TCP/IP地址族为AF_INETtype:类型。指创建socket的应用程序所希望的通信服务类型,TCP对应的类型为SOCK_STREAMUDP对应的类型为SOCK_DGRAMIP对应的类型为SOCK_RAWProtocol:协议。指本socket所希望的协议,如TCP, UDP, ICMP等。TCP/IP协议族中,协议族与socket类型基本上可以唯一确定一个协议,因此,协议值通常可以置0。socket系统调用的过程如下:因为套接字有几种类型,首先要注明要建立什么类型的。还要选择套接字的地址格式。socket() 函数有两个最重要的选项是AF_UNIX和IAF_INET。AF_UNIX就像UNIX路径名一样识别套接字。这种形式对于在同一台机器上的进程间通信很有用。而AF_INET使用如 0 这样被点号隔开的4个十进制数的地址格式。除了机器地址以外,还可以利用端口号来允许每台机器上的多个AF_INET套接字。下面主要介绍广泛使用的AF_INET方式。 另一个必须提供的参数是套接字的类型。两个重要的类型是SOCK_STREAM 和SOCK_DGRAM。SOCK_STREAM 表明数据以字符流方式通过套接字。而SOCK_DGRAM 则表明数据是数据报的形式。下面介绍常见并易于使用的SOCK_STREAM 套接字。其功能是指定协议类型。定义如下。#include #include int socket(int domain, int type,int protocol) 其中,domain说明网络程序所在的主机采用的通信协议族AF_UNIX和AF_INET等。AF_UNIX只能够用于单一的UNIX系统进程间通信,而AF_INET是面向Internet的,因而可以允许在远程主机之间通信。Type是网络程序所采用的通信协议SOCK_STREAM,SOCK_DGRAM等。SOCK_STREAM 表明用的是TCP协议,这样会提供按顺序的、可靠的、双向的、面向连接的位流。 SOCK_DGRAM 表明用的是UDP协议,这样只会提供定长的、不可靠的、无连接的通信。 Protocol,由于指定了类型,所以一般只要用0来代替就可以了。 返回值:出错时返回-1,成功时返回套接字描述符sockfd 。 socket()函数指定了协议族(IPv4,IPv6或UNIX)和套接字类型(字节流、数据报或原始套接字),但并没有指定本地协议地址或远程协议地址。socket 为网络通信做基本的准备。成功时返回文件描述符,失败时返回-1,由errno可知道出错的详细情况。 2. bind()函数在建立套接字后,要使用bind() 函数提供套接字监听的地址。功能:bind( ) 给套接字分配一个本地协议地址。即将本地socket地址(包括本地主机地址和本地端口)与所创建的socket号联系起来,即将本地socket地址赋予socket,其格式为:bind (sockid, localaddr, addrlen)其中:sockid,socket号localddr, 本地socket地址,指定本地主机地址和本地端口号addrlen, 地址长度,以字节为单位的地址结构的长度bind( )函数的定义:#include #include int bind(int sockfd, struct sockaddr *my_addr, int addrlen) 其中:sockfd是由socket()函数返回的文件描述符。Addrlen是sockaddr结构的长度。my_addr是指向数据结构 struct sockaddr 的指针,保存地址(即端口和 IP 地址) 信息。sockaddr的定义如下。struct sockaddr unisgned short as_family; char sa_data14; 但由于考虑到系统的兼容性,一般不用这个头文件,而使用另外一个结构struct sockaddr_in 来代替。sockaddr_in的定义如下。struct sockaddr_in unsigned short sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero8; 在Internet的编程中,sin_family一般为AF_INET,sin_addr设置为INADDR_ANY表示可以和任何的主机通信,sin_port是要监听的端口号,sin_zero8是用来填充的。 bind将本地的端口同套接字返回的文件描述符捆绑在一起。该函数返回0表示成功,返回-1表示出错。addrlen 设置为 sizeof(struct sockaddr)。要让内核自动处理地址IP和端口port,可进行如下处理。my_addr.sin_port = 0; /* 让bind( ) 自己选择合适的端口*/my_addr.sin_addr.s_addr = INADDR_ANY; /*自动填上所运行的机器的 IP 地址*/3. listen()函数listen( )函数的功能:将未连接主动套接字的转换为被动套接字,指示内核接受对该套接字的连接请求。用于面向连接服务器,表示愿意接收连接,listen( )在accept( )之前调用。listen( )函数的调用格式是:listen (sockid, quelen)其中:sockid, 本地socket号quelen, 请求队列长度,listen( )以此参数限制排队请求的个数。listen( )函数的定义为:#include int listen(int sockfd, int backlog);其中:sockfd是调用 socket() 函数返回的套接字文件描述符,是绑定后的文件描述符。Backlog用于设置请求排队的最大长度,当有多个客户端程序和服务端相连时, 使用这个表示可以介绍的排队长度。Listen()函数将绑定的文件描述符变为监听套接字,返回的情况与bind()函数相同。下面的代码说明如何利用 socket(),bind() 和 listen() 函数建立连接并可以接受数据。 /* 建立套接字的程序; 源代码由 提供 */ int establish(unsigned short portnum) char mynameMAXHOSTNAME+1; int s; struct sockaddr_in sa; struct hostent *hp; memset(&sa, 0, sizeof(struct sockaddr_in); /*本机地址*/ gethostname(myname, MAXHOSTNAME); /*得到本机地址信息*/ hp= gethostbyname(myname); if (hp = NULL) /*若本机不存在!? */ return(-1); sa.sin_family= hp-h_addrtype; /*这是本主机的地址 */ sa.sin_port= htons(portnum); /* this is our port number */ if (s= socket(AF_INET, SOCK_STREAM, 0) 0) /*建立套接字*/ return(-1); if (bind(s,&sa,sizeof(struct sockaddr_in) 0) close(s); return(-1); /*将地址绑定到套接字*/ listen(s, 3); /* 队列的最大连接数*/ return(s); 4. accept ()函数accept( )用于面向连接的服务器。其功能是在已完成队列头返回下一个已完成的连接。accept( )函数的调用格式为:newsock = accept (sockid, clientaddr, addrlen)其中:sockid, 本地socket号clientaddr, 指向客户socket地址结构的指针,初始值为空,客户连接上后指向客户地址addrlen, 客户socket地址长度accept( )函数的定义:#include int accept(int sockfd, struct sockaddr *cliaddr, int* addrlen);其中,sockfd是listen后的文件描述符。Addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了。bind()、listen()和accept()是服务器端用的函数,调用accept()函数时,服务器端的程序会一直阻塞到某个客户程序发出了连接为止。 accept成功时返回最后的服务器端的文件描述符,这时服务器端就可以向该描述符写信息了。失败时返回-1。 5. fork()函数服务器应该为多个用户服务,在处理连接时,应该还可以接受调用。为此,一般用 fork()函数来处理每个连接。功能:派生新进程。定义:#include pid_t fork (void);在子进程中返回0,在父进程中返回子进程的进程ID。返回码:出错时返回 -1,调用一次返回两次。6. fork()函数的典型应用(1)一个进程可为自己创建一个备份。当一个备份处理一个操作时,其他的备份可以执行其他的任务。这是非常典型的网络服务器。(2)一个进程想执行其他的程序,由于创建新进程的惟一方法是调用fork()函数,进程首先调用fork()函数以生成一个备份,然后其中一个备份(通常为子进程)调用exec来代替自己去执行新程序。下面的代码演示了如何使用 establish() 和 get_connection() 函数来处理多个连接。 #include /* 必须的头文件*/ #include #include #include #include #include #include #include #include #define PORTNUM 50000 /*需要一个端口数值,50000是一个随意选用的数值*/ void fireman(void); void do_something(int); main() int s, t; if (s= establish(PORTNUM) 0) /* 建立端口号*/ perror(establish); exit(1); signal(SIGCHLD, fireman); /* 除去僵进程*/ for (;) /* 循环,等待呼叫*/ if (t= get_connection(s) 0) ; /* 这是一个与套接字共同工作的函数,在得到一个连接后调用该函数*/ void do_something(int s) /* 以下是对套接字进行的操作: : */ 在建立了套接字后,要等待对该套接字的调用。accept() 函数返回一个新的连接到调用方的套接字。 演示的代码如下。 /*在用establish()函数建立的套接字上等待连接的发生 */ int get_connection(int s) int t; /* 连接的套接字*/ if (t = accept(s,NULL,NULL) 0) /* 如果存在某个连接,则接受 */ return(-1); return(t); 10.2.2 在客户端建立连接1. 建立客户端套接字客户端要调用服务器端上的套接字,要先有个套接字。和建立监听的套接字一样,该工作使用 socket() 函数来完成。2. 用connect() 函数连接在给出套接字的地址后,可以用 connect() 函数来连接要监听的套接字。connect( )函数的功能是建立与TCP服务器的连接。其调用格式如下:connect (sockid, dstaddr, addrlen)其中:sockid, 本地socket号dstaddr: 一个指向对方socket地址结构的指针addrlen: 对方socket地址长度connect() 函数的定义: #include #include int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);其中:sockfd 是系统调用 socket() 返回的套接字文件描述符。serv_addr储存了服务器端的连接信息,其中sin_add是服务器端的地址。addrlen serv_addr的长度,设置为 sizeof(struct sockaddr)。3. 客户端用connect()函数发起连接服务器端必须通过调用socket()、bind()和1isten()函数来完成接受外来连接的准备,称为“被动打开(Passive Open)”。客户端通过connect()函数进行主动打开(active open),这引起客户TCP发送一个SYN分节(表示同步),它告诉服务器客户端将在待建立的连接中发送的数据的初始序列号。服务器端必须确认客户的SYN,同时自己也得发送一个SYN分节,它含有服务器端将在同一连接中发送的数据的初始序列号。服务器端以单个分节向客户端发送SYN和对客户SYN的ACK。客户端必须确认服务器端的SYN。4. 连接出错时的返回信息connect()函数是客户端用来同服务器端进行连接的。成功时返回0;sockfd是同服务器端通信的文件描述符,失败时返回-1。(1)服务器超时错误出错原因 :未收到SYN的响应(服务器超时,75秒)。返回值:ETIMEDOUT用户端输出:Connection time out.(2)“服务器无此项端口服务”错误 出错原因 :收到RST响应SYN到达服务器,但该服务器无此项端口服务。返回值:ECONNREFUSE用户端输出:Connection refused(3)ICMP错误,目的地不可达出错原因:ICMP错误,目的地不可达。返回值:EHOSTUNREACH用户端输出:No route to host在给出套接字地址后,可以用 connect() 函数来连接监听的套接字。代码如下。 int call_socket(char *hostname, unsigned short portnum) struct sockaddr_in sa; struct hostent *hp; int a, s; if (hp= gethostbyname(hostname) = NULL) /* 是否知道主机地址? */ errno= ECONNREFUSED; return(-1); /* 不知道*/ memset(&sa,0,sizeof(sa); memcpy(char *)&sa.sin_addr,hp-h_addr,hp-h_length); /* 设置地址*/ sa.sin_family= hp-h_addrtype; sa.sin_port= htons(u_short)portnum); if (s= socket(hp-h_addrtype,SOCK_STREAM,0) 0) /* 得到套接字*/ return(-1); if (connect(s,&sa,sizeof sa) 0) /* 连接 */ close(s); return(-1); return(s); 这个函数返回一个可以通过数据的套接字。 10.2.3 通过套接字传输数据在传输数据的双方建立连接之后,就可以像处理一般的文件一样,直接使用read() 和 write() 函数来进行读写处理,或传输数据。但通常的文件读写相比,套接字的读写一般不能一次得到所要的所有数据,所以要一直循环到得到所需的全部数据。下面是一个将数据读到缓存的简单例子。 int read_data(int s, / * 已经连接的套接字*/ char *buf, /* 指向缓冲区的指针*/ int n /* 所需要的字符数(bytes) */ ) int bcount; /* 读入的字节计数*/ int br; /* 本次应读入的字节数*/ bcount= 0; br= 0; while (bcount 0) bcount += br; /* increment byte counter */ buf += br; /* 移动缓冲区指针,以便下一次读入*/ else if (br 0) /* 向调用者发出一个“错误”信号*/ return(-1); return(bcount); 相同的函数也可以写数据,留给读者思考。10.2.4 关闭连接数据传输完毕之后,需要关闭套接字之间连接。一般可使用 close() 函数关闭每边的套接字连接。如果一边的套接字已经关闭,而另外一边仍在向它写数据,则返回一个错误代码。 套接字功能是将套接字做上“已关闭的标记”,并立即返回进程。将套接字描述字的访问计数器减1。当访问计数器值为0时,引发TCP的四个“分组连接终止”序列,从而关闭套接字。其定义如下。#include int close(int sockfd);10.2.5 面向连接的套接字实例 服务器端通过一个连接向客户端发送字符串“Hello,world!”。只要在服务器上运行该服务器软件,在客户端运行客户软件,客户端就会收到该字符串。1. 服务器端代码实例 /*/ /* 服务器端代码程序 (server.c) */*/ #include stdio.h #include stdlib.h #include errno.h #include string.h #include sys/types.h #include netinet/in.h #include sys/socket.h #include sys/wait.h #define MYPORT 3490 /*服务器监听端口号 */ #define BACKLOG 10 /* 最大同时连接请求数 */ main() intsock fd,new_fd; /* 监听套接字:sock_fd,数据传输套接字:new_fd * / struct sockaddr_in my_addr; /* 本机地址信息 */ struct sockaddr_in their_addr; /* 客户地址信息 */ n_size; if (sockfd = socket(AF_INET, SOCK_STREAM, 0) = -1) /*错误检测 */ perror(socket); exit(1); my_addr.sin_family=AF_INET; my_addr.sin_port=htons(MYPORT); my_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(my_addr.sin_zero),8); if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockad dr) = -1) /*错误检测*/ perror(bind); exit(1); if (listen(sockfd, BACKLOG) = -1) /*错误检测*/ perror(listen); exit(1); while(1) /* main accept() loop */ sin_size = sizeof(struct sockaddr_in); if (new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size) = -1) perror(accept); continue; printf(server: got connection from %s , inet_ntoa(their_addr.sin_addr); if (!fork() /* 子进程代码段 */ if (send(new_fd, Hello, world! , 14, 0) = -1) perror(send); close(new_fd); exit(0); close(new_fd); / * 父进程不再需要该套接字*/ waitpid(-1,NULL,WNOHANG) 0 /*等待子进程结束,清除子进程所占用资源 */ /*end 程序server.c 服务器端首先创建一个套接字,然后将该套接字与本地地址及端口号捆绑,如果成功就在相应的套接字上监听。当accpet()函数捕捉到一个连接服务请求时,就生成一个新的套接字,并通过这个新的套接字向客户端发送字符串“Hello,world!”,然后关闭该套接字。 fork()函数生成一个子进程来处理数据传输部分,fork()语句对于子进程返回的值为0。所以包含fork()函数的if语句是子进程代码部分,它与if语句后面的父进程代码部分是并发执行的。 2. 客户端程序 /*/ /* 客户端代码程序 (client.c) */*/ #include stdio.h #include stdlib.h #include errno.h #include string.h #include netdb.h #include sys/types.h #include netinet/in.h #include sys/socket.h #define PORT 3490 #define MAXDATASIZE 100 /*每次最大数据传输量 */ int main(int argc, char *argv) int sockfd, numbytes; char bufMAXDATASIZE; struct hostent *he; struct sockaddr_in their_addr; if (argc != 2) fprintf(stderr,usage: client hostname ); exit(1); if(he=gethostbyname(argv1)=NULL) herror(gethostbyname); exit(1); if (sockfd = socket(AF_INET, SOCK_STREAM, 0) = -1) perror(socket); exit(1); their_addr.sin_family=AF_INET; their_addr.sin_port=htons(PORT); their_addr.sin_addr = *(struct in_addr *)he-h_addr); bzero(&(their_addr.sin_zero),8); if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr) = -1) /*错误检测*/ perror(connect); exit(1); if (numbytes=recv(sockfd, buf, MAXDATASIZE, 0) = -1) perror(recv); exit(1); bufnumbytes = ; printf(Received: %s,buf); close(sockfd); return 0; /*end程序client.c 客户端代码相对来说要简单一些,首先通过服务器域名获得其IP地址,然后创建一个套接字,调用connect()函数与服务器建立连接,连接成功之后接收从服务器发送过来的数据,最后关闭套接字,结束程序。 无连接的客户服务器程序的在原理上和连接的客户服务器是一样的,两者的区别在于无连接的客户服务器程序中的客户一般不需要建立连接,而且在发送接收数据时,需要指定远程机的地址。 注意,例子中没有错误检查,这在“真实”的程序中是很重要的。应该对此充分重视。3. 用Makefile编译 下面使用GNU(Gnus Not UNIX)的make实用程序来编译。关于make的详细说明,见make的使用介绍。 # Makefile # all:server client server:server.c gcc $ -o $ client:client.c gcc $ -o $ 运行make后会生成服务器端和客户端两个程序。10.3 服务器端和客户端的信息函数 10.3.1 转换和网络方面的信息函数 1. 字节转换函数 在网络上面有着许多类型的主机,也存在不同的语言,如 ASCII和EBCDIC。此外,这些主机表示数据的字节顺序也可能是不同的。例如i386芯片是低字节在内存地址的低端,高字节在高端,而alpha芯片却相反。除非传输的全部都是文本,否则一定要考虑这种情况,必要时应使用相应的函数来转换。在Linux中使用专门的字节转换函数把它们统一起来,这些函数有htons(),ntohs(),htonl() 和 ntohl()。如下所示。unsigned long int htonl(unsigned long int hostlong) unsigned short int htons(unisgned short int hostshort) unsigned long int ntohl(unsigned long int netlong) unsigned short int ntohs(unsigned short int netshort) 在这四个转换函数中,h代表host,n代表 network,s 代表short,l代表long。 htonl()函数的意义是将本机器上的long数据转化为网络上的long,其他几个函数的意义与此类似。 在传输一个整型数据前,应先作如下转换。i= htonl(i); write_data(s, &i, sizeof(i); 在读数据后,则转换回来。read_data(s, &i, sizeof(i); i= ntohl(i); 2. IP和域名的转换 在网络上可以用IP或用域名标志一台机器。下面介绍IP到域名及域名到IP的转换。struct hostent *gethostbyname(const char *hostname) struct hostent *gethostbyaddr(const char *addr,int len,int type) 其中,struct hostent的定义如下。struct hostent char *h_name; /* 主机的正式名称 */ char *h_aliases; /* 主机的别名 */ int h_addrtype; /* 主机的地址类型 AF_INET*/ int h_length; /* 主机的地址长度 对于IP4 是4字节32位*/ char *h_addr_list; /* 主机的IP地址列表 */ #define h_addr h_addr_list0 /* 主机的第一个IP地址*/ gethostbyname可以将机器名(如 )转换为一个结构指针,在这个结构里面储存了域名的信息。 gethostbyaddr可以将一个32位的IP地址如(C0A80001)转换为结构指针。 这两个函数失败时返回NULL 且设置h_errno错误变量,调用h_strerror()函数可以得到详细的出错信息。 3. 字符串的IP和32位的IP转换在网络上的IP地址常用点分十进制表示(如),而在上面的struct in_addr结构中用的是32位的IP。上面的32位IP(C0A80001)就是。为了进行转换可以使用如下两个函数。int inet_aton(const char *cp,struct in_addr *inp) char *inet_ntoa(struct in_addr in) 函数里面 a 代表ASCII,n 代表network。第一个函数表示将点分十进制的IP转换为32位的IP,存储在inp指针里面。第二个函数是将32位IP转换为点分十进制的格式。 4. 服务信息函数 在网络程序里,有时候需要知道端口、IP和服务信息。这时可以使用以下几个函数:int getsockname(int sockfd,struct sockaddr *localaddr,int *addrlen) int getpeername(int sockfd,struct sockaddr *peeraddr, int *addrlen) struct servent *getservbyname(const char *servname,const char *protoname) struct servent *getservbyport(int port,const char *protoname) struct servent char *s_name; /* 正式服务名 *
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 多职能团队绩效评估指标体系模板
- 律师从业资格考试及答案解析
- 2025年教育培训行业在线教育平台质评报告
- 地铁土建安全员考试题库及答案解析
- 企业信誉长期维护承诺书(9篇)
- 2025年人工智能行业人才培养与产业发展研究报告及未来发展趋势预测
- 银行从业资格证考试桂林及答案解析
- 2025年新能源行业智能电网技术应用现状研究报告及未来发展趋势预测
- 企业品牌传播活动策划与执行工具
- 2025年数字化营销行业消费者画像与精准营销研究报告及未来发展趋势预测
- 2025年人保专业笔试真题及答案
- 视频监控作业实施计划方案
- 10.1 国家利益高于一切(教学课件) 2025-2026学年统编版道德与法治八年级上册
- 2025年全国临床医学检验技术中级职称考试真题及答案解析
- 2025年宝鸡市陈仓区社区专职招聘人员(50人)考试模拟试题及答案解析
- 银行公私联动课件
- 制作小火车课件
- 神经符号推理系统-洞察与解读
- 南农《土壤学》课件
- 车库进出口坡道施工方案
- 分子生物学文献阅读汇报
评论
0/150
提交评论