《简明SOCKET 编程.pdf》由会员分享,可在线阅读,更多相关《简明SOCKET 编程.pdf(26页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。
1、中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)附录一附录一 简明简明 Socket 编程指南编程指南 在本说明文档中,主要讲述了一些网络 SOCKET 编程的基本概念和有关函数说明,并给出了部分示例程序的源代码。在完成“TCP 和 IP 通信程序设计实验”、“实时声音传输实验”和“HTTP 代理实现实验”时,可以参考本文档的内容。一、一、SOCKET 基本概念基本概念 1 Linux/Unix:Socket 函数库函数库 Linux Socket 函数库是从 Berkeley 大学开发的 BSD UNIX 系统中移植过来的。BSD Socket 接口是在众多 Un
2、ix 系统中被广泛支持的 TCP/IP 通信接口,Linux 下的 Socket 程序设计,除了微小的差别之外,也适用于大多数其它 Unix 系统。Socket 的使用,和文件操作比较类似。如同文件的读、写、打开、关闭等操作一样,TCP/IP 网络通信同样也有这些操作,不过它使用的接口不是文件描述符或者 FILE*,而是一个称做 Socket 的描述符。类似于文件操作,对于 Socket,也通过读、写、打开、关闭操作来进行网络数据传送。同时,还有一些辅助的函数,如域名/IP 地址查询、Socket 功能设置等。2.DOS:WATTCP 函数库函数库 关于 WATTCP 函数库,也就是 DOS
3、下 TCP/IP 协议驱动程序库是由加拿大 Waterloo 大学的 Erick Engelke 提供的。NCSA 的 telnet、ftp 等程序,都是利用 Erick Engelke 的 TCP/IP程序库 WATTCP 开发出来的。WATTCP 是一个很容易使用的 C 语言函数库,相对 Unix 和其它环境下广泛使用的 BSD Socket 接口而言,它在 DOS 下提供了一个更为简单和直观的TCP/IP 编程接口。WATTCP 编程接口,相对于 Unix Socket 而言,作了相当的简化。Unix 下,网络操作和文件系统几乎融为一体,但 DOS 下不存在如 Unix 一样强大的网络文
4、件系统功能。因此,在DOS 下没有 Unix 中那种直接用文件系统调用来操作 Socket 的便利。DOS 下的 TCP/IP 通信和 DOS 系统几乎是完全独立的。WATTCP 支持 DOS TCP/IP 环境下的基本 Socket 接口,大致和 Unix Socket 兼容,包括面向连接的 TCP(SOCK_STREAM)和非连接的 UDP(SOCK_DGRAM)型 Socket。另外 WATTCP 提供一些专用的 SOCKET 接口函数。3.Windows Sockets 规范规范 Windows Sockets 规范以 U.C.Berkeley 大学 BSD UNIX 中流行的 Soc
5、ket 接口为范例定义了一套 Micosoft Windows 下网络编程接口。它不仅包含了人们所熟悉的 Berkeley Socket风格的库函数;也包含了一组针对 Windows 的扩展库函数,以使程序员能充分地利用Windows 消息驱动机制进行编程。这一套 Windows Sockets API 能够在所有 3.0 以上版本的 Windows 和所有 Windows 如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)Scokets 实现上使用,所以它不仅为 Windwos Sockets 实现和 Win
6、dows Sockets 应用程序提供了 16 位操作环境,而且也提供了 32 位操作环境。Windows Sockets 也支持多线程的 Windows 进程。一个进程包含了一个或多个同时执行的线程。在 Windows 3.1 非多线程版本中,一个任务对应了一个仅具有单个线程的进程。而我们在本书中所提到的线程均是指在多线程 Windows 环境中的真正意义的线程。在非多线程环境中(例如 Windows 3.0)这个术语是指 Windows Sockets 进程。Windows Sockets规范中的针对 Windows 的扩展部分为应用程序开发者提供了开发具有Windows 应用软件的功能。
7、它有利于使程序员写出更加稳定并且更加高效的程序,也有助于在非占先 Windows 版本中使多个应用程序在多任务情况下更好地运作。除了 WSAStartup()和 WSACleanup()两个函数除外,其他的 Windows 扩展函数的使用不是强制性的。4.套接口基本概念套接口基本概念 通讯的基石是套接口,一个套接口是通讯的一端。在这一端上你可以找到与其对应的一个名字。一个正在被使用的套接口都有它的类型和与其相关的进程。套接口存在于通讯域中。通讯域是为了处理一般的线程通过套接口通讯而引进的一种抽象概念。套接口通常和同一个域中的套接口交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程
8、序)。Windows Sockets 规范支持单一的通讯域,即 Internet 域。各种进程使用这个域互相之间用Internet 协议族来进行通讯(Windows Sockets 1.1 以上的版本支持其他的域,例如 Windows Sockets 2)。套接口可以根据通讯性质分类;这种性质对于用户是可见的。应用程序一般仅在同一类的套接口间通讯。不过只要底层的通讯协议允许,不同类型的套接口间也照样可以通讯。用户目前可以使用两种套接口,即流套接口和数据报套接口。流套接口提供了双向的,有序的,无重复并且无记录边界的数据流服务。数据报套接口支持双向的数据流,但并不保证是可靠,有序,无重复的。也就是
9、说,一个从数据报套接口接收信息的进程有可能发现信息重复了,或者和发出时的顺序不同。数据报套接口的一个重要特点是它保留了记录边界。对于这一特点,数据报套接口采用了与现在许多包交换网络(例如以太网)非常类似的模型。二、二、SOCKET 编程原理编程原理 1.套接口网络编程原理套接口网络编程原理 套接口有三种类型:流式套接口,数据报套接口及原始套接口。流式套接口定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输。数据报套接口定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错。原始套接口允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现
10、的测试等。无连接服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与服务程序之间的相互作用。若使用无连接的套接口编程,程序的流程可以用图3-1表示。如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)服务器 socket()bi nd()readf rom()sendt o()阻塞,等待客户数据处理服务请求 socket()bi nd()sendto()readfrom()客户机服务请求服务应答图 3-1 无连接套接口应用程序时序图 cl ose()cl ose()面向连接服务器处理的请求往往比较复杂
11、,不是一来一去的请求应答所能解决的,而且往往是并发服务器。使用面向连接的套接口编程,可以通过图3-1来表示:其时序。如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)服务器 socket()bi nd()l i sten()阻塞,等待客户数据处理服务请求 socket()connect()w ri te()客户机请求数据应答数据图 3-2 面向连接套接口应用程序时序图 read()read()accept()w ri te()建立连接 cl ose()cl ose()套接口工作过程如下:服务器首先启动,通过调用
12、socket()建立一个套接口,然后调用bind()将该套接口和本地网络地址联系在一起,再调用listen()使套接口做好侦听的准备,并规定它的请求队列的长度,之后就调用accept()来接收连接。客户在建立套接口后就可调用connect()和服务器建立连接。连接一旦建立,客户机和服务器之间就可以通过调用read()和write()来发送和接收数据。最后,待数据传送结束后,双方调用close()关闭套接口。2.Windows Sockets 编程原理编程原理 由于Windows的基于消息的特点,WINSOCK和BSD套接口相比,有如下一些新的扩充:1.异步选择机制 异步选择函数 WSAAsyn
13、cSelect()允许应用程序提名一个或多个感兴趣的网络事件,如FD_READ,FD_WRITE,FD_CONNECT,FD_ACCEPT 等等代表的网络事件。当被提名的网络事件发生时,Windows 应用程序的窗口函数将收到一个消息。这样就可以实现事件驱动了。如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)2.异步请求函数 异步请求函数允许应用程序用异步方式获得请求的信息,如 WSAAsyncGetXByY()类函数。这些函数是对 BSD 标准函数的扩充。函数 WSACancelAsyncRequest()
14、允许用户中止一个正在执行的异步请求。3.阻塞处理方法 WINSOCK 提供了钩子函数负责处理 Windows 消息,使 Windows 的消息循环能够继续。WINSOCK 提供了两个函数(WSASetBlockingHook()和 WSAUnhookBlockingHook()让应用程序设置或取消自己的钩子函数。函数 WSAIsBlocking()可以检测是否阻塞,函数WSACancelBlockingCall()可以取消一个阻塞的调用。4.错误处理 WINSOCK 提供了两个 WSAGetLastError()和 WSASetLastError()来获取和设置最近错误号。5.启动和终止 由于
15、 Windows Sockets 的服务是以动态连接库 WINSOCK.DLL 形式实现的,所以必须要先调用WSAStartup()函数对Windows Sockets DLL进行初始化,协商WINSOCK的版本支持,并分配必要的资源。在应用程序关闭套接口后,还应调用 WSACleanup()终止对 Windows Sockets DLL 的使用,并释放资源,以备下一次使用。在这些函数中,实现 Windows 网络实时通信的关键是异步选择函数 WSAAsyncSelect()的使用。三、三、SOCKET 函数库介绍函数库介绍 1.DOS:WATTCP 函数库函数库 兼容型 Socket 函数
16、WATTCP 支持 DOS TCP/IP 环境下的基本 Socket 接口,大致和 Unix Socket 兼容,包括面向连接的 TCP(SOCK_STREAM)和非连接的 UDP(SOCK_DGRAM)型 Socket。其主要的函数有:WATTCP 初始化:sock_init():初始化 TCP/IP 驱动程序,建立和 pkt driver 的调用关系;sock_exit():清除 TCP/IP 驱动程序和 pkt driver 的联系;由于 DOS 环境下,TCP/IP 驱动程序是完全独立的,为了正常使用,必须首先初始化,使用完毕后,必须清除现场,以免影响其它程序的运行。Socket 创建
17、和释放:socket()、n_close()如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)连接建立和撤销:bind()、listen()、accept()、connect()Socket 发送和接收:n_read()、n_write()、select()数据转换函数 gethostbyname()、inet_addr()、inet_ntoa()这些函数中,n_read()、n_write()、n_close()分别对应于 Unix Socket 接口中的 read()、write()、close()函数,因
18、为需要和 DOS 系统下的文件操作函数区分开,用了另外的名字。以上这些函数,其含义和用法和标准的 Unix Socket 接口基本一致,是实验中应该掌握并学会使用的部分。专用 Socket 接口函数 另外,WATTCP函数库是相当开放的,上面的Socket接口是层次比较高的接口,对Socket的控制功能稍微弱一些,如,函数 n_read()和 n_write()都不是 non_blocking 型的,没有提供non_blocking 选项。如果需要对 TCP Socket 进行更强的控制,可以完全弃之不用,而改用较为低级的 TCP/UDP 接口函数,但是,这要求直接使用 WATTCP 的内部数
19、据结构 Tcp_Socket,对 WATTCP 的内部实现机制有比较清楚的了解,并在程序中间进行必要的协调。具体的函数有:TCP/IP 接口初始化:sock_init():初始化 TCP/IP 驱动程序,建立和 pkt driver 的调用关系;sock_exit():清除 TCP/IP 驱动程序和 pkt driver 的联系;Socket 操作函数:tcp_listen()、tcp_accept():被动等待建立 TCP 连接;tcp_open()、udp_open():主动建立 TCP/UDP 连接;sock_close():关闭 Socket;Socket 读写函数:sock_read
20、()、sock_write()非 non_blocking 的 Socket 读写函数;sock_fastread()、sock_fastwrite()sock_read()和 sock_write()的 non_blocking 版本;sock_flush()完全发送缓冲区中的数据;如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)Socket 等待型操作的容错处理:sock_wait_xxx 宏:sock_wait_established tcp_open 之后,等待连接建立的完成;sock_wait_in
21、put 连接建立之后,等待接收网络数据;sock_wait_closed sock_close 之后,等待连接撤销的完成;关于这些函数的使用,请参见相应的示例。2.Linux/Unix:Socket 函数库函数库 Socket 操作:Socket():分配 Socket#include#include int socket(int domain,int type,int protocol);socket()函数分配一个 Socket 句柄,用于指定特定网络下、使用特定的协议和数据传送方式进行通信。Socket 接口是不仅仅局限于 TCP/IP 的,但是由于 TCP/IP 的广泛使用,它们几乎被
22、完全等同起来了。Socket 句柄分配以后,如果要开始 TCP 通信,还需要建立连接。根据需要,可以主动地建立连接(通过 connect())和被动地等待对方建立连接(通过 listen()),在连接建立后才能使用读写操作通过网络连接进行数据交换。参数说明:domain domain 参数选择通信中使用的协议族,也就是网络的类型,可以是以下之一:AF_UNIX (UNIX 内部协议)AF_INET (ARPA Internet 协议,也就是 TCP/IP 协议族,亦即我们实验中所使用的)AF_ISO (ISO 协议)AF_NS (Xerox Network Systems 协议)AF_IMPL
23、INK(IMP host at IMP link layer)type 数据传送的方式,可以是以下之一:SOCK_STREAM:保证顺序的、可靠传送的双向字节数据流,最为常用,也是 TCP 连接所使用的方式。SOCK_DGRAM:无连接的、不保证可靠的、固定长度(通常很小)的消息传送。SOCK_SEQPACKET:顺序的、可靠的双向固定长度的数据包传送,只用于 AF_NS 类型的网络中。SOCK_RAW:原始的数据传送,适用于系统内部专用的网络协议和接口,和SOCK_RDM一样,只能由超级用户使用。SOCK_RDM:可靠的数据报传送,未实现。protocol protocol 参数指定通信中使
24、用的协议。在给定 Socket 的协议族和传送类型之后,一般情况下所使用的协议也就固定下来,如下表所示,此时 protocol 参数可使用缺省值0;但如果还有多个协议供选择,则必须使用 protocol 参数来标识。协议族(仅考虑 IP传送类型 protocol 参数常量协议类型 如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)协议族)(/usr/include/linux/in.h)SOCK_STREAM IPPROTO_TCP TCP SOCK_DGRAM IPPROTO_UDP UDP SOCK_RAW
25、 IPPROTO_ICMP ICMP AF_INET SOCK_RAW IPRROTO_RAW(raw)返回值:正常执行时,返回 Socket 描述符;否则,返回-1,错误状态在全局变量 errno 中。close():关闭 Socket#include int close(int fd);Socket 和文件描述符的关闭操作都是使用这个函数。参数说明:fd Socket 描述符。返回值:正常时返回 0,-1 表示出错。bind():给 Socket 指定本地地址#include#include int bind(int sockfd,struct sockaddr*my_addr,int a
26、ddrlen);bind 函数给已经打开的 Socket 指定本地地址。这个函数的使用有以下两种情况:如果此 Socket 是面向连接的,而且此 Socket 在连接建立过程中处于被动的地位,即,己方程序使用 listen 函数等待对方建立连接,对方用 connect 函数来向此 Socket 建立连接,这种情况下,必须用 bind 给此 Socket 设定本地地址。在己方使用 listen 函数时,除指定 Socket描述符之外,该 Socket 必须已经用 bind 函数设定好了本地地址(包括 IP 地址和端口号),这样,系统在收到建立连接的网络请求时,才能根据请求的目的地址,识别是通向哪
27、个 Socket的连接,从而己方才能用此 Socket 接收到发给此 Socket 地址的数据包。不指定 Socket 的本地地址,就无法将此 Socket 用于连接建立和数据接收。如果此 Socket 用于无连接的情形,同样也要求给该 Socket 设定本地地址,这样,以后系统从网络中接收到数据后,才知道该送给哪个 Socket 及其相对应的进程。参数说明:sockfd Socket 描述符。addrlen my_addr 结构的长度。my_addr 用于侦听连接请求的本地地址。struct sockaddr 是一个通用型的结构,不仅包含TCP/IP 协议的情况,同时也是为了适合于其它网络,
28、如 AF_NS。由于它的这种通用性,它只是定义了一个一般意义上的存储空间,如/usr/include/linux/socket.h 中所示:struct sockaddr unsigned short sa_family;/*address family,AF_xxx*/char sa_data14;/*14 bytes of protocol address*/;当使用 TCP/IP 协议(即:Internet 协议)时,可用如下的 struct 等价地代替 struct sockaddr(/usr/include/linux/in.h):如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得
29、联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)#define _SOCK_SIZE_ 16 /*sizeof(struct sockaddr)*/struct sockaddr_in short int sin_family;/*Address family*/unsigned short int sin_port;/*Port number*/struct in_addr sin_addr;/*Internet address*/*Pad to size of struct sockaddr.*/unsigned char _pad_SOCK_SIZE_-
30、sizeof(short int)-sizeof(unsigned short int)-sizeof(struct in_addr);在 Socket 程序中,等待建立连接一方的准备过程请参见编程实例,以及 listen()、accept()的说明。返回值:正常时返回 0,否则返回-1,同时 errno 是系统错误码。listen():准备接受连接请求。#include int listen(int s,int backlog);在用 bind()给一个 Socket 设定本地地址之后,就可以将这个 Socket 用于接受连接请求,即 listen()。调用 listen()之后,系统将给此
31、 Socket 配备一个连接请求的队列,暂存系统接收到的、申请向此 Socket 建立连接的请求,等待用户程序用 accept()正式接受该请求。队列长度,就由 backlog 参数指定。如下面的简图所示:通信己方 Me 通信对方 Socket_Me Peer Sockets 0-连接建立请求 1-connect()Socket_peer_1 1-连接建立请求 2-connect()Socket_peer_2 连接请求暂存队列 backlog-1-连接建立请求 n-connect()Socket_peer_n 如果短时间内向己方建立连接的请求过多,己方来不及处理,那么排在 backlog 之后
32、的请求将被系统拒绝。因此,backlog 参数实际上规定了己方程序能够容许的连接建立处理速度。至于己方程序使用此 Socket(及其指定的本地地址)实际建立连接的个数,由己方程序调用accept()的次数来决定,参见 accept()的说明。参数说明:sSocket 描述符。backlog连接请求暂存队列长度。返回值:正常时返回 0;否则返回-1,同时 errno 是系统错误码。accept:接受指定 Socket 上的连接请求#include#include int accept(int s,struct sockaddr*addr,int*addrlen);在调用 listen()之后,系
33、统就在 Socket 的连接请求暂存队列里存放每一个向该 Socket(及其本地地址)建立的连接请求。accept()函数的作用就是,从该暂存队列中取出一个连接请求,用该 Socket 的数据,创建一个新的 Socket:Socket_New,并为它分配一个文件描述符。如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)Socket_New 即标识了此次建立的连接,可被己方用来向连接的另一方发送和接收数据(write/read,send/recv)。同时,原 Socket 仍然保持打开状态不变,继续用于等待网络连
34、接请求。如果该 Socket 的暂存队列中没有待处理的连接请求,根据 Socket 的特征选项(是否non_blocking),accept()函数将选择两种方式:如果该 Socket 不是 non_blocking 型的,accept()将一直等待,直到收到一个连接请求后才返回;如果该 Socket 是 non_blocking 型的,那么accept()将立即返回,但如果没有连接请求,只返回错误信息,不创建新的 Socket_New。accept()返回后,如果创建了新的 Socket_New 来标识新建立的连接,那么参数 addr 指定的结构里面将会有对方的地址信息,addrlen 是地
35、址信息的长度。关于 accept()的进一步信息,如:如何检测某 Socket 有无待处理的连接请求、如何在使用 accept()接受连接请求之前先获取连接对方的地址、如何根据获取的对方地址信息拒绝该连接请求等,请参阅 Linux manual,此处不再累述。参数说明:s Socket 描述符。addr accept()接受连接后,在addr指向的结构中存放对方的地址信息。如果是AF_INET Socket,该地址信息就是对方的 IP 地址和端口号。addrlen 在调用accept()之前,*addrlen必须被设置为addr数据结构的合法长度。在accept()返回之后,*addrlen
36、中是对方地址信息的长度。返回值:如果正常创建了一个新的连接,那么返回非负的整数:即新连接的 Socket 描述符(注意,用于等待连接请求的原 Socket 保持打开状态不变,可用于接收新的连接请求。);否则,返回-1,errno 是系统错误码。connect:建立连接#include#include int connect(int sockfd,struct sockaddr*serv_addr,int addrlen);前面提到的函数,如 bind、listen、connect 等,都是用于被动地等待对方建立连接时需要使用的,而 connect()函数,则是主动地向对方建立连接时使用的。co
37、nnect()使用一个事先打开的 Socket,和目的方(即通信对方,或称服务器一方)地址信息,向对方发出连接建立请求。一个完整的 Socket 通信发起过程可简单地图示为:主动发起方(客户方)被动接受方(服务方)listen(sock_w);等待连接建立请求 connect(sock_s)发送连接建立请求 sock_d=accept();接受连接建立请求 发送应答 收到连接建立应答 此时,在 sock_s 和 sock_d 之间,一个连接就建立完毕。如果是 SOCK_STREAM 型的 Socket,通常只用 connect()建立一个正常的连接。但如果是如果您在阅读过程中发现疏漏和错误,请
38、您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)SOCK_DGRAM 型的 Socket,connect()函数并不象上图中那样向目的方发出连接建立请求,而只是简单地用给出的地址设置该 Socket 的目的地址,以后该 Socket 的无连接数据报就发往该目的地址。因此,对于 SOCK_DGRAM 型的 Socket,可以多次调用 connect()来改变该Socket 的目的地址。SOCK_DGRAM 型的 Socket 与本实验关系不大,故不再详述。参数说明:sockfd Socket 描述符。serv_addr 通信目的方的地址。其格式参
39、见 bind()的说明。addrlen 目的地址长度。返回值:连接正常建立时返回 0;否则,返回-1,系统错误码在 errno 中。send/recv:用 Socket 发送和接收数据#include#include int send(int s,const void*msg,int len,unsigned int flags);int sendto(int s,const void*msg,int len,unsigned int flags,const struct sockaddr*to,int tolen);int recv(int s,void*buf,int len,unsign
40、ed int flags);int recvfrom(int s,void*buf,int len,unsigned int flags,struct sockaddr*from,int*fromlen);在连接建立完成后,通信双方就可以使用以上这些函数来进行数据的发送和接收操作。其中,send 和 recv 用于连接建立以后的发送和接收;sendto 和 recvfrom 用于非连接的协议。对于非 non_blocking 型的 Socket,send 将等待数据发送完后才返回;对于 non_blocking 型的 Socket,send 将立即返回,用户程序需要用 select()函数决定
41、网络发送是否结束。类似地,对于非 non_blocking 型的 Socket,若系统没有收到任何数据,recv 将等待接收数据到达后才返回;对于 non_blocking 型的 Socket,recv 将立即返回,并返回错误信息或者接收到的数据字节数。sendto 和 recvfrom 因为是非连接型的发送和接收,必须在参数中给出目的地址或者存放源地址的空间。参数说明:s Socket 描述符;msg,buf 存放接收或者发送数据的存储空间;len 接收或者发送数据的字节数;to,from sendto 和 recvfrom 所使用的,目的方地址和存放源地址的空间;tolen,fromlen
42、 目的地址和源地址空间大小。flag 通常设为 0,详细说明请参见 Linux Manual。返回值:send/sendto 返回实际发送的数据字节数,或者-1,表示出错;recv/recvfrom 返回实际接收到的数据字节数,或者-1,表示出错。read/write:用系统文件操作进行 Socket 通信#include ssize_t read(int fd,void*buf,size_t count);ssize_t write(int fd,const void*buf,size_t count);如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与
43、网络通信实验室(copyright 1999)在连接建立完成后,对于连接建立过程中被动的一方,在 accept()正常返回后,它返回一个新的 Socket,并且为该 Socket 分配了一个文件描述符;对于连接请求发起方,connect()正常返回后,相应的 Socket 中也包含有已分配的文件描述符。因此,可以使用标准的 Unix文件读写函数 read()/write()来进行 Socket 通信。要注意的是,由于网络数据和磁盘文件不一样,不是已经准备好的,因此,每次读写操作不一定能传送完指定长度的数据,需要由程序反复进行剩余部分的传送。另外,文件描述符是较底层的文件操作参数,不同于 C 语
44、言中常用的 FILE*。FILE*是使用 fread/fwrite 函数来进行读写操作的。参数说明:fd 文件或者 Socket 描述符。buf 数据缓冲区。count 数据字节数。返回值:正常时,返回所读写的字节数(注意,可能小于 count 参数指定的数目);否则,返回-1,errno 是系统错误码。getsockopt/setsockopt:获取、设置 Socket 特征选项。#include#include int getsockopt(int s,int level,int optname,void*optval,int*optlen);int setsockopt(int s,in
45、t level,int optname,const void*optval,int optlen);由于在普通的 Socket 编程中很少涉及这些选项,在这里不作介绍。non_blocking 特性:由于前面多处提到 non_blocking 特性,这里介绍一下如何设置这种特性。在这里,我们又可以看到 Socket 和文件描述符在 Unix 系统中的相似性。实际上 non_blocking 特性也是通过 Unix 文件操作函数来设置的:#include#include fcntl(socket,F_SETFL,O_NONBLOCK);数据格式转换#include unsigned long i
46、nt htonl(unsigned long int hostlong);unsigned short int htons(unsigned short int hostshort);unsigned long int ntohl(unsigned long int netlong);unsigned short int ntohs(unsigned short int netshort);数据格式转换函数提供和硬件平台无关的、主机数据字节顺序和网络字节顺序之间的转换。由于各种平台 CPU 结构的不同,在不同的硬件平台下,主机的字节顺序有两种情况:Intel 80 x86 和 SUN Spar
47、c CPU 的低位在前格式和 Motorola CPU(68000、PowerPC)等的高位在前格式。网络数据交换要求网络中所有的 int 型数据都有统一的字节顺序:高位在前格式,因此在 Socket 函数库中提供了以上统一的字节顺序转换函数。在 Socket 程序中使用的地址数据,如端口号等,都必须遵循这样统一的字节顺序。因如果您在阅读过程中发现疏漏和错误,请您尽快和编者取得联系 中国科学技术大学 多媒体技术与网络通信实验室(copyright 1999)此,在本实验的例子程序中,在 bind()函数、connect()函数等涉及 struct sockaddr_in 地址数据的场合,都可以
48、看到以上转换函数的使用,以加强源程序的可移植性。主机名字/地址数据查询 为配合 DNS 的使用,尽量方便 Internet 主机名字的记忆,避免使用烦琐的数字式 IP 地址,Socket 函数库提供了方便的主机名字查询函数。struct hostent struct hostent 是一个关于主机地址信息的数据结构,其中包含从 DNS 服务器得到的比较全面的主机信息。gethostbyname()和 gethostbyaddr()都返回这样的数据结构。实际使用时,可用此结构中的地址信息来设置 bind()和 connect()函数参数中的 struct sockaddr_in 中的地址,以支持
49、 DNS 名字的使用。参见本实验的具体示例。#include struct hostent char*h_name;/*official name of host*/char*h_aliases;/*alias list*/int h_addrtype;/*host address type*/int h_length;/*length of address*/char*h_addr_list;/*list of addresses from name server*/;#define h_addr h_addr_list0 /*address,for backward compatibility*/这其中最常用的是 h_addr,即主机的缺省地址(因为该主机名字可能对应多个地址)。gethostbyname#include struct hostent*gethostbyname(char*name);根据 DNS 名字,查找主机地址信息。name 可以是 DNS 名字,如”,也可以是 IP 地址串:”202.38.64.2”。gethostbyaddr#include struct hostent*gethostbyaddr(char*addr;int len,int type);根据IP地址查找主机地址信息。addr是整数格式的IP地址指针,如:unsigned char
限制150内