Internet Sockets 网络编程指南Word格式文档下载.docx

上传人:b****6 文档编号:21556741 上传时间:2023-01-31 格式:DOCX 页数:30 大小:43.29KB
下载 相关 举报
Internet Sockets 网络编程指南Word格式文档下载.docx_第1页
第1页 / 共30页
Internet Sockets 网络编程指南Word格式文档下载.docx_第2页
第2页 / 共30页
Internet Sockets 网络编程指南Word格式文档下载.docx_第3页
第3页 / 共30页
Internet Sockets 网络编程指南Word格式文档下载.docx_第4页
第4页 / 共30页
Internet Sockets 网络编程指南Word格式文档下载.docx_第5页
第5页 / 共30页
点击查看更多>>
下载资源
资源描述

Internet Sockets 网络编程指南Word格式文档下载.docx

《Internet Sockets 网络编程指南Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《Internet Sockets 网络编程指南Word格式文档下载.docx(30页珍藏版)》请在冰豆网上搜索。

Internet Sockets 网络编程指南Word格式文档下载.docx

∙数据报Socket

∙阻塞

∙select()--多路同步I/O,酷!

∙参考资料

∙DisclaimerandCallforHelp

什么是socket?

你始终听到人们谈论着"

socket"

,而你不知道他的确切含义。

那么,现在我告诉你:

他是使用Unix文件描述符(fieldescriptor)和其他程序通讯的方式。

什么?

Ok--你也许听到一些Unix高手(hacker)这样说:

“呀,Unix中所有的东西就是文件!

”那个家伙也许正在说到一个事实:

Unix程序在执行任何形式的I/O的时候,程序是在读或者写一个文件描述符。

一个文件描述符只是一个和打开的文件相关联的整数。

但是(注意后面的话),这个文件可能是一个网络连接,FIFO,管道,终端,磁盘上的文件或者什么其他的东西。

Unix中所有的东西是文件!

因此,你想和Internet上别的程序通讯的时候,你将要通过文件描述符。

最好相信刚才的话。

现在你脑海中或许冒出这样的念头:

“那么我从哪里得到网络通讯的文件描述符呢,聪明人?

”无论如何,我要回答这个问题:

你利用系统调用socket()。

他返回套接口描述符(socketdescriptor),然后你再通过他来调用send()和recv()。

“但是...”,你可能现在叫起来,“如果他是个文件描述符,那么为什么不用一般的调用read()和write()来通过套接口通讯?

”简单的答案是:

“你可以使用一般的函数!

”。

详细的答案是:

“你可以,但是使用send()和recv()让你更好的控制数据传输。

有这样一个事实:

在我们的世界上,有很多种套接口。

有DARPAInternet地址(Internet套接口),本地节点的路径名(Unix套接口),CCITTX.25地址(你可以完全忽略X.25套接口)。

也许在你的Unix机器上还有其他的。

我们在这里只讲第一种:

Internet套接口。

Internet套接口的两种类型

什么意思?

有两种Internet套接口?

是的。

不,我在撒谎。

其实还有很多,但是我可不想吓着你。

我们这里只讲两种。

Exceptforthissentence,whereI'

mgoingtotellyouthat"

RawSockets"

arealsoverypowerfulandyoushouldlookthemup.

好了,好了。

那两种类型是什么呢?

一种是"

StreamSockets"

,另外一种是"

DatagramSockets"

我们以后谈到他们的时候也会用到"

SOCK_STREAM"

和"

SOCK_DGRAM"

数据报套接口有时也叫“无连接套接口”(如果你确实要连接的时候用connect()。

流式套接口是可靠的双向通讯的数据流。

如果你向套接口安顺序输出“1,2”,那么他们将安顺序“1,2”到达另一边。

他们也是无错误的传递的,有自己的错误控制。

有谁在使用流式套接口?

你可能听说过telnet,不是吗?

他就使用流式套接口。

你需要你所输入的字符按顺序到达,不是吗?

同样,WWW浏览器使用的HTTP协议也使用他们。

实际上,当你通过端口80telnet到一个WWW站点,然后输入“GETpagename”的时候,你也可以得到HTML的内容。

为什么流式套接口可以达到高质量的数据传输?

他使用了“传输控制协议(TheTransmissionControlProtocol)”,也叫“TCP”(请参考RFC-793获得详细资料。

)TCP控制你的数据按顺序到达并且没有错误。

你也许听到“TCP”是因为听到过“TCP/IP”。

这里的IP是指“Internet协议”(请参考RFC-791.)IP只是处理Internet路由而已。

那么数据报套接口呢?

为什么他叫无连接呢?

为什么他是不可靠的呢?

恩,有这样的事实:

如果你发送一个数据报,他可能到达,他可能次序颠倒了。

如果他到达,那么在这个包的内部是无错误的。

数据报也使用IP作路由,但是他不选择TCP。

他使用“用户数据报协议(UserDatagramProtocol)”,也叫“UDP”(请参考RFC-768.)

为什么他们是无连接的呢?

主要原因是因为他并不象流式套接口那样维持一个连接。

你只要建立一个包,在目标信息中构造一个IP头,然后发出去。

不需要连接。

应用程序有:

tftp,bootp等等。

“够了!

”你也许会想,“如果数据丢失了这些程序如何正常工作?

”我的朋友,每个程序在UDP上有自己的协议。

例如,tftp协议每发出一个包,收到者发回一个包来说“我收到了!

”(一个“命令正确应答”也叫“ACK”包)。

如果在一定时间内(例如5秒),发送方没有收到应答,他将重新发送,直到得到ACK。

这一点在实现SOCK_DGRAM应用程序的时候非常重要。

网络理论

既然我刚才提到了协议层,那么现在是讨论网络究竟如何工作和演示SOCK_DGRAM的工作。

当然,你也可以跳过这一段,如果你认为已经熟悉的话。

朋友们,现在是学习数据封装(DataEncapsulation)的时候了!

这非常非常重要。

It'

ssoimportantthatyoumightjustlearnaboutitifyoutakethenetworkscoursehereatChicoState;

-).主要的内容是:

一个包,先是被第一个协议(在这里是TFTP)包装(“封装”),然后,整个数据(包括TFTP头)被另外一个协议(在这里是UDP)封装,然后下一个(IP),一直重复下去,直到硬件(物理)层(Ethernet)。

当另外一台机器接收到包,硬件先剥去Ethernet头,内核剥去IP和UDP头,TFTP程序再剥去TFTP头,最后得到数据。

现在我们终于讲到臭名远播的网络分层模型(LayeredNetworkModel)。

这种网络模型在描述网络系统上相对其他模型有很多优点。

例如,你可以写一个套接口程序而不用关心数据的物理传输(串行口,以太网,连接单元接口(AUI)还是其他介质。

因为底层的程序为你处理他们。

实际的网络硬件和拓扑对于程序员来说是透明的。

不说其他废话了,我现在列出整个层次模型。

如果你要参加网络考试,可一定要记住:

∙应用层(Application)

∙表示层(Presentation)

∙会话层(Session)

∙传输层(Transport)

∙网络层(Network)

∙数据链路层(DataLink)

∙物理层(Physical)

物理层是硬件(串口,以太网等等)。

应用层是和硬件层相隔最远的--他是用户和网络交互的地方。

这个模型如此通用,如果你想,你可以把他作为修车指南。

把他应用到Unix,结果是:

∙应用层(ApplicationLayer)(telnet,ftp,等等)

∙传输层(Host-to-HostTransportLayer)(TCP,UDP)

∙Internet层(InternetLayer)(IP和路由)

∙网络访问层(NetworkAccessLayer)(网络层,数据链路层和物理层)

现在,你可能看到这些层次如何协调来封装原始的数据了。

看看建立一个简单的数据包有多少工作?

哎呀,你将不得不使用"

cat"

来完成他们!

简直是笑话。

对于流式套接口你要作的是send()发送数据。

对于数据报式套接口你按照你选择的方式封装数据然后用sendto()。

内核将为你建立传输层和Internet层,硬件完成网络访问层。

这就是现代科技。

现在结束我们的网络理论速成班。

哦,忘记告诉你关于路由的事情了。

但是我不准备谈他。

如果你真的想知道,那么参考IPRFC。

如果你从来不曾了解他,也没有关系,你还活着不是吗。

structs

终于到达这里了,终于谈到编程了。

在这章,我将谈到被套接口用到的各种数据类型。

因为他们中的一些太重要了。

首先是简单的一个:

socketdescriptor。

他是下面的类型:

int

仅仅是一个常见的int。

从现在起,事情变得不可思议了。

请跟我一起忍受苦恼吧。

注意这样的事实:

有两种字节排列顺序:

重要的字节在前面(有时叫"

octet"

),或者不重要的字节在前面。

前一种叫“网络字节顺序(NetworkByteOrder)”。

有些机器在内部是按照这个顺序储存数据,而另外一些则不然。

当我说某数据必须按照NBO顺序,那么你要调用函数(例如htons())来将他从本机字节顺序(HostByteOrder)转换过来。

如果我没有提到NBO,那么就让他是本机字节顺序吧。

我的第一个结构(TM)--structsockaddr.这个数据结构为许多类型的套接口储存套接口地址信息:

structsockaddr{

unsignedshortsa_family;

/*addressfamily,AF_xxx*/

charsa_data[14];

/*14bytesofprotocoladdress*/

};

sa_family能够是各种各样的事情,但是在这篇文章中是"

AF_INET"

sa_data为套接口储存目标地址和端口信息。

看上去很笨拙,不是吗。

为了对付structsockaddr,程序员创造了一个并列的结构:

structsockaddr_in("

in"

代表"

Internet"

.)

structsockaddr_in{

shortintsin_family;

/*Addressfamily*/

unsignedshortintsin_port;

/*Portnumber*/

structin_addrsin_addr;

/*Internetaddress*/

unsignedcharsin_zero[8];

/*Samesizeasstructsockaddr*/

这个数据结构让可以轻松处理套接口地址的基本元素。

注意sin_zero(他被加入到这个结构,并且长度和structsockaddr一样)应该使用函数bzero()或memset()来全部置零。

Also,andthisistheimportantbit,apointertoastructsockaddr_incanbecasttoapointertoastructsockaddrandvice-versa.这样的话即使socket()想要的是structsockaddr*,你仍然可以使用structsockaddr_in,andcastitatthelastminute!

同时,注意sin_family和structsockaddr中的sa_family一致并能够设置为"

最后,sin_port和sin_addr必须是网络字节顺序(NetworkByteOrder)!

你也许会反对道:

"

但是,怎么让整个数据结构structin_addrsin_addr按照网络字节顺序呢?

要知道这个问题的答案,我们就要仔细的看一看这个数据结构:

structin_addr,有这样一个联合(unions):

/*Internetaddress(astructureforhistoricalreasons)*/

structin_addr{

unsignedlongs_addr;

他曾经是个最坏的联合,但是现在那些日子过去了。

如果你声明"

ina"

是数据结构structsockaddr_in的实例,那么"

ina.sin_addr.s_addr"

就储存4字节的IP地址(网络字节顺序)。

如果你不幸的系统使用的还是恐怖的联合structin_addr,你还是可以放心4字节的IP地址是和上面我说的一样(这是因为#define。

ConverttheNatives!

我们现在到达下个章节。

我们曾经讲了很多网络到本机字节顺序,现在是采取行动的时刻了!

你能够转换两种类型:

short(两个字节)和long(四个字节)。

这个函数对于变量类型unsigned也适用。

假设你想将short从本机字节顺序转换为网络字节顺序。

用"

h"

表示"

本机(host)"

,接着是"

to"

,然后用"

n"

网络(network)"

,最后用"

s"

short"

h-to-n-s,或者htons()("

HosttoNetworkShort"

)。

太简单了...

如果不是太傻的话,你一定想到了组合"

,"

,和"

l"

但是这里没有stolh()("

ShorttoLongHost"

)函数,但是这里有:

∙htons()--"

∙htonl()--"

HosttoNetworkLong"

∙ntohs()--"

NetworktoHostShort"

∙ntohl()--"

NetworktoHostLong"

现在,你可能想你已经知道他们了。

你也可能想:

如果我改变char的顺序会怎么样呢?

我的68000机器已经使用了网络字节顺序,我没有必要去调用htonl()转换IP地址。

你可能是对的,但是当你移植你的程序到别的机器上的时候,你的程序将失败。

可移植性!

这里是Unix世界!

记住:

在你将数据放到网络上的时候,确信他们是网络字节顺序。

最后一点:

为什么在数据结构structsockaddr_in中,sin_addr和sin_port需要转换为网络字节顺序,而sin_family不需要呢?

答案是:

sin_addr和sin_port分别封装在包的IP和UDP层。

因此,他们必须要是网络字节顺序。

但是sin_family域只是被内核(kernel)使用来决定在数据结构中包含什么类型的地址,所以他应该是本机字节顺序。

也即sin_family没有发送到网络上,他们可以是本机字节顺序。

IP地址和如何处理他们

现在我们很幸运,因为我们有很多的函数来方便地操作IP地址。

没有必要用手工计算他们,也没有必要用<

<

操作符来操作long。

首先,假设你用structsockaddr_inina,你想将IP地址"

132.241.5.10"

储存到其中。

你要用的函数是inet_addr(),转换numbers-and-dots格式的IP地址到unsignedlong。

这个工作可以这样来做:

ina.sin_addr.s_addr=inet_addr("

);

注意:

inet_addr()返回的地址已经是按照网络字节顺序的,你没有必要再去调用htonl()。

上面的代码可不是很健壮(robust),因为没有错误检查。

inet_addr()在发生错误的时候返回-1。

记得二进制数吗?

在IP地址为255.255.255.255的时候返回的是(unsigned)-1!

这是个广播地址!

记住正确的使用错误检查。

好了,你现在可以转换字符串形式的IP地址为long了。

那么你有一个数据结构structin_addr,该如何按照numbers-and-dots格式打印呢?

在这个时候,也许你要用函数inet_ntoa()("

ntoa"

意思是"

networktoascii"

):

printf("

%s"

inet_ntoa(ina.sin_addr));

他将打印IP地址。

注意的是:

函数inet_ntoa()的参数是structin_addr,而不是long。

同时要注意的是他返回的是一个指向字符的指针。

在inet_ntoa内部的指针静态地储存字符数组,因此每次你调用inet_ntoa()的时候他将覆盖以前的内容。

例如:

char*a1,*a2;

.

a1=inet_ntoa(ina1.sin_addr);

/*thisis198.92.129.1*/

a2=inet_ntoa(ina2.sin_addr);

/*thisis132.241.5.10*/

address1:

%s\n"

a1);

address2:

a2);

运行结果是:

address1:

132.241.5.10

address2:

如果你想保存地址,那么用strcpy()保存到自己的字符数组中。

这就是这章的内容了。

以后,我们将学习转换"

whitehouse.gov"

形式的字符串到正确的IP地址(请看后面的DNS一章。

socket()--得到文件描述符!

我猜我不会再扯远了--我必须讲socket()这个系统调用了。

这里是详细的定义:

#include<

sys/types.h>

sys/socket.h>

intsocket(intdomain,inttype,intprotocol);

但是他们的参数怎么用?

首先,domain应该设置成"

,就象上面的数据结构structsockaddr_in中一样。

然后,参数type告诉内核是SOCK_STREAM类型还是SOCK_DGRAM类型。

最后,把protocol设置为"

0"

(注意:

有很多种domain、type,我不可能一一列出了,请看socket()的manpage。

当然,还有一个"

更好"

的方式去得到protocol。

请看getprotobyname()的manpage。

socket()只是返回你以后在系统调用种可能用到的socket描述符,或者在错误的时候返回-1。

全局变量errno中储存错误值。

(请参考perror()的manpage。

bind()--我在哪个端口?

一旦你得到套接口,你可能要将套接口和机器上的一定的端口关联起来。

(如果你想用listen()来侦听一定端口的数据,这是必要一步--MUD经常告诉你说用命令"

telnetx.y.z6969"

.)如果你只想用connect(),那么这个步骤没有必要。

但是无论如何,请继续读下去。

这里是系统调用bind()的大略:

intbind(intsockfd,structsockaddr*my_addr,intaddrlen);

sockfd是调用socket返回的文件描述符。

my_addr是指向数据结构structsockaddr的指针,他保存你的地址(即端口和IP地址)信息。

addrlen设置为sizeof(structsockaddr)。

简单得很不是吗?

再看看例子:

string.h>

#defineMYPORT3490

main()

{

intsockfd;

structsockaddr_inmy_addr;

sockfd=socket(AF_INET,SOCK_STREAM,0);

/*dosomeerrorchecking!

*/

my_addr.sin_family=AF_INET;

/*hostbyteorder*/

my_addr.sin_port=htons(MYPORT);

/*short,networkbyteorder*/

my_addr.sin_addr.s_addr=inet_addr("

bzero(&

(my_addr.sin_zero),8);

/*zerotherestofthestruct*/

/*don'

tforgetyourerrorcheckingforbind():

bind(sockfd,(structsockaddr*)&

my_addr,sizeof(structsockaddr));

这里也有要注意的几件事情。

my_addr.sin_port是网络字节顺序,my_addr.sin_addr.s_addr也

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 中医中药

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1