蓝森林首页 | 返回主页 | 本站地图 | 站内搜索 | 联系信箱 |
 您目前的位置:首页 > 自由软件 > 技术交流 > 应用编程


    

蓝森林 http://www.lslnet.com 2006年6月6日 10:18


*******网络编程基础( 讨论版V1 )*******

最近这里有关网络编程方面的讨论比较多,我就抛砖引玉,就有关网络编程问题(主要讨论应用层到传输层的接口),给几个讨论的话题,从最基本的开始,每次讨论5个话题,希望各位论坛朋友参与讨论,畅所欲言!

1、套接口是什么?套接口和套节字是什么关系?

2、TCP和UDP有什么区别?

3、IPV4和IPV6由什么区别?如何互操作?

4、TCP的三路握手和四分组连接终止序列的过程分别是什么?

5、在TCP状态转化的过程中,分析如下几种状态:
TIME_WAIT、FIN_WAIT_1、FIN_WAIT_2、CLOSE_WAIT和LAST_WAIT。

*******网络编程基础( 讨论版V1 )*******

补充一点:
各位大虾,讨论的时候请针对上面的问题,使用数字1、2、3、4、5分别对应上述问题分别描述。

*******网络编程基础( 讨论版V1 )*******

好累啊
要真仔细说的话还是真的累
因为知识点很多了

2TCP是有连接的,UDP是无连接的。有连接的话保证数据流准确按序收发,并且在收发错误时会报错

UDP不保证发送的按序,并且不保证能够发送,当然如果网络没有问题的话
那么使用它还是会发送成功的
3 IPV4 和V6区别在于IP地址长度不一样
另外如果在IP层比较的话
那么它们的包长不一样
还有
IPV6分包只能在发送方进行

*******网络编程基础( 讨论版V1 )*******

非常好,斑竹首先说话了!
对于上述讨论点,可以针对某个说说自己的意见。

*******网络编程基础( 讨论版V1 )*******

http://edu.sdinfo.net/74596379271364608/20021106/1097592.shtml

1.
套接字是网络通信的基本构件,它提供了不同主机间进程双向通信的端点.
套接字存在于特定的通信域(即地址族)中,只有隶属于同一地址族的套接字才能建立对话。Linux支持AF_INET(IPv4协议)、AF_INET6(IPv6协议)和AF_LOCAL(Unix域协议)。

Linux支持以下的socket families或domain:

◆ Unix domain sockets;

◆ INET TneIntemet address family supports communications via;

◆ TCP/IP protocols;

◆ Amateur radio X.25;

◆ Novel IPX;

◆ Appletalk DDP;

◆ X.25。

套接口(socket)=网络地址+端口号。,要建立一个套接口必须调用socket函数,套接口有三种类型,即字节流套接口(SOCK_STREAM),数据报套接口(SOCK_DGRAM)和原始套接口(SOCK_RAW)。

4,5说起来太累,得把那tcp/ip详解上的图贴上来,才直观

*******网络编程基础( 讨论版V1 )*******

对gadfly 的一点补充:

定义一个连接的一个端点的两元组,即IP地址和端口号,称为一个套接口。在网络连接中,两个端点所组成的四元组(即本地IP、本地PORT、远程IP和远程PORT)称为socket pair,该四元组唯一的标识了一个网络连接。该情况可通过netstat验证。该四元组一旦建立便可读写数据到该接口。

套节字是用来引用和访问套接口的描述字(应用程序便是通过该描述字调用API与内核交互的),并且一个套接字唯一的对应一个套接口,反之,一个套接口可以有多个套节字。

*******网络编程基础( 讨论版V1 )*******

5.
连接过程是通过一系列状态表示的,这些状态有:LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT和 CLOSED。CLOSED表示没有连接,各个状态的意义如下:

LISTEN - 侦听来自远方TCP端口的连接请求;

SYN-SENT - 在发送连接请求后等待匹配的连接请求;

SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;

ESTABLISHED - 代表一个打开的连接,数据可以传送给用户;

FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;

FIN-WAIT-2 - 从远程TCP等待连接中断请求;

CLOSE-WAIT - 等待从本地用户发来的连接中断请求;

CLOSING - 等待远程TCP对连接中断的确认;

LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;

TIME-WAIT - 等待足够的时间以确保远程TCP接收到连接中断请求的确认;

CLOSED - 没有任何连接状态;

TCP连接过程是状态的转换,促使发生状态转换的是用户调用:OPEN,SEND,RECEIVE,CLOSE,ABORT和STATUS;传送过来的数据段,特别那些包括以下标记的数据段SYN,ACK,RST和FIN;还有超时,上面所说的都会时TCP状态发生变化。

 

下面的图表示了TCP状态的转换,但这图中没有包括错误的情况和错误处理,不要把这幅图看成是总说明了。

[img]http://www.longen.org/S-Z/details~z/TCPDetail-6.gif[/img]

http://www.longen.org/S-Z/details~z/TCP.htm

*******网络编程基础( 讨论版V1 )*******

握手和终止过程,也可以从上图中总结出来,各位可以归纳出c/s分别的状态变化序列

*******网络编程基础( 讨论版V1 )*******

对无双的一点补充:

针对标题2

UPD是不可靠的,如果向一个UDP套接口发送数据,那么并不能保证数据能够正确的到达,如果client发送的数据被路由丢弃或者服务器的应答信息丢失,那么client将一直阻塞,直到应用设置的超时到达,UDP需要应用来做接受确认、传输超时或者重传的机制。UDP缺乏流量控制。UDP是一个无连接协议,确省情况下client没有connect的动作(可以对UDP套接口的client或server调用connect),套接口的两个端点是通过API来标识的。一般的,UDP服务属于迭代的,而TCP服务大多数是并发的。UDP的队列机制被封装在API中。当一个TCP连接一旦成功建立,套接口的四元组是固定的,但是UDP不能保证这一点。再不能保证是否连接已建立的情况下,UDP的异步错误是不可预知的。对于一个UDP套接口可以多次调用connect,但是对于TCP只能调用一次。

TCP是面向连接的、全双工的、可靠的,TCP传输的是无记录边界有序字节流(有序是指:对于传输过程中的每个字节TCP与一个序列号关联起来),TCP通过通告窗口来控制流量。

有关TCP再此不做详细讨论,是以后讨论的重点。

*******网络编程基础( 讨论版V1 )*******

感谢gadfly对状态转化图的引入,不知道gadfly能否就TIME_WAIT和CLOE_WAIT和FIN_WAIT_1这三种状态做些描述,建议主要针对以下两方面来做描述:
(1)、该状态在什么情况下出现。
(2)、TCP为什么要引入该状态,分别对应与那些处理细节

一点补充,对于网络编程者而言,如果能够对TCP状态做深入的理解,那么网络应用中出现的大多数问题可通过状态图分析,从而找到原因(一些实现细节也隐含在状态图中),同时对于理解整个传输层协议由很大的帮助。

*******网络编程基础( 讨论版V1 )*******

3、IPv4和IPv6都属于网际协议(所有的网际协议由多个RFC定义),其版本有0、1、2、3和5,但是比较通用的是版本4和6。
版本4是80年代以后使用的比较广泛的主力协议,版本4使用32位的地址结构,主要给TCP、UDP、ICMP和IGMP提供传输分组的服务。
版本6是90年代中期设计出来的,主要变化是使用了128位的大地址结构,版本6主要给TCP、UDP和ICMPv6提供传输分组的服务。
在目前的应用中,有同时支持IPv4和IPv6的,一般称其为“IPv4/IPv6主机”或者"双栈主机"。
对于这两种协议之间的差别,可以到相关的系统中去察看,一般的在/usr/include/netinet/in.h中,各个系统定义的不尽相同,比如对于结构sockaddr_in中成员sin_len,sin_family和sin_port的数据类型的定义在很多unix系统中的定义有一定的差别,不过对于使用者来说,你只要关心它的大小就可以了,至于到底定义为哪一种类型,同样的可以到系统头文件中可以找得到。在Posix.1g中定义的套接口地址结构如下:
IPv4的定义:
struct in_addr {
in_addr_t s_addr;
};
struct sockaddr_in{
unit8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
其中成员sin_zero暂时没有使用到,用来扩充功能,一般在使用时将其初始化为0,事实上,Posix.1g只需要这个结构中的三个成员sin_family、sin_port和sin_addr,加入其他的两项完全是为了兼容性考虑的。在此强调,这个结构中重要的是结构的大小至少要求16字节,每个具体的成员的大小,请到你的系统中去察看,这里不再赘述。
需要说明的是:在实际的网络通讯中,该结构并不参与通信,尽管成员sin_port和sin_addr用在不同主机间的通信中。
IPv6的定义:
struct in6_addr {
unit8_t s6_addr[16];
};
#define SIN6_LEN
struct sockaddr_in6{
unit8_t sin6_len;
sa_family_t sin6_family;
in_port_t sin6_port;
unit32_t sin6_flowinfo;
struct in6_addr sin6_addr;

};
该结构比较特殊的是成员sin6_flowinfo,事实上该成员的32位用来表示不同的含义,如流量控制(低24位)、优先级(下4位)等。
以上所定义的结构,在我们实际的网络编程中,往往要作为某一个套接口函数的参数来使用,由于历史的原因和为了消除特定协议之间的差别,引入了所谓的通用套接口地址结构,定义如下:
struct sockaddr {
unit8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};
该结构一般的定义在/usr/include/sys/socket.h中,并且在很多系统中套接口函数原型的定义也使用该通用结构,如 int bind(int s, const struct sockaddr *addr, socklen_t addrlen);,这样在我们编写程序的时候,需要将只想特定协议的地址结构的指针类型转化为该通用套接口地址结构,例如:
struct sockaddr_in testserv;
/* other code */
if( ( ret = connect( sockfd, (struct sockaddr * )&amp;testserv,sizeof( testserv ) ) ) < 0 ) {
close(sockfd);
if( errno == EINTR ) {
errno = ETIMEDOUT;
continue;
}
}
/* other code */
以上简单的描述了IPb4和IPv6地址结构之间的差别,以及通用地址结构的使用。

有关两者互操作的下次补充(我要休息了,累!)

*******网络编程基础( 讨论版V1 )*******

-->

我就试着用上图解释,错了不要拍砖啊:-). 请各位纠正和补充。

正常情况下,
主动关闭的一端发出FIN请求后(close or shotdown),主动关闭的一端就进入这个状态,
FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;


被动关闭的一方接到FIN后,就发出ACK,并进入
CLOSE-WAIT - 等待足够的时间以确保远程TCP接收到连接中断请求的确认;

被动关闭的一方接着又发送FIN包,就进入了
LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;

主动关闭的一方接到ACK后,就进入了
FIN-WAIT-2 - 从远程TCP等待连接中断请求;

在主动的一方接收到FIN后,就发送ACK包,并进入TIME-WAIT状态。2MSL后进入closed

被动一方在接受到ACK包后,就进入了closed的状态。


TIME_WAIT状态状态也称为2MSL等待状态。MSL是TCP报文的生存时间,这样可让TCP再次发送最后的ACK以防这个ACK丢失(加入丢失,另一端超时并重发最后的FIN)。设想一下,
如果这个状态时间不足够长,如果ACK丢失,同时主动方的这个端口又重用了,对方因为
没有收到FIN的ACK,所以就重发FIN,有可能导致主动方连接的错误关闭。

如果是服务器被终止,并试图立即重新启动这个服务器程序的话,就可能发生经常出现的错误:can't bind local address: address already in use. 就是因为端口处于TIME_WAIT状态。不过可以通过设置选项SO_REUSEADDR来避免这种错误。


如果想了解的更透彻一些,建议通读TCP/IP详解 卷一的18章.

*******网络编程基础( 讨论版V1 )*******

大家都讨论得很激烈
看来论坛中还是藏龙卧虎之地很不错

另外
如果client发送的数据被路由丢弃或者服务器的应答信息丢失,那么client将一直阻塞,直到应用设置的超时到达

我觉得UDP就是发送以后不管的
只是向UDP端口写完数据后就返回
不会等待是不是发送成功

*******网络编程基础( 讨论版V1 )*******

client在sendto后,需要recvfrom(除非你的这个UDP应用只发送数据不接收数据),对于服务端来的响应信息client是一直阻塞的(除非应用设置了超时),因而不论是client发送的数据被路由丢弃还是服务器的应答信息丢失,client都无法得知这些信息,这种情况对于异步错误也是一样的。

*******网络编程基础( 讨论版V1 )*******

-->

同意你的观点,研究了一下,udp的输出队列有数据后,就会放到interface的
buf中,如果interface的buf满了,至少在linux上的处理是直接丢弃(参看sendto 的man ENOBUFS,和TCP/IP详解1的23)。在windows上缺省的buf满后,倒是会阻塞并等待。

而输入队列也有这个问题。如果发送方过快,即使数据到达对方主机,如果输入buf慢的话,udp包也是会丢失的。

*******网络编程基础( 讨论版V1 )*******

*******网络编程基础( 讨论版V1 )*******

对于sendto而言,是非阻塞的,如果发送的msg为0,它也能够成功,这时候发送的仅仅是一个IP头部和一个UDP头和一个空的数据报,即使在sendto中没有指明bind的IP和PORT,UDP会让系统来选择一个localhost和随机的Port填充结构 const struct sockaddr *to。

*******网络编程基础( 讨论版V1 )*******

对于recvfrom而言,是阻塞的(除非设置超时),unix95中原型如下:
ssize_t recvfrom( int  s,void  *buf,size_t  len,int  flags,struct sockaddr *from,size_t  *fromlen)
和sendto返回类型一样,recvfrom返回的是接受到的数据报大小。参数from和fromlen可以指定,也可以不指定,如果指定那么UDP用它来标识数据报的发送者是谁。该函数是从接受缓冲区接受数据报(如果缓冲区有数据报的话),如果RECVBUF没有数据,那么将阻塞。

*******网络编程基础( 讨论版V1 )*******

上面的关闭序列中,我只是列举了单方主动关闭的情况.

大家还可以研究一下双方同时主动关闭,以及单方半关闭的情况,这样能更好的理解shutdown,close的实现机制

*******网络编程基础( 讨论版V1 )*******

上面的5个问题只有4缺少完整讨论,这个留给其他的兄弟吧!总不能就我们几个说来说去,呵呵:)
接下来,谈论第二版V2。



Copyright © 1999-2000 LSLNET.COM. All rights reserved. 蓝森林网站 版权所有。 E-mail : webmaster@lslnet.com