网络socket编程指南 .pdf

上传人:H****o 文档编号:39719780 上传时间:2022-09-07 格式:PDF 页数:37 大小:769.06KB
返回 下载 相关 举报
网络socket编程指南 .pdf_第1页
第1页 / 共37页
网络socket编程指南 .pdf_第2页
第2页 / 共37页
点击查看更多>>
资源描述

《网络socket编程指南 .pdf》由会员分享,可在线阅读,更多相关《网络socket编程指南 .pdf(37页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、网络 socket编程指南介绍Socket 编程让你沮丧吗?从man pages 中很难得到有用的信息吗?你想跟上时代去编Internet 相关的程序,但是为你在调用connect()前的 bind()的结构而不知所措?等等如果你了解C 语言并想穿过网络编程的沼泽,如果你曾经像我一样为了调试到处寻找相关资料而不能解决问题,希望这里对你有一定的帮助。读者对象这个文档是一个指南,而不是参考书。如果你刚开始socket 编程并想找一本入门书,那么你是我的读者。但这不是一本完全的 socket 编程书。平台和编译器这篇文档中的大多数代码都在Linux 平台 PC 上用 GNU 的 gcc 成功编译过。

2、而且它们在HPUX 平台上用gcc 也成功编译过。但是注意,并不是每个代码片段都独立测试过。先来点让你晕的吧,呵呵,坚持下去你就会柳暗花明。客户机/服务器模式在 TCP/IP 网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Server model)。该模式的建立基于以下两点:1、非对等作用;2、通信完全是异步的。客户机/服务器模式在操作过程中采取的是主动请示方式:首先服务器方要先启动,并根据请示提供相应服务:(过程如下)1、打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。2、等待客户请求到达该端口。3、接收到重复服务请求,处理该请求并发送应答信号。

3、4、返回第二步,等待另一客户请求5、关闭服务器。客户方:1、打开一通信通道,并连接到服务器所在主机的特定端口。2、向服务器发送服务请求报文,等待并接收应答;继续提出请求3、请求结束后关闭通信通道并终止。典型过程图1 面向连接的套接字的系统调用时序图名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 37 页 -2 无连接协议的套接字调用时序图3 面向连接的应用程序流程图注意,客户-服务器之间可以使用SOCK_STREAM、SOCK_DGRAM 或者其它(只要它们采用相同的)。一些很好的客户-服务器的例子有telnet/telnetd、ftp/ftpd 和 bootp/bootpd。每

4、次你使用ftp 的时候,在远端都有一个ftpd 为你服务。一般,在服务端只有一个服务器,它采用fork()来处理多个客户的连接。基本的程序是:服务器等待一个连接,接受(accept()连接,然后fork()一个子进程处理它。这是下面会具体讲到的。目录:1)什么是套接字?2)网络理论3)结构体4)本机转换5)IP 地址和如何处理它们6)socket()函数7)bind()函数8)connect()函数9)listen()函数10)accept()函数11)send()和 recv()函数名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 37 页 -12)sendto()和 recvf

5、rom()函数13)close()和 shutdown()函数14)getpeername()函数15)gethostname()函数16)域名服务(DNS)17)简单的服务器18)简单的客户端19)数据报套接字Socket 20)阻塞21)select()-多路同步 I/O 22)参考资料1、什么是socket?什么是套接字?你经常听到人们谈论着socket,或许你还不知道它的确切含义。现在让我告诉你:它是使用标准Unix 文件描述符(file descriptor)和其它程序通讯的方式。你也许听到一些Unix 高手(hacker)这样说过:呀,Unix 中的一切就是文件!那个家伙也许正在说

6、到一个事实:Unix 程序在执行任何形式的I/O 的时候,程序是在读或者写一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数。但是(注意后面的话),这个文件可能是一个网络连接,FIFO,管道,终端,磁盘上的文件或者什么其它的东西。Unix 中所有的东西就是文件!所以,你想和Internet 上别的程序通讯的时候,你将要使用到文件描述符。你必须理解刚才的话。现在你脑海中或许冒出这样的念头:那么我从哪里得到网络通讯的文件描述符呢?,这个问题无论如何我都要回答:你利用系统调用socket(),它返回套接字描述符(socket descriptor),然后你再通过它来进行send()和 r

7、ecv()调用。但是.,你可能有很大的疑惑,如果它是个文件描述符,那么为什么不用一般调用read()和 write()来进行套接字通讯?简单的答案是:你可以使用!。详细的答案是:你可以,但是使用send()和 recv()让你更好的控制数据传输。存在这样一个情况:在我们的世界上,有很多种套接字。有DARPA Internet 地址(Internet 套接字),本地节点的路径名(Unix 套接字),CCITT X.25 地址(你可以将 X.25 套接字完全忽略)。也许在你的Unix 机器上还有其它的。1)套接字编程原理一个完整的网间通信进程需要由两个进程组成,并且只能用同一种高层协议。也就是说,

8、不可能通信的一端用TCP,而另一端用 UDP。一个完整的网络信需要一个五元组来标识:协议、本地地址、本地端口号、远端地址、远端端口号。1.1)Client/server 通信模型在客户/服务器模式中我们将请求服务的一方称为客户(client),将提供某种服务的一方称为服务器(server)。一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“惊醒”并且为客户提供服务对客户的请求作出适当的反应。虽然基于连接的服务是设计客户机/服务器应用程序时的标准,但有些服务也是可以通过无连接的接口提供的。客

9、户机/服务器的请求/响应过程示意图如下所示。图 1 客户/服务器通信模型通过上面的分析,我们不难理解一个一个完整的网络应用程序包括客户端和服务器两个部分。客户与服务器进程的作用是非对称的,因此编码不同。服务进程一般是等待客户请求而启动的,只要系统运行,该服务进程一直存在,直到终止或强迫终止。1.2)Windows Sockets规范Windows Sockets 规范是 90 年代初 Microsoft 公司联合其他几家大公司共同制定的一套在Windows 下的二进制兼容网络编程接口规范。它以U.C.Berkeley 大学 BSD UNIX中流行的 Socket 接口为基础,主要在其上扩充了一

10、组针对Windows 的扩展库函数,增名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 37 页 -加了符合 Windows 消息驱动特性的网络事件异步选择机制,以使程序员能够充分利用Windows 消息驱动机制进行编程。Windows Sockets 的用途是将基础网络抽象出来,这样,您不必对网络非常了解,并且您的应用程序可在任何支持套接字的网络上运行。它为应用程序开发者定义了一套简单统一的API,并让各家网络软件供应商共同遵守。Windows Sockets 规范从 90 年代初的 1.0 版本开始,经过不断的完善和发展,目前已经有了Windows Sockets 2 版本。值

11、得注意的是,Microsoft 的 MFC 库现在只支持Windows Sockets 1 版本,不支持Windows Sockets 2 版本。MFC 提供了两个类用以封装Windows Sockets API。一个是 CAsyncSocket 类,它主要是提供给那些具有一定网络编程经验,希望同时拥有Socket API 编程的灵活性和类库编程便利性的开发者的。另一个是CSocket 类,它由 CAsyncSocket 类派生,它具有更高的抽象化,致力于简化网络编程所需的操作。1.3)套接字1.3.1)套接字定义套接字是一个通信终结点,它是Sockets 应用程序用来在网络上发送或接收数据包

12、的对象。套接字具有类型,与正在运行的进程相关联,并且可以有名称。目前,套接字一般只与使用网际协议组的同一 通信域 中的其他套接字交换数据。使用套接字的应用程序间通信模型如图2 所示。应用程序 A套接字 A应用程序 B套接字 B图2 套接字通信模型1.3.2)分类可用的套接字类型有以下两种:流式套接字、数据报套接字什么意思?有两种类型的Internet 套接字?是的。不,我在撒谎。其实还有很多,但是我可不想吓着你。我们这里只讲两种。除了这些,我打算另外介绍的Raw Sockets 也是非常强大的,很值得查阅。那么这两种类型是什么呢?一种是Stream Sockets(流格式),另外一种是 Dat

13、agram Sockets(数据包格式)。我们以后谈到它们的时候也会用到SOCK_STREAM 和 SOCK_DGRAM。数据报套接字有时也叫 无连接套接字(如果你确实要连接的时候可以用connect()。)流式套接字是可靠的双向通讯的数据流。如果你向套接字按顺序输出1,2,那么它们将按顺序1,2 到达另一边。它们是无错误的传递的,有自己的错误控制,在此不讨论。有什么在使用流式套接字?你可能听说过telnet,不是吗?它就使用流式套接字。你需要你所输入的字符按顺序到达,不是吗?同样,WWW 浏览器使用的HTTP 协议也使用它们来下载页面。实际上,当你通过端口80 telnet 到一个WWW 站

14、点,然后输入GET pagename 的时候,你也可以得到HTML 的内容。为什么流式套接字可以达到高质量的数据传输?这是因为它使用了 传输控制协议(The Transmission Control Protocol),也叫 TCP (请参考RFC-793 获得详细资料。)TCP 控制你的数据按顺序到达并且没有错误。你也许听到TCP 是因为听到过TCP/IP。这里的IP 是指 Internet 协议(请参考RFC-791。)IP 只是处理Internet 路由而已。那么数据报套接字呢?为什么它叫无连接呢?为什么它是不可靠的呢?有这样的一些事实:如果你发送一个数据报,它可能会到达,它可能次序颠倒

15、了。如果它到达,那么在这个包的内部是无错误的。数据报也使用IP 作路由,但是它不使用TCP。它使用 用户数据报协议(User Datagram Protocol),也叫 UDP (请参考RFC-768。)为什么它们是无连接的呢?主要是因为它并不象流式套接字那样维持一个连接。你只要建立一个包,构造一个有目标信息的IP 头,然后发出去。无需连接。它们通常使用于传输包-包信息。简单的应用程序有:tftp,bootp 等等。你也许会想:假如数据丢失了这些程序如何正常工作?我的朋友,每个程序在UDP 上有自己的协议。例如,tftp 协议每发出的一个被接受到包,收到者必须发回一个包来说 我收到了!(一个

16、命令正确应答 也叫ACK 包)。如果在一定时间内(例如 5 秒),发送方没有收到应答,它将重新发送,直到得到ACK。这一ACK过程在实现SOCK_DGRAM 应用程序的时候非常重要。1.3.2.1)流式套接字流式套接字提供没有记录边界的数据流,即字节流。字节流能确保以正确的顺序无重复地被送达。名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 37 页 -创建并初始化套接字监听来自客户机的请求接受连接请求接收数据发送数据关闭连接创建并初始化套接字请求建立连接发送数据接收数据关闭连接服务器客户机图 3 流式套接字(有连接通信)编程1.3.2.2)数据报套接字数据报套接字支持面向记录的数

17、据流,但不能确保能被送达,也无法确保按照发送顺序或不重复。创建并初始化套接字监听来自客户机的请求进行处理发送结果给客户端关闭连接创建并初始化套接字向服务器发出请求接收结果关闭连接服务器客户机图 4 数据报套接字(无连接通信)编程 有序 指数据包按发送的顺序送达。不重复 指一个特定的数据包只能获取一次。这两种套接字都是双向的,是可以同时在两个方向上(全双工)进行通信的数据流。注意在某些网络协议下(如XNS),流可以面向记录,即作为记录流而非字节流。但在更常用的TCP/IP 协议下,流为字节流。Windows Sockets 提供与基础协议无关的抽象化级别。1.3.3)套接字的作用套接字的作用非常

18、大,至少在下面三种通信上下文中如此:客户端/服务器模型。对等网络方案,如聊天应用程序。通过让接收应用程序将消息解释为函数调用来进行远程过程调用(RPC)。1.3.4)端口与地址在网络上,一个套接字的标识主要借助于地址和端口来描述。套接字的地址指该套接字所在计算机的网络地址,可以为域名或IP 地址的形式。通常,创建套接字时不必指明网络地址,只有在拥有多个网络地址的机器时,才需要显式指定一个网络地址。同一机器上可以运行多个网络应用程序,每个应用程序都有自己的套接字用以进行网络通信,此时如果只有地址标识套接字,则当一个通信包到达机器时,将无法确定究竟是哪个应用程序的套接字需要接收此信息。由此增加了端

19、口的概念,以协助区分同一机器上不同应用程序的套接字。端口用于标识进程,同一机器上不同的网络应用程序各有不同的端口,这样,通过“网络地址+端口号”的标识方法,便唯一标识了机器上的应用程序了。某些端口是专门为公共服务保留的(Ftp:21,http:80),除非程序是要提供这些服务,否则应尽量避免使用这些端口。一般来说,端口 1024 以前的端口号都是系统保留的或是作为公共服务的,应尽量选择大于1024 的端口号,以避免冲突。1.3.5)套接口属性套接口有一系列的属性用于标识套接口的状态等信息,它们的属性如表1 所示。表 1 套接口属性选项类型含义缺省值SO_ACCEPTCON BOOL 套接口正在

20、监听FALSE SO_BROADCAST BOOL 套接口设置为可发送广播数据FALSE 名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 37 页 -SO_DEBUG BOOL 允许 Debug FALSE SO_DONTLINGER BOOL 是否禁止 SO_LINGER 选项TRUE SO_DONTROUTE BOOL 路由被禁止FALSE SO_ERROR int 得到并且清除错误状态0 SO_KEEPALIVE BOOL 活跃信息正在被发送FALSE SO_LINGER struct 返回目前的linger 信息1_onoff SO_OOBINLINE BOOL 带外数据

21、正在普通数据流中被接收FALSE SO_RCVBUF int 接收缓冲区大小与具体实现有关SO_REUSEADDR BOOL 该套接口捆绑的地址可否被他人使用FALSE SO_SNDBUF int 发送缓冲区大小与具体实现有关SO_TYPE int 套接口类型与接口类型有关TCP_NODELAY BOOL 禁止采用 Nagle 进行合并传送与具体实现有关可以通过 getsockopt()函数获取套接口的属性,也可以通过setsockopt()函数设置套接口的属性。3、网络理论既然我刚才提到了协议层,那么现在是讨论网络究竟如何工作和一些关于SOCK_DGRAM 包是如何建立的例子。当然,你也可以

22、跳过这一段,如果你认为已经熟悉的话。现在是学习数据封装(Data Encapsulation)的时候了!它非常非常重要。图 1:数据封装它重要性重要到你在网络课程学习中无论如何也得也得掌握它。主要的内容是:一个包,先是被第一个协议(在这里是 TFTP)在它的报头(也许是报尾)包装(封装),然后,整个数据(包括TFTP 头)被另外一个协议(在这里是UDP)封装,然后下一个(IP),一直重复下去,直到硬件(物理)层(这里是以太网)。当另外一台机器接收到包,硬件先剥去以太网头,内核剥去IP 和 UDP 头,TFTP 程序再剥去TFTP 头,最后得到数据。现在我们终于讲到声名狼藉的网络分层模型(Lay

23、ered Network Model)。这种网络模型在描述网络系统上相对其它模型有很多优点。例如,你可以写一个套接字程序而不用关心数据的物理传输(串行口,以太网,连接单元接口(AUI)还是其它介质),因为底层的程序会为你处理它们。实际的网络硬件和拓扑对于程序员来说是透明的。不说其它废话了,我现在列出整个层次模型。如果你要参加网络考试,可一定要记住:应用层(Application)表示层(Presentation)会话层(Session)传输层(Transport)网络层(Network)数据链路层(Data Link)物理层(Physical)物理层是硬件(串口,以太网等等)。应用层是和硬件层

24、相隔最远的-它 是用户和网络交互的地方。名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 37 页 -这个模型如此通用,如果你想,你可以把它作为修车指南。把它对应到 Unix,结果是:应用层(Application Layer)(telnet,ftp,等等)传输层(Host-to-Host Transport Layer)(TCP,UDP)Internet 层(Internet Layer)(IP 和路由)网络访问层(Network Access Layer)(网络层,数据链路层和物理层)现在,你可能看到这些层次如何协调来封装原始的数据了。看看建立一个简单的数据包有多少工作?哎呀,

25、你将不得不使用cat 来建立数据包头!这仅仅是个玩笑。对于流式套接字你要作的是send()发 送数据。对于数据报式套接字,你按照你选择的方式封装数据然后使用sendto()。内核将为你建立传输层和Internet 层,硬件完成网络访问层。这就是现代科技。现在结束我们的网络理论速成班。哦,忘记告诉你关于路由的事情了。但是我不准备谈它,如果你真的关心,那么参考IP RFC。4、结构体4.1 基本的 Windows Sockets API 编程需要在程序中添加下面的包含语句:#include 使用 vc+编译时需添加编译链接依赖项ws2_32.lib 库协议寻址在 winsock 中,应用程序通过s

26、ockaddr_in 结构来指定IP 地址和服务端口信息sockaddr_in internetAddr;int nPortID=5320;internetAddr.sin_family=AF_INET;internet.sin_addr.s_addr=inet_addr(202.202.42.88);/INADDR_ANY internet.sin_port=htons(nPortID);ip 地址不容易记忆,还提供了许多地址和名称解析函数如gethostbyname,gethostbyaddr等。4.2 常用函数1)WSAStartup 调用 windows Socket DLL 函数原型

27、int WSAStartup(WORD wVersionRequested,/应用程序要求的sockets 版本LPWSADATA lpWSAData/指向数据结构WSDATA 的指针,/得到 windows Socket 的具体信息);WSDA TA 定义如下:typedef struct WSAData WORD wVersion;WORD wHighVersion;#ifdef _WIN64 unsigned short iMaxSockets;unsigned short iMaxUdpDg;char FAR*lpVendorInfo;char szDescriptionWSADESC

28、RIPTION_LEN+1;char szSystemStatusWSASYS_STATUS_LEN+1;#else char szDescriptionWSADESCRIPTION_LEN+1;char szSystemStatusWSASYS_STATUS_LEN+1;unsigned short iMaxSockets;unsigned short iMaxUdpDg;char FAR*lpVendorInfo;#endif WSADATA,FAR*LPWSADATA;2)WSACleanup 结束对 Windows Sockets DLL 的调用函数原型:int WSACleanup(

29、void);3)socket 用于建立 Sockets。名师资料总结-精品资料欢迎下载-名师精心整理-第 7 页,共 37 页 -函数原型:SOCKET socket(int af,/地址族,一般是AF_INET int type,/socket 类型,SOCK_STREAM 或 SOCK_DGRAM int protocol/协议类型,通常取值0);4)closesocket 关闭套接字函数原型:int closesocket(SOCKET s/要关闭的套接字);5)bind 将一个本地地址和一个SOCKET 描述字连接起来函数原型:int bind(SOCKET s,/要绑定的套接字con

30、st struct sockaddr FAR*name,/指向 SOCKADDR 结构的地址int namelen/地址结构的sizeof)Tcp/ip SOCKADDR结构struct sockaddr unsigned short sa_family;char sa_data4;struct sockaddr_in short sin_family;unsigned short sin_port;struct in_addr sin_addr;char sin_zero8;6)listen 设定 socket 为监听状态函数原型:int listen(SOCKET s,/进行监听的sock

31、et int backlog/客户端可以连接的请求个数);7)accept 接受一个 socket 的连接请求,同时返回一个新的socket,新的 socket用来在服务器与客户端之间传递和接收信息。函数原型:SOCKET accept(SOCKET s,/处于监听状态的socket struct sockaddr FAR*addr,/将要接受地址的sockaddr 指针int FAR*addrlen/地址的长度);8)connect 连接客户端的socket到指定的网络服务器。连接成功后,客户端用此socket与服务器通信。函数原型:int connect(SOCKET s,/将要连接的so

32、cket const struct sockaddr FAR*name,/目标 socket地址int namelen/地址结构 sizeof);9)recv 用于接收已经建立连接的socket 数据信息函数原型:int recv(SOCKET s,char FAR*buf,/接收数据缓冲区int len,/缓冲区长度int flags/有 MSG_PEEK 和 MSG_OOB);名师资料总结-精品资料欢迎下载-名师精心整理-第 8 页,共 37 页 -返回值:接收到的字节数10)send 对已经建立连接的socket发送数据信息函数原型:int send(SOCKET s,char FAR*

33、buf,/发送数据缓冲区int len,/缓冲区长度int flags/有 MSG_PEEK 和 MSG_OOB);返回值:发送的字节数11)WSAAsyncSelect 要求 socket在有事件发生时通知使用者,本函数将套接口设置成为非阻塞方式。函数原型:int WSAAsyncSelect(SOCKET s,HWND hWnd,/接收网络事件的窗口句柄unsigned int wMsg,/发送给窗口的网络事件消息long lEvent/网络消息);12)sendto 向目标地址发送数据信息int sendto(SOCKET s,const char FAR*buf,int len,int

34、 flags,const struct sockaddr FAR*to,int tolen);13)recvfrom 接收目标地址传来的数据信息int recvfrom(IN SOCKET s,OUT char FAR*buf,IN int len,IN int flags,OUT struct sockaddr FAR*from,IN OUT int FAR*fromlen);终于谈到编程了。在这,将谈到被套接字用到的各种数据类型。因为它们中的一些内容很重要了。首先是简单的一个:socket描述符。它是下面的类型:int。仅仅是一个常见的int。从现在起,事情变得不可思议了,而你所需做的就是

35、继续看下去。注意这样的事实:有两种字节排列顺序:重要的字节(有时叫octet,即八位位组)在前面,或者不重要的字节在前面。前一种叫 网络字节顺序(Network Byte Order)。有些机器在内部是按照这个顺序储存数据,而另外一些则不然。当我说某数据必须按照NBO 顺序,那么你要调用函数(例如htons()来将它从本机字节顺序(Host Byte Order)转换过来。如果我没有提到 NBO,那么就让它保持本机字节顺序。我的第一个结构(在这个技术手册TM 中)-struct sockaddr.。这个结构为许多类型的套接字储存套接字地址信息:struct sockaddr unsigned

36、short sa_family;/*地址家族,AF_xxx*/char sa_data14;/*14 字节协议地址*/;sa_family 能够是各种各样的类型,但是在这篇文章中都是AF_INET。sa_data 包含套接字中的目标地址和端口信息。这好像有点 不明智。为了处理struct sockaddr,程序员创造了一个并列的结构:struct sockaddr_in(in 代表 Internet。)struct sockaddr_in short int sin_family;/*通信类型*/名师资料总结-精品资料欢迎下载-名师精心整理-第 9 页,共 37 页 -unsigned sho

37、rt int sin_port;/*端口*/struct in_addr sin_addr;/*Internet 地址*/unsigned char sin_zero8;/*与 sockaddr 结构的长度相同*/;用这个数据结构可以轻松处理套接字地址的基本元素。注意sin_zero(它被加入到这个结构,并且长度和struct sockaddr 一样)应该使用函数bzero()或 memset()来全部置零。同时,这一重要的字节,一个指向sockaddr_in 结构体的指针也可以被指向结构体sockaddr 并且代替它。这样的话即使socket()想要的是struct sockaddr*,你仍

38、然可以使用struct sockaddr_in,并且在最后转换。同时,注意sin_family 和 struct sockaddr 中的sa_family 一致并能够设置为AF_INET。最后,sin_port 和 sin_addr 必须是网络字节顺序(Network Byte Order)!你也许会反对道:但是,怎么让整个数据结构struct in_addr sin_addr 按照网络字节顺序呢?要知道这个问题的答案,我们就要仔细的看一看这个数据结构:struct in_addr,有这样一个联合(unions):/*Internet 地址(一个与历史有关的结构)*/struct in_add

39、r unsigned long s_addr;它 曾 经 是 个 最 坏 的 联 合,但 是 现 在 那 些 日 子 过 去 了。如 果 你 声 明ina 是 数 据 结 构struct sockaddr_in 的 实 例,那 么ina.sin_addr.s_addr 就储存 4 字节的IP 地址(使用网络字节顺序)。如果你不幸的系统使用的还是恐怖的联合struct in_addr,你还是可以放心4 字节的IP 地址并且和上面我说的一样(这是因为使用了#define。)4.3、本机转换我们现在到了新的章节。我们曾经讲了很多网络到本机字节顺序的转换,现在可以实践了!你能够转换两种类型:short

40、(两个字节)和 long(四个字节)。这个函数对于变量类型unsigned 也适用。假设你想将short 从本机字节顺序转换为网络字节顺序。用h 表示本机(host),接着是to,然后用n 表 示 网络(network),最后用s 表示short:h-to-n-s,或者 htons()(Host to Network Short)。太简单了.如果不是太傻的话,你一定想到了由n,h,s,和l形成的正确组合,例如这里肯定没有stolh()(Short to Long Host)函数,不仅在这里没有,所有场合都没有。但是这里有:htons()-Host to Network Short htonl(

41、)-Host to Network Long ntohs()-Network to Host Short ntohl()-Network to Host Long 现在,你可能想你已经知道它们了。你也可能想:如果我想改变char 的顺序要怎么办呢?但是你也许马上就想到,用不着考虑的。你也许会想到:我的68000 机器已经使用了网络字节顺序,我没有必要去调用htonl()转换IP 地址。你可能是对的,但是当你移植你的程序到别的机器上的时候,你的程序将失败。可移植性!这里是Unix 世界!记住:在你将数据放到网络上的时候,确信它们是网络字节顺序的。最后一点:为什么在数据结构struct socka

42、ddr_in 中,sin_addr 和 sin_port 需要转换为网络字节顺序,而 sin_family 需不需要呢?答案是:sin_addr 和 sin_port 分别封装在包的IP 和 UDP 层。因此,它们必须要是网络字节顺序。但是sin_family 域只是被内核(kernel)使用来决定在数据结构中包含什么类型的地址,所以它必须是本机字节顺序。同时,sin_family 没有发送到网络上,它们可以是本机字节顺序。4.4、IP 地址和如何处理它们现在我们很幸运,因为我们有很多的函数来方便地操作IP 地址。没有必要用手工计算它们,也没有必要用h_name);printf(IP Addr

43、ess:%sn,inet_ntoa(*(struct in_addr*)h-h_addr);return 0;在使用gethostbyname()的时候,你不能用perror()打印错误信息(因为errno 没有使用),你应该调用herror()。相当简单,你只是传递一个保存机器名的字符串(例如 whitehouse.gov)给 gethostbyname(),然后从返回的数据结构struct hostent 中获取信息。唯一也许让人不解的是输出IP 地址信息。h-h_addr 是一个char*,但是inet_ntoa()需要的是struct in_addr。因此,我转换h-h_addr 成

44、struct in_addr*,然后得到数据。5、简单的服务器这个服务器所做的全部工作是在流式连接上发送字符串Hello,World!n。你要测试这个程序的话,可以在一台机器上运行该程序,然后在另外一机器上登陆:$telnet remotehostname 3490 remotehostname 是该程序运行的机器的名字。服务器代码:#include stdio.h#include stdlib.h#include errno.h#include string.h#include sys/types.h#include netinet/in.h#include sys/socket.h#inc

45、lude sys/wait.h#define MYPORT 3490/*定义用户连接端口*/#define BACKLOG 10/*多少等待连接控制*/main()int sockfd,new_fd;/*listen on sock_fd,new connection on new_fd*/struct sockaddr_in my_addr;/*my address information*/struct sockaddr_in their_addr;/*connectors address information*/int sin_size;if(sockfd=socket(AF_INET

46、,SOCK_STREAM,0)=-1)perror(socket);exit(1);名师资料总结-精品资料欢迎下载-名师精心整理-第 16 页,共 37 页 -my_addr.sin_family=AF_INET;/*host byte order*/my_addr.sin_port=htons(MYPORT);/*short,network byte order*/my_addr.sin_addr.s_addr=INADDR_ANY;/*auto-fill with my IP*/bzero(&(my_addr.sin_zero),;/*zero the rest of the struct

47、*/if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr)=-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

48、;printf(server:got connection from%sn,inet_ntoa(their_addr.sin_addr);if(!fork()/*this is the child process*/if(send(new_fd,Hello,world!n,14,0)=-1)perror(send);close(new_fd);exit(0);close(new_fd);/*parent doesnt need this*/while(waitpid(-1,NULL,WNOHANG)0);/*clean up child processes*/如果你很挑剔的话,一定不满意我所有

49、的代码都在一个很大的main()函数中。如果你不喜欢,可以划分得更细点。你也可以用我们下一章中的程序得到服务器端发送的字符串。6、简单的客户程序这个程序比服务器还简单。这个程序的所有工作是通过3490 端口连接到命令行中指定的主机,然后得到服务器发送的字符串。客户代码:#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 PORT 3490/

50、*客户机连接远程主机的端口*/#define MAXDATASIZE 100/*每次可以接收的最大字节*/int main(int argc,char*argv)名师资料总结-精品资料欢迎下载-名师精心整理-第 17 页,共 37 页 -int sockfd,numbytes;char bufMAXDA TASIZE;struct hostent*he;struct sockaddr_in their_addr;/*connectors address information*/if(argc!=2)fprintf(stderr,usage:client hostnamen);exit(1);

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 技术总结

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com