版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、UNIX套接字网络编程接口的产生与发展过程套接字与UNIX操作系统的关系套接字编程的基本概念面向连接的套接字编程无连接的套接字编程原始套接字Linux系统的网络编程接口第2章 UNIX中的套接字网络编程接口本章提要2.1.1 问题的提出 站在应用程序实现的角度,应用程序如何方便地使用协议栈软件进行通信呢? 如果能在应用程序与协议栈软件之间提供一个软件接口,就可以方便客户与服务器软件的编程。2.1 UNIX套接字网络编程接口的产生与发展 套接字应用程序编程接口是网络应用程序通过网络协议栈进行通信时所使用的接口,即应用程序与协议栈软件之间的接口,简称套接字编程接口(Socket API)。 它定义
2、了应用程序与协议栈软件进行交互时可以使用的一组操作,决定了应用程序使用协议栈的方式、应用程序所能实现的功能、以及开发具有这些功能的程序的难度。2.1 UNIX套接字网络编程接口的产生与发展 加州大学伯克利(Berkley)分校开发并推广了一个包括 TCP/IP 互联协议的 UNIX,称为BSD UNIX(Berkeley Software Distribution UNIX)操作系统,套接字编程接口是这个操作系统的一个部分。 后来的许多操作系统并没有另外搞一套其它的编程接口,而是选择了对于套接字编程接口的支持。 由于这个套接字规范最早是由Berkeley大学开发的,一般将它称为Berkeley
3、 Sockets规范。2.1.2 套接字编程接口的起源与应用 要想实现套接字编程接口,可以采用两种实现方式: 一种是在操作系统的内核中增加相应的软件,网络程序中用系统调用的方法来实现(Unix/Linux) 一种是通过开发操作系统之外的函数库,网络程序中采用调用库函数的方法来实现(Windows) 2.1.3 套接字编程接口的两种实现方式 UNIX操作系统对文件和所有其它的输入/输出设备采用一种统一的的操作模式,就是“打开-读-写-关闭”(open - read - write - close)的I/O模式。 当TCP/IP协议被集成到UNIX内核中的时候,相当于在 UNIX系统中引入了一种新
4、型的I/O 操作,就是应用程序通过网络协议栈来交换数据。 2.1.4 套接字通信与UNIX操作系统的输入/输出 在UNIX系统的实现中,套接字是完全与其他I/O集成在一起的。操作系统和应用程序都将套接字编程接口也看作一种输入/输出机制。 但是,用户进程与网络协议的交互作用实际要比用户进程与传统的I/O设备相互作用要复杂得多。 2.1.4 套接字通信与UNIX操作系统的输入/输出 其次,使用套接字的应用程序必须说明许多细节。仅仅提供open、read、write、close四个过程远远不够。为避免单个套接字函数参数过多,套接字编程接口的设计者定义了多个函数。2.1.4 套接字通信与UNIX操作系
5、统的输入/输出2.2 套接字编程的基本概念 套接口是对网络中不同主机上应用进程之间进行双向通信的端点的抽象,一个套接口就是网络上进程通信的一端,提供了应用层进程利用网络协议栈交换数据的机制。 图2.1 电插座与电话插座的作用2.2.1 什么是套接字(SOCKET) 2.2.1 什么是套接字(SOCKET)? 我们应当从多个层面来理解套接字这个概念的内涵。 从套接字所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议栈进行通信的接口,是应用程序与网络协议栈进行交互的接口。 图2.2 应用进程、套接口、网络协议栈及操作系统的关系 进程、套接口、协议栈、操作系统的关系 从实现的
6、角度来讲,非常复杂。套接字是一个复杂的软件机构,包含了一定的数据结构,包含许多选项,由操作系统内核管理。 从使用的角度来讲,非常简单。对于套接字的操作形成了一种网络应用程序的编程接口(API)。 本书把这一套操作套接字的编程接口函数称作套接字编程接口,套接字是它的操作对象。 总之,套接字是网络通信的基石。 套接字编程接口2.2.2 套接字的特点1通信域 套接字存在于通信域中,通信域是为了处理一般的进程通过套接字通信而引入的一种抽象概念,套接字通常只和同一域中的套接字交换数据。 如果数据交换要穿越域的边界,就一定要执行某种解释程序。 本课程中,仅仅针对Internet域,并且使用Internet
7、协议族(即TCP/IP协议族)来通信。2套接字具有三种类型 每一个正被使用的套接字都有它确定的类型,只有相同类型的套接字才能相互通信。 (1) 数据报套接字 (Datagram SOCKET) 数据报套接字提供无连接的、不保证可靠的独立的数据报传输服务。在Internet通信域中,数据报套接字使用UDP数据报协议形成的进程间通路,具有UDP协议为上层所提供的服务的所有特点。数据报套接字基于UDP协议 图2.3 在Internet通信域中,数据报套接字基于UDP协议 (2)流套接字(Stream SOCKET) 流套接字提供双向的、有序的、无重复的、无记录边界的、可靠的数据流传输服务。在Inte
8、rnet通信域中,流套接字使用TCP协议形成的进程间通路,具有TCP协议为上层所提供的服务的所有特点,在使用流套接字传输数据之前,必须在数据的发送端和接收端之间先建立连接,如下页图2.4所示。 流式套接字基于TCP协议图2.4 在Internet通信域中,流式套接字基于TCP协议(3)原始套接字 (RAW SOCKET) 原始套接字允许对较低层次的协议,如IP、ICMP直接访问,或用于检验新的协议的实现。TCPUDP IP / ICMP Ethernet应 用 程 序流套接字数据报套接字原始套接字原始套接字3套接字由应用层通信进程创建,并为其服务 套接字由应用层的通信进程创建,并为其服务,这就
9、是说,每一个套接字都有一个相关的应用进程,操作该套接字的代码是该进程的组成部分。4使用确定的IP地址和传输层端口号 套接字编程时,往往在生成套接字的描述符后,要将套接字与计算机上的特定的IP地址和传输层端口号相关联,这个过程称为绑定。 一个套接口要使用一个确定的三元组网络地址信息,才能使它在网络中唯一地被标识。 (1)不管是采用对等模式或者客户机/服务器模式,通信双方的应用程序都需要开发。 (2)双方所交换数据的结构和交换数据的顺序有特定的要求,不符合现在成熟的应用层协议的要求时。甚至,有时需要自己去开发应用层协议,自己设计最适合的数据结构和信息交换规程。 2.2.3 套接字的应用场合2.2.
10、4 套接字使用的数据类型和相关的问题1三种表示套接字地址的结构 在套接字编程接口中,专门定义了三种结构型的数据类型,用来存储协议相关的网络地址,在套接字编程接口的函数调用中要用到它们。三种表示套接字地址的结构 (1)sockaddr 结构,针对各种通信域的套接字,存储它们的地址信息: struct sockaddr unsigned short sa_family; / 地址家族 char sa_data14; / 协议地址 (2) sockaddr_in 结构,专门针对Internet通信域,存储套接字相关的网络地址信息,例如IP地址、端口号等信息。 struct sockaddr_in s
11、hort int sin_family; /地址家族 unsigned short int sin_port; /2字节端口号 struct in_addr sin_addr; /4字节IP 地址 unsigned char sin_zero8; /8字节全0 /最后包含一个8字节的全0的域,以使该结构在大小上与sockaddr相同三种表示套接字地址的结构 (3)in_addr 结构,专门用来存储 IP地址: struct in_addr unsigned long s_addr; /4字节IP 三种表示套接字地址的结构 (4)这些数据结构组合使用的一般用法: 首先,定义一个sockaddr_
12、in的结构实例,并将它清零。 比如: struct sockaddr_in myad; memset(&myad, 0, sizeof(struct sockaddr_in); 一般,三种结构组合起来使用: 然后,为这个myad结构赋值,比如: myad.sin_family = AF_INET; myad.sin_port = htons(8080); myad.sin_addr.s_addr = htonl(INADDR_ANY);注1:地址家族有AF_INET、AF_DECnet、等字符常量可 选,在Internet域内进行通信时,应赋值AF_INET。注2:INADDR_ANY字符常量
13、表示该主机的任何一个IP地址。一般,三种结构组合起来使用: 第三步:在函数调用中使用时,将这个结构强制转换为sockaddr类型。 如:accept(listenfd, (sockaddr*)(&myad), &addrlen); 一般,三种结构组合起来使用:2主机字节顺序和网络字节顺序 多字节数据的字节顺序 大端模式 (big endian): 高字节放到低地址上 小端模式 (little endian): 高字节放到高地址上 在具体计算机中多字节数据的存储顺序称为主机字节顺序。 不同的机器内存中主机字节顺序不相同,与CPU设计有关: Motorola 68000、PowerPC系列 - 大
14、端模式 (big endian) Intel x86系列 - 小端模式 (little endian) 多字节数据在网络协议报头中的 顺序,称为 网络字节顺序。 网络通信时一律使用统一的 大端模式网络字节顺序,避免主 机间通信时识别兼容问题。5612347834785612 低址: 高址: 表示 0 x12345678时: Motorola Intel2主机字节顺序和网络字节顺序 网络应用程序要在不同的计算机中运行,多字节数据在各机器内存中存放的主机字节顺序可能不同,可能是大端模式,也可能是小端模式;但是,在网络上传输时,在信包协议报头字段中的网络字节顺序是一定的,都是大端模式。 所以,应用程
15、序在编程的时候,在把主机内存变量中的IP地址和端口号装入网络通信套接字的时候,应当把它们从主机字节顺序转换为网络字节顺序;相反,在主机输出显示时,应从收到的网络字节顺序转换为主机字节顺序。 在不同机器上运行网络程序,解决兼容性问题的途径: 往网络上发送前:将IP地址、端口号转换成网络字节序; 从网络上接收后:将IP地址、端口号转换成主机字节序。四个转换函数 套接字编程接口特为解决这个问题设置了四个函数: htons(): 短整数主机字节顺序转换为 网络字节顺序, 用于端口号。 htonl(): 长整数主机字节顺序转换为 网络字节顺序, 用于IP地址。 说明 h 代表host, n 代表 net
16、work;s 代表short,l 代表 long ntohs(): 短整数网络字节顺序转换为 主机字节顺序,用于端口号。 ntohl(): 长整数网络字节顺序转换为 主机字节顺序,用于IP地址。 这四个函数将被转换的数值作为函数的入口参数,函数返回值是转换后的结果。四个转换函数 说明 h 代表host, n 代表 network;s 代表short,l 代表 long 3两个IP地址转换函数 在因特网中,IP地址常常用点分十进制的表示方法,但在套接字中,IP地址是无符号长整型数,套接字编程接口设置了两个函数,专门用于两种形式的IP地址的转换。(1)inet-addr 函数unsigned lo
17、ng inet_addr(const char* cp) 入口参数cp:点分十进制形式IP地址字符串; 返回值: 无符号长整型的网络字节顺序 的IP地址。例:0 x80 00 00 01u_long“128.0.0.1”char *(2)inet_ntoa函数char* inet_ntoa(struct in_addr in) 入口参数in:包含无符号长整型IP地址的 in_addr结构变量; 返回值: 指向点分十进制IP地址字符串 的指针。 例:0 x80 00 00 01u_longstruct in_addr“128.0.0.1”char * 通常,我们使用域名来标识站点,可以将文字型的
18、主机域名直接转换成IP地址:struct hostent* gethostbyname( const char* name); 入口参数name :是站点的主机域名字符串 返回值: 是指向hostent 结构的指针 hostent结构包含主机名,主机别名数组,返回地址的类型(一般是AF_INET),地址长度字节数,已符合网络字节顺序的IP地址等。 4域名解析函数上述gethostbyname()域名解析函数的返回结构hostent为 struct hostent char* h_name; /主机名 char* h_aliases; /主机别名列表 short h_addrtype; /返回地
19、址的类型 short h_length; /地址的长度 char* h_addr_list; /主机IP地址列表(主机可能多个IP) #define h_addr h_addr_list0; /主机IP地址列表中的第一个地址 ;比如,可以用下面的程序,将主机域名解析出IP地址: h = gethostbyname(某域名); printf(“host name: %s”, hh_name); printf(“IP address is: %s”, inet_ntoa(*(struct in_addr *)hh_addr);4域名解析函数2.3 面向连接的套接字编程2.3.1 套接字的工作过程2
20、.3.2 UNIX套接字编程接口的系统调用1创建套接字SOCKET() SOCKET过程创建一个套接字并返回一个整型描述符: int socket(int proto_family, int type, int protocol);2 BIND()绑定套接字到指定的地址int bind(int sockfd, struct sockaddr* my_addr, int addrlen);3Listen()启动监听int listen( int sockfd, int queue_size); 举例:listen(sockfd, 10); 图2.6 监听套接字使用缓冲区接纳多个客户端的连接请求
21、4ACCEPT() 接受连接请求int accept(int sockfd, struct sockaddr* addr, int* addrlen);举例:int clientfd; / 定义响应套接字描述符变量int addrlen = sizeof(sockaddr); / 获得套接字地址结构长度struct sockaddr_in cltsockaddr; / 定义用于返回客户端地址的结构clientfd = accept(listenfd, (sockaddr*)(&cltsockaddr), &addrlen); / 接受客户连接请求 5CONNECT()请求建立连接int con
22、nect(int sockfd, struct sockaddr* service_addr, int addrlen);举例: if (connect(sockfd, (struct sockaddr*)(&serv_addr), sizeof(struct sockaddr)0) 连接服务器有错时的报错处理,并退出 6 READ()和WRITE()读/写套接字 int read(int sockfd, void* buffer, int len ); int write(int sockfd, void* buffer, int len )7 SEND()和RECV()向套接字发送/接收i
23、nt send(int sockfd, char* buf, int len, int flags );int recv(int sockfd, char* buf, int len, int flags );8CLOSE()关闭套接字int close(int sockfd); 2.3.3 面向连接的套接字编程实例1实例的功能 服务器对来访的客户计数,并向客户报告这个计数值。 客户建立与服务器的一个连接并等待它的输出。 每当连接请求到达时,服务器生成一个可打印的ASCII串信息,将它在连接上发回,然后关闭连接。 客户显示收到的信息,然后退出。1实例的功能 例如,对于服务器接收的第10次客户连
24、接请求,该客户将收到并打印如下信息: This server has been contacted 10 times. 2实例程序的命令行参数 实例是UNIX环境下的C程序,客户和服务器程序在编译后,均以命令行方式执行。 服务器程序执行时可以带一个命令行参数,是用来表示接受客户端请求时的服务器监听套接字的协议端口号。 这个参数是可选的。如果不指定端口号,代码将使用程序内定的缺省端口号5188。 2实例程序的命令行参数 客户程序执行时可以带两个命令行参数:一个是服务器所在计算机的主机名,另一个是服务器监听的协议端口号。这两个参数都是可选的。 如果没有指定协议端口号,客户使用程序内定的缺省端口51
25、88。 如果一个参数也没有,客户使用缺省端口5188和缺省主机名localhost,localhost是映射到客户所在计算机的一个别名。 允许客户与本地机上的服务器通信,对调试是很有用的。3客户程序代码/*-* 程序: client.c* 目的: 创建一个套接字,通过网络连接一个服务器,并打印来自服务器的信息* 语法: client host port * host - 运行服务器的计算机的名字* port - 服务器监听套接字所用协议端口号* 注意:两个参数都是可选的。如果未指定主机名,客户* 使用缺省主机名localhost;如果未指定端口号,客户* 将使用PROTOPORT中给定的缺省协
26、议端口号。*-*/ 3客户程序代码#include #include /* UNIX下,套接字的相关包含文件。*/#include #include #include #include #include #define PROTOPORT 5188 /*缺省协议端口号*/extern int errno;char localhost = “localhost”; /*缺省主机名*/ 3客户程序代码int main(int argc, char *argv) struct hostent *ptrh; /* 指向主机列表中一个条目的指针 */ struct sockaddr_in servadd
27、r; /* 存放服务器端网络地址的结构 */ int sockfd; /* 客户端的套接字描述符 */ int port; /* 服务器端套接字协议端口号*/ char* host; /* 服务器主机名指针 */ int n; /* 读取的字符数 */ char buf1000 ; /* 缓冲区,接收服务器发来的数据 */ 关于argc、argv的说明:例:命令行若键入 # client hp1 8080,则参数个数 argc=3,参数字符串数组元素 argv0=“client”, argv1=“hp1”, argv2=“8080”,在程序中可引用。memset(char*)&servaddr
28、, 0, sizeof(servaddr); servaddr.sin_family = AF_INET; /*因特网协议族*/ /*检查命令行参数,若有则取端口号,否则使用内定缺省值*/if (argc2) port = atoi(argv2) /*若指定协议端口则转换成整数*/ else port = PROTOPORT; /*否则,使用缺省端口号*/if (port0) /*若端口号合法值则将它装入网络地址结构*/ servaddr.sin_port = htons(u_short)port)else /* 否则,打印错误信息并退出*/ fprintf(stderr,”bad port
29、number %sn”,argv2); exit(1);3客户程序代码/* 检查主机参数并指定主机名 */if(argc1) host = argv1; /* 如果指定了主机名参数,就使用它 */else host = localhost; /* 否则,使用缺省值 */ 3客户程序代码/* 将主机名转换成相应的IP地址并复制到servaddr 结构中 */ptrh = gethostbyname(host); /*从服务器主机名得到相应IP地址*/If (char *)ptrh = null) /*检查主机名的有效性,无效则退出*/ fprintf( stderr, ”invalid host
30、: %sn”, host ); exit(1);memcpy(&servaddr.sin_addr, ptrh-h_addr, ptrh-h_length); 3客户程序代码/* 创建一个套接字*/sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd 0) fprintf(stderr, ”socket creation failedn” ); exit(1);/* 请求连接到服务器 */if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr) 0) write(1,b
31、uf, n); /*1stdout设备(即显示器), 0stdin设备 n = recv(sockfd, buf, sizeof( buf ), 0 ); /* 关闭套接字*/ closesocket(sockfd); /* 终止客户程序*/ exit(0); 3客户程序代码4服务器实例代码/*-* 程序:server.c* 目的: 分配一个套接字,然后反复执行如下几步:* (1) 等待客户的下一个连接* (2) 发送一个短消息给客户* (3) 关闭与客户的连接* (4) 转向(1)步* 命令行语法: server port * port 服务器端监听套接字使用的协议端口号* 注意: 端口号可
32、选。如果未指定端口号,服务器使用PROTOPORT中* 指定的缺省端口号*-*/ 4服务器实例代码#include #include #include #include #include #include #define PROTOPORT 5188 /*监听套接字的缺省协议端口号*/#define QLEN 6 /*监听套接字的请求队列大小*/int visits = 0; /* 对于客户连接的计数*/ int main(argc, argv)int argc;char* argv;struct hostent *ptrh; /*指向主机列表中一个条目的指针*/struct sockaddr
33、_in servaddr; /*存放服务器网络地址的结构*/struct sockaddr_in clientaddr; /*存放客户网络地址的结构*/int listenfd; /*监听套接字描述符*/int clientfd; /*响应套接字描述符*/int port; /*协议端口号*/int alen; /*地址长度*/char buf1000; /*供服务器发送字符串所用的缓冲区*/ 4服务器实例代码memset(char*)&servaddr, 0, sizeof(servaddr); /*清空sockaddr结构*/servaddr.sin_family = AF_INET; /
34、*设置为因特网协议族*/servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*设置本地IP地址*/*检查命令行参数,若指定了,就用该端口号,否则使用缺省端口号*/if (argc 1) port = atoi(argv1); /*如果指定了端口号,就将它转换成整数*/else port = PROTOPORT; /*否则,使用缺省端口号*/ 4服务器实例代码if (port 0) /* 测试端口号是否合法 */ servaddr.sin_port = htons(u_short)port);else /* 打印错误信息并退出 */ fprintf( s
35、tderr, ”bad portnumber %sn”, argv1 ); exit(1);/* 创建一个用于监听的流式套接字 */listenfd = SOCKET(AF_INET, SOCK_STREAM, 0);if (listenfd 0) fprintf( stderr, “socket creation failedn” ); exit(1); 4服务器实例代码/* 将本地地址绑定到监听套接字*/if ( bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr) ) 0 ) fprintf(stderr, ”bind
36、 failedn” ); exit(1);/* 开始监听,并指定监听套接字请求队列的长度 */if (listen(listenfd, QLEN) 0) fprintf(stderr, ”listen filedn” ); exit(1); 4服务器实例代码 while(1) /*服务器主循环-接受和处理来自客户端的连接请求*/ alen = sizeof(clientaddr); /*接受客户端连接请求,并生成响应套接字*/ if(clientfd=accept(listenfd,(struct sockaddr *)&clientaddr, &alen) 0 ) fprintf( stde
37、rr, “accept failedn”); exit(1); visits+; /*累加访问的客户数*/ sprintf(buf, “This server has been contacted %d time.n”, visits ); send(clientfd, buf, strlen(buf), 0 ); /*向客户端发送信息*/ closesocket(clientfd); /*关闭响应套接字*/ closesocket(listenfd); /*关闭监听套接字*/ 4服务器实例代码关于阻塞的问题 图2.7 服务器进程因调用ACCEPT()而被阻塞 2.3.4 进程的阻塞问题和对策
38、1什么是阻塞 阻塞是指一个进程执行了一个函数或者系统调用,该函数由于某种原因不能立即完成,因而不能返回调用它的进程,导致进程受控于这个函数而处于等待的状态,进程的这种状态称为阻塞。 2.3.4 进程的阻塞问题和对策图2.8 RECV()函数的两种执行方式 注: recv()函数究竟按阻塞方式还是非阻塞方式执行,取决于程序开始时的设置,默认方式下,recv()函数按阻塞方式执行。 2能引起阻塞的套接字调用 在Berkeley套接字网络编程接口的模型中,套接字的默认行为是阻塞的,具体地说,在一定情况下,有以下操作套接字的系统调用会引起进程阻塞: (1)accept() (2)read()、recv
39、() 和 readfrom() (3)write()、send() 和 sendto() (4)connect() (5)select() (6)closesocket() 3阻塞工作模式带来的问题图2.9 采用阻塞工作模式的服务器不能很好地为多个客户服务 采用阻塞工作模式的单进程服务器是不能很好地同时为多个客户服务的。下图是一个例子。 4一种解决方案 利用 UNIX/Linux 操作系统的 fork() 系统调用,编制多进程并发执行的服务器程序,可以用创建子进程的办法实现。主进程对于每一个连上来上的客户端,专门创建一个子进程为它服务,这样,通过主进程和各个子进程的并发执行,来实现服务器端对多
40、个客户的并发服务。Unix / Linux 系统调用 fork() 的特点进程在调用fork()时,会将自己拷贝一份,一份为父进程,另一份为子进程。CPU会分别调度执行。在各进程中调用fork()后的返回值: 父进程:fork() 返回值为正(为新建的子进程的进程号) 子进程:fork() 返回值为 0, 出错时:fork() 返回值为负。因此,根据fork()返回值就可分清当前进程是父进程还是子进程。另外,调用fork()前的父进程中每个打开的套接字描述符,在fork()返回后会被父进程和子进程共享。此时因为有父、子2个进程共享,故每个套接字描述符引用计数 reference count=2
41、,以后,该共享的套接字描述符在各进程中用close()每关闭1次,引用计数值count就会减1,当套接字描述符的引用计数值count被减为0时,此套接字才真正被close,所占用资源才被释放。这时服务端结点会发送出首部FIN标志为1的TCP段,进入释放TCP连接的过程。父进程主代码 .If (pid = fork() = 0) . 子进程代码 exit(0); else if (pid 0) 报错信息 .父进程主代码 .使用 fork() 时基本的编程框架是:若fork()返回值pid=0,该部分代码为子进程若fork()返回值pid=负,则进行出错处理若fork()返回值pid=正,该部分代
42、码为父进程 父进程 父进程 子进程 典型的并发服务器编程框架listenfd = socket(); /监听套接字bind(listenfd, ); /绑定监听套接字listen(listenfd, LISTENQ); /启动监听for( ; ; ) connfd = accept(listenfd, ); /接受客户连接,生成连接套接字 if (pid = fork() = 0) close(listenfd); /子进程关闭监听套接字 do_it(connfd); /在连接套接字上处理该客户请求 close(connfd); /结束该客户处理, 关闭连接套接字 exit(0); /子进程终
43、止 close(connfd); /父进程关闭连接套接字 注:详情可参见Unix网络编程第2版 第1卷4.7 - 4.8举例:#include #include #include #include int main(int argc, char* argv) int listenfd, clientfd, pid; struct sockaddr_in ssockaddr, csockaddr; char buffer1024; int addrlen, n; Unix / Linux 多进程并发服务器例Unix / Linux 多进程并发服务器例/* 创建监听套接字 */listenfd =
44、 socket(AF_INET, SOCK_STREAM, 0);if (listenfd 0) fprintf(stderr, socket error!n); exit(1); /* 为监听套接字绑定网络地址 */memset(&ssockaddr, 0, sizeof(struct sockaddr_in);ssockaddr.sin_family = AF_INET;ssockaddr.sin_addr.s_addr = htonl(INADDR_ANY);ssockaddr.sin_port = htons(8080);If (bind(listenfd, &ssockaddr, s
45、izeof(struct sockaddr_in) 0) buffern = 0; /串尾标记0 printf(Client Send: %s, buffer); write(clientfd, buffer, n); /同样数据回送给客户端if (n 0) fprintf( stderr, read error!n); exit(3); Unix / Linux 多进程并发服务器例 /* 通信完毕,子进程将关闭与这个客户连接的套接字,此时 n=0 */ printf(client %s closed!n, inet_ntoa(csockaddr.sin_addr); close(client
46、fd); /* 子进程关闭客户连接套接字 */ exit(0); /* 子进程终止 */ else if (pid 0) printf(“fork failed!n”); /pid0时,为出错处理 close(clientfd); /* 主进程,只关心监听套接字,不关心每个客户的 连接套接字,所以关闭这个客户连接套接字 */ /* end of while(1),结束服务器处理各个客户端的连接请求 */ close(listenfd); /* 主进程关闭监听套接字 */ exit(0); /* 主进程终止 */ Unix / Linux 多进程并发服务器例2.4 无连接的套接字编程2.4.1
47、无连接的套接字编程的两种模式 使用数据报套接字开发网络应用程序,既可以采用客户/服务器模式,也可以采用对等模式。图2.10 对等模式的数据报套接字的编程模型 1对等模式 2客户/服务器模式 图2.11 C/S模式的数据报套接字的编程模型(可多个客户端) 2.4.2 两个数据报专用的系统调用1发送数据报 SENDTO() int sendto(int sockfd, const void* msg, /*发送缓冲区*/ int len, unsigned int flags, /*一般置为0*/ struct sockaddr* to, /*接收方套接字地址*/ int tolen); 2接收数
48、据报 RECVFROM() int recvfrom(int sockfd, void* buf, /*接收缓冲区*/ int len, unsigned int flags, /*一般置为0*/ struct sockaddr* from, /*发送方套接字地址*/ int* fromlen) 2.4.3 数据报套接字的对等模式编程实例聊天程序命令行: #chat 目的IP 目的端口 源IP 源端口#include #include #include #include #include #include /* 声明一个定时器超时的中断处理例程 */void int_proc(int sign
49、o) /* signo为Unix系统的信号值 */数据报套接字的对等模式编程实例int main(int argc, char* argv) struct sockaddr_in daddr, saddr, cmpaddr; int sockfd; int timer = 3; char buffer1024; int addrlen, n; /* 判断用户输入的命令行是否正确,如果有错,提示用法 */ if (argc != 5) printf(用法:%s 目的IP 目的端口 源IP 源端口n, argv0); exit(1); /* 设定中断处理函数,并设置时间限制 */ signal(S
50、IGALRM, int_proc); /*若定时器超时,则执行例程int_proc()*/ alarm(timer); /* 设定一个3秒的报警时钟 */ /* 建立数据报套接字 */ sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd 0) fprintf( stderr, socket error!n); exit(1); 数据报套接字的对等模式编程实例/* 为结构变量daddr的各个字段赋值 */addrlen = sizeof(struct sockaddr_in);memset(&daddr, 0, addrlen);daddr.s
51、in_family = AF_INET;daddr.sin_port = htons(atoi(argv2);if (inet_pton(AF_INET, argv1, &daddr.sin_addr ) = 0) fprintf(stderr, “Invaild dest IP!n”); /*stderr为系统标准报错设备*/ exit(1);数据报套接字的对等模式编程实例 int inet_pton(int af, const char *src, void *dst),这个函数将点分十进制IP地址字符串转换到长整型的网络地址,第一个参数af是地址族,第二个参数*src是点分十进制IP来源
52、地址,第三个参数* dst是接受转换后的长整型数据。/* 为结构变量saddr的各个字段赋值 */addrlen = sizeof(struct sockaddr_in);memset(&saddr, 0, addrlen);saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv4);if (inet_pton(AF_INET, argv3, &saddr.sin_addr ) = 0) fprintf(stderr, Invaild source IP!n); exit(0); 数据报套接字的对等模式编程实例/* 绑定地址 */
53、if (bind(sockfd, &saddr, addrlen) 0 ) fprintf( stderr, bind local address error!n); exit(1);/* 从标准输入即键盘获得字符串放入缓冲后,发送给目标地址 */if (fgets(buffer,1024,stdin) = NULL ) exit(0);if (sendto(sockfd,buffer,strlen(buffer),0,&daddr,addrlen)0) fprintf( stderr, sendto error!n); exit(2); 数据报套接字的对等模式编程实例while (1) /*
54、 接收信息并显示 */ n = recvfrom(sockfd,buffer,1024,0,&cmpaddr,&daddrlen); if (n 0) /*根据errno数值是否常量EWOULDBLOCK,区别超时错和一般性错*/ if ( errno = EWOULDBLOCK) fprintf(stderr, recvfrom timeout error!n); else fprintf(stderr, recvfrom error!n); exit(3); 数据报套接字的对等模式编程实例 else /* 比较数据报来源地址cmpaddr与命令行参数中想要与之聊天的目标地址daddr是否一
55、致: */ if (memcmp(cmpaddr, daddr, addrlen) continue; /* 若两个地址不同,则memcmp()返回非0,不是要聊天的那台 机器发来的数据报,因此结束此循环 */ buffern = 0; /* 两个地址相同,是要聊天的那台机器发来的数 据报,因此准备送显示,先将数据最后结束处添0 */ printf( “Received: %s”, buffer); /* 然后送显示 */ 数据报套接字的对等模式编程实例 /* 从标准输入即键盘获得字符串,准备对对方发来数据进行响应 */ if (fgets(buffer,1024,stdin) = NULL
56、) exit(0); /* 发送给目标地址 */ if (sendto(sockfd,buffer,strlen(buffer),0,&daddr,addrlen)0) fprintf(stderr, sendto error!n); exit(3); /* 关闭套接字 */ close(sockfd);数据报套接字的对等模式编程实例2.5 原始套接字 (Raw Socket) 利用原始套接字 ,可以访问基层的网络协议,如IP(网际协议)、ICMP(Internet控制消息协议)、IGMP(Internet组管理协议)等。很多网络实用工具,如traceroute、ping、网络嗅探器(snif
57、fer)程序等,就是利用原始套接字实现的。 Raw socket的作用主要在三个方面:1. 通过raw socket接收发向本机的 ICMP/IGMP信包、数据链 路层帧, 或者用来发送ICMP/IGMP信包、数据链路层帧;2. 接收、发送本机TCP/IP栈不能识别处理的IP信包(如开发本 机内核中还没有实现的基于IP的OSPF协议或自定义协议);3. 用来发送一些自己规定源地址等特殊作用的IP包(如以伪造 自己源IP地址、源端口写IP头、TCP头等等的hacker行为)2.5.1 原始套接字的创建 格式一 (Unix/Linux): int sockRaw=socket(AF_INET, SOCK_RAW, protocol);格式二 (Windows):SOCKET sockRaw=WSASocket(AF_INET, SOCK_RAW, protocol, NULL, 0, 0); 参数 protocol 的取值 要想收到什么样的数据包,就应在参数 protocol 里指定相应的协议类型,取符号常量值如下: IPPROTO_ICMP 1/* ICMP协议 */ IPPROTO_IGMP2 /*
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026宁夏银川丽人妇产医院招聘28人备考题库含答案详解(模拟题)
- 2026陕西西安市西北工业大学材料学院高温功能材料团队招聘1人备考题库含完整答案详解【必刷】
- 2026中冶京诚工程技术有限公司春季校园招聘备考题库含完整答案详解【名校卷】
- 2026安徽池州市直学校招聘教师14人备考题库附答案详解【模拟题】
- 2026陕西安康学院高层次人才招聘备考题库(名校卷)附答案详解
- 2026中国能建中电工程中南院春季校园招聘8人备考题库含完整答案详解【夺冠系列】
- 2026年合作项目进度汇报材料6篇
- 2026上海师范大学康城实验学校第二批教师招聘4人备考题库带答案详解(培优)
- 2025 网络基础之量子通信与网络安全的新突破课件
- 行业特定数据分析与报告生成工具
- 电力行业安全管理与监督指南(标准版)
- 2026吐鲁番高昌区招聘社区工作者36人笔试备考试题及答案解析
- (一模)包头市2026年高三第一次模拟考试历史试卷(含答案)
- 成人肠内营养耐受不良识别与防治专家共识2026
- 1.身心健康很重要(教学课件)统编版道德与法治二年级下册
- 2025-2026学年春季第二学期中小学总务处工作计划
- 血透室发生地震应急预案演练
- 多囊卵巢综合征诊疗指南(2025年版)
- 公司监事会档案管理制度
- 光伏网络安全培训
- TCSES88-2023建设项目竣工环境保护设施验收技术规范污染影响类总则
评论
0/150
提交评论