TCPIP作业.docx
- 文档编号:13017407
- 上传时间:2023-06-10
- 格式:DOCX
- 页数:13
- 大小:99.02KB
TCPIP作业.docx
《TCPIP作业.docx》由会员分享,可在线阅读,更多相关《TCPIP作业.docx(13页珍藏版)》请在冰点文库上搜索。
TCPIP作业
TCP/IP与Internet
学院:
班级:
学号:
姓名:
一.什么是TCP/IP协议
是TransmissionControlProtocol/InternetProtocol的简写,即传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。
虽然从名字上看TCP/IP包括两个协议,传输控制协议(TCP)和网际协议(IP),但TCP/IP实际上是一组协议,它包括上百个各种功能的协议,如:
远程登录、文件传输和电子邮件等,而TCP协议和IP协议是保证数据完整传输的两个基本的重要协议。
通常说TCP/IP是Internet协议族,而不单单是TCP和IP。
TCP/IP是用于计算机通信的一组协议,我们通常称它为TCP/IP协议族。
之所以说TCP/IP是一个协议族,是因为TCP/IP协议包括TCP、IP、UDP、ICMP、RIP、TELNETFTP、SMTP、ARP、TFTP等许多协议,这些协议一起称为TCP/IP协议。
TCP(TransportControlProtocol)传输控制协议
IP(InternetworkingProtocol)网间网协议
UDP(UserDatagramProtocol)用户数据报协议
ICMP(InternetControlMessageProtocol)互联网控制信息协议
SMTP(SimpleMailTransferProtocol)简单邮件传输协议
SNMP(SimpleNetworkmanageProtocol)简单网络管理协议
FTP(FileTransferProtocol)文件传输协议
ARP(AddressResolationProtocol)地址解析协议
一.TCP/IP的结构
TCP/IP由四个层次组成:
网络接口层、网间网层、传输层、应用层。
每个层次的功能描述如下:
●网络接口层这是TCP/IP软件的最低层,负责接收IP数据报并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP层
●网间网层负责相邻计算机之间的通信。
其功能包括三方面。
一、处理来自传输层的分组发送请求,收到请求后,将分组装入IP数据报,填充报头,选择去往信宿机的路径,然后将数据报发往适当的网络接口。
二、处理输入数据报:
首先检查其合法性,然后进行寻径--假如该数据报已到达信宿机,则去掉报头,将剩下部分交给适当的传输协议;假如该数据报尚未到达信宿,则转发该数据报。
三、处理路径、流控、拥塞等问题。
●传输层提供应用程序间的通信。
其功能包括:
一、格式化信息流;二、提供可靠传输。
为实现后者,传输层协议规定接收端必须发回确认,并且假如分组丢失,必须重新发送。
●应用层向用户提供一组常用的应用程序,比如电子邮件、文件传输访问、远程登录等。
远程登录TELNET使用TELNET协议提供在网络其它主机上注册的接口。
TELNET会话提供了基于字符的虚拟终端。
文件传输访问FTP使用FTP协议来提供网络内机器间的文件拷贝功能。
TCP/IP的五层结构图如下:
TCP报头
TCP报头总长最小为20个字节,其报头结构如下图(图1)所示;
比特0 比特15比特16 比特31
源端口(16)
目的端口(16)
序列号(32)
确认号(32)
TCP偏移量(4)
保留(6)
标志(6)
窗口(16)
校验和(16)
紧急(16)
选项(0或32)
数据(可变)
二.TCP/IP的相关函数的分析
#include
intgetaddrinfo(constchar*hostname,constchar*service,conststructaddrinfo*hints,structaddrinfo**result);
返回0:
成功
返回非0:
出错
hostname:
一个主机名或者地址串(IPv4的点分十进制串或者IPv6的16进制串)
service:
一个服务名或者10进制端口号数串。
hints:
可以是一个空指针,也可以是一个指向某个addrinfo结构的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。
举例来说:
如果指定的服务既支持TCP也支持UDP,那么调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM使得返回的仅仅是适用于数据报套接口的信息。
本函数通过result指针参数返回一个指向addrinfo结构链表的指针,而addrinfo结构定义在头文件netdb.h中:
structaddrinfo{
intai_flags;
intai_family;
intai_socktype;
intai_protocol;
socklen_tai_addrlen;
char*ai_canonname;
structsockaddr*ai_addr;
structaddrinfo*ai_next;
};
如果本函数返回成功,那么由result参数指向的变量已被填入一个指针,它指向的是由其中的ai_next成员串联起来的addrinfo结构链表。
可以导致返回多个addrinfo结构的情形有以下2个:
1.如果与hostname参数关联的地址有多个,那么适用于所请求地址簇的每个地址都返回一个对应的结构。
2.如果service参数指定的服务支持多个套接口类型,那么每个套接口类型都可能返回一个对应的结构,具体取决于hints结构的ai_socktype成员。
我们必须先分配一个hints结构,把它清零后填写需要的字段,再调用getaddrinfo然后遍历一个链表逐个尝试每个返回地址。
getaddrinfo解决了把主机名和服务名转换成套接口地址结构的问题。
其中,如果getaddrinfo出错,那么返回一个非0的错误值。
#include
constchar*gai_strerror(interror);
该函数以getaddrinfo返回的非0错误值的名字和含义为他的唯一参数,返回一个指向对应的出错信息串的指针。
由getaddrinfo返回的所有存储空间都是动态获取的,这些存储空间必须通过调用freeaddrinfo返回给系统。
#include
voidfreeaddrinfo(structaddrinfo*ai);
ai参数应指向由getaddrinfo返回的第一个addrinfo结构。
这个连表中的所有结构以及它们指向的任何动态存储空间都被释放掉。
例子:
structaddrinfo*host_serv(constchar*host,constchar*serv,intfamily,intsocktype)
{
intn;
structaddrinfohints,*res;
bzero(&hints,sizeof(structaddrinfo));
hints.ai_flags=AI_CANONNAME;
hints.ai_family=family;
hints.ai_socktype=socktype;
if((n=getaddrinfo(host,serv,&hints,&res))!
=0)
return(NULL);
return(res);
}
inaddroute函数
用rtrequest可以创建一个新的路由表记录项,它们或者是RTMADD命令的结果,也
可能是RTMRESOLVE命令的结果。
这两个命令都会从已经存在并且设置了克隆标志的记录项中创建一个新的记录项。
创建以后就要调用rnhaddaddr函数,我们在
Internet协议中看到的是inaddroute函数。
如果所增加的路由不是一个主机路由,也没有设置克隆标志,这时就要检查路由表
的主键(IP地址)。
如果IP地址不是一个多播地址,该新创建的路由表记录项就要设置克隆标志。
rnaddroute为路由表增加记录项。
这个函数的功能是为所有非多播网络路由设置克隆标志,包括默认的路由。
这个克隆标
志的作用是为任何一个在路由表中能够查到一条非多播网络路由或默认路由的目的地址创建一个新的主机路由。
这个新克隆的主机路由是在它第一次查找时创建的。
这里根据网络堆栈进行TCP协议的read函数进行编程分析。
首先是总体的调用顺序:
(从用户通过tcp/协议进入物理层。
当然还有udp,icmp等。
)(用户程序)read()->sys_read()->sock_read()->inet_read()->tcp_read();先说到这里。
从sock_read()开始
调用接口函数为staticintsock_read(structinode*inode,structfile*file,char*ubuf,intsize)
{
1、sock=socki_loopup(indoe);/*这个根据节点找到对应socket结构的指针2、verify_arae(VERIFY_WRITE,ubuf,size)/*给内核提供写时赋值的渠道*/
3、sock->ops->read(sock,ubuf,size,(file->f_flags&O_NONBLOCK))/*这里就是调用inet_read()*/
}
这个结构来自这里:
/*sock->ops为structproto_ops结构,因为是inet域,sock->ops被赋值为inet_proto_ops。
如果是unix域,就被赋值为unix_proto_ops类似的。
staticstructproto_opsinet_proto_ops={
AF_INET,
inet_create,
...
inet_read,
...
}
所以后面调用的是inet_read()函数:
staticintinet_read(structsocket*sock,char*ubuf,intsize,intnoblock)/*noblock,发生阻塞时是否立即返回,*ubuf指向用户内存的地址,intsize,读取的大小,sock对应的接口文件*/
{
structsock*sk=(structsock*)sock->data;/*data域一般都是存放里面的小的重要的数据结构类型,比如sokect->data是sock类型,而sock->data就是sk_buff类型*/
1、inet_autobind(sk)/*这个函数为sk结构分配一个端口号*/
2、sk->prot->read(sk,(unsignedchar*)ubuf,size,noblock,0);
}
先说inet_autobind:
staticinet_auto_bind(structsock*sk)
{
...
1.1、sk->num=get_new_socknum(sk->prot,0);/*这里真正是分配端口号的函数*/
1.2put_sock(sk->num,sk);/*这里是将sock结构的sk,送入其对应协议的端口号队列,sk->num是大于1024的*/
...
}
1.3
voidput_sock(unsignedshortnum,structsock*sk)
{
structsock*sk1;
structsock*sk2;/*这里定义两个sock*结构为了让sk进入队列任意两个结构之间,后面见到*/
....
num=num&(SOCK_ARRAY_SIZE-1);/*因为数组中队列最大下标为255。
*/
save_flags(flags);/*保存当前的falgs,为了禁止中断*/
sk->prot->inuse+=1;/*sk->prot使用计数加1*/
for(mask=0xff000000;mask!
=0xffffffff;mask=(mask>>8)|mask)
{
if(mask&sk->saddr)&&(mask&sk->saddr)!
=(mask&0xfffffff)/*这里是算有效位
(1)的数目,以8位为一个段*/
{
mask=mask<<8;
break;
}
}}/*这里判断用于此ip地址的相应mask,为了使用利用前面的1的数量来选择位置*/
sk1=sk->prot->sock_array[num];/*取当前的第一个sock结构,用其中的saddr来和新的sk->addr进行比较*/
for(sk2=sk1;sk!
=NULL;sk2=sk2->next)
{
if(!
(sk2->saddr&mask))/*sk->saddr和sk的mask是对应的*/
if(sk==sk1)
{
sk->next=sk->prot_array_array[num];
sk->prot-sock_array[num]=sk;
sti();
return;
}
....../*后面就是插入两个sock之间的操作*/
}
}
举例:
函数中实现的插入顺序有效位(即非零位)从多到少排列的,如
10.16.1.23排在10.16.1.0之前,10.16.1.0排在10.16.0.0之前,依次类推。
2、这里为tcp_read()了。
staticinttcp_read(structsock*sk,unsignedchar*to,intlen,intnonblock,unsignedflags)
{
structwait_queuewait={current,NULL};/*这是等待进程初始化为current当前进程*/
其他的一些情况判断:
比如if(sk->state==TCP_LISTEN)/*侦听套结字不负责数据传送,其receive_queue缓存的均是请求连接(数据包(SYN数据包),如果读取的套结字状态是侦听,说明用户调用出现了问题*/
if(flags&MSG_OOB)/*如果请求读紧急数据,就返回读取紧急函数读取值*/
returntcp_read_urg(sk,nonblock,to,len,flags);
/*分配要更新的变量,如果仅仅是PEEK预处理,不对内核变量更新*/
peek_seq=sk->copied_seq;
seq=&sk->copied_seq;
if(flags&MSG_PEEK)
seq=&peek_seq;
/*将前面的wait变量加到sock结构的sleep睡眠队列中,下文代码随时可以进入睡眠等待状态*/
add_wait_queue(sk->sleep,&wait);
while(len>0)/*len表示用户需要读取的数据长度,满足就返回*/
{
/*如果有紧急数据,立即跳出循环*/
if(copied&&sk->urg_data&&sk->urg_seq==*seq)/*
break;
current->state=TASK_INTERRUPTABLE;/*程序为可中断状态,条件不满足时,可以随时进入睡眠*skb=skb_peek(&sk->receive_queue);/*将接受队列中的下一个sk_buff结构取出来,为NULL,是返回NULL*/
do{
if(!
skb)
break;/*如果为NULL,中断do..while循环*/
if(before(*seq,skb->h.th->seq))
break;/*要读取的序列号在当前接受到的序列号前,说明出现了点问题*/
offset=*seq-skb->h.th->seq;/*读取的序列号和当前sk_buff序列号中相对偏移*/
if(skb->h.th->syn)
offset--;/*如果sk_buff是个syn数据包,不算入偏移*/
if(offset
gotofound_ok_skb;/*如果offset(序列号)小于skb->len的长度,那么表示skb所表示的数据包中有可用数据*/
if(skb->h.th->fin);
gotofound_fin_ok;/*跳转到fin包处理;
if(!
flags&MSG_PEEK))/*PEEK表示预处理,不做处理*/
skb->used=1;/*skb->used置一,表示数据包已作处理*/
skb=skb->next;
}while(skb!
=(structsk_buff*)&sk->receive_queue);/*将sk->receive中的sk_buff都处理完*/
if(copied)/*如果拷贝了数据,就跳出最外层while循环*/
break;
......
cleanup_rbuf(sk);
release_sock(sk);
sk->sockect->flags|=SO_WAITDATA;
/*这个不算*/schedule();/*进程调用,前面本进程已经设置为TASK_INTERRUPTABLE状态了*/
sk->inuse=1;
if(current->signal&~current->blocked)
{
copied=-ERESTARTSYS;
break;
}
continue;
}
found_ok_skb:
skb->users++;/*防止内核其他部分将其释放*/
used=skb->len-offset;/*sk->len表示数据包长度,offset表示可读取的数据的起始偏移量,相减得到:
used表示可读取的数据字节数*/
if(len used=len;/*被初始化本次需要读取的字节数*/ if(sk->urg_data)/*如果包含紧急数据,此版本紧急数据为一个字节*/ unsignedlongurg_offset=sk->urg_seq-*seq; if{.....) else used=urg_offset;/*如果普通数据中有紧急数据,那么不能越过此进行处理*/ memcpy_tofs(to,((unsignedchar*)skb->h.th)+skb->h.th->doff*4+offset,used);/*拷贝到用户空间*/ 从tcpheadr+本身长度(skb->h.th->doff*4(头部以4字节为一个长度单位)+offset(此为可读取的偏移地址),used表示数据的长度,如果有紧急数据,此长度就是紧急数据长度*/ ..... found_fin_ok: ++*seq;/*fin数据包只占一个序列号*/ }endofthewhile remove_wait_queue(sk->sleep,&wait); current->state=TASK_RUUNING;/*将本进程从睡眠队列中删除,被重新设置成可运行状态*/ cleanup_rbuf(sk); release_sock(sk); returncopied;/*copied记录拷贝了多少字节的值*/ } staticinttcp_read_urg(structsock*sk,intnonblock,unsignedchar*to,intlen,unsignedflags) { ....../*紧急数据为一个字节,所以用charc来接受*/ charc=sk->urg_data; put_fs_byte(c,to);/*将c放入to的用户空间*/ release_sock(sk); return1; ...... } 最后一个函数: voidrelease_sock(structsock*sk)/*将读取的那个sock结构对应在sk->back_log中sk_buff都读到receive_queue中,正好继续来处理*/ { while(skb=skb_dequeue(&sk->back_log))! =NULL) sk->blog=1; if(sk->prot->rcv) sk->prot->rcv(skb,skb->dev,skb->opt,skb->saddr,skb->len,skb->daddr,1,(struct inet_protocol*)sk->pair);/*此函数的作用调用get_sock()函数和tcp_check函数(此函数用户计算校验和)。 ...... if(sk->dead&&sk->state==TCP_CLOSE) { reset_timer(sk,TIME_DONE,min(sk->rtt*2,TCP_DONE_TIME);/*如果sk->state为关闭状态,设定时器*/ } } inttcp_rcv(strcutsk_buff*skb,structdevice*dev,structoptions*opt,unsignedlongdaddr,unsignedshortlen,unsignedlongsaddr,intredo,structinet_protocol*protocol) { skb: 为被接受的数据包,dev: 为接受此数据包的设备: IP选项: len为: IP负载长度; redo: 0: 表示一个新的数据包,redo为1,表示缓存的; if(! redo) { if(tcp_check(th,len,addr,daddr))/*校验,返回0为成功*/ kfree_skb(skb,FREE_READ);/*释放kmalloc分配的空间*/ if(sk==NULL) tcp_reset(daddr,saddr,th,&tcp_prot,opt,dev,skb,skb->ip_hdr->tos,255);/*发送一个RST复位数据包,使对方断开连接,如果对方还连接,对方再次发出connect;*/ if(sk->rmem_alloc+skb->mem_len>=sk->rcvbuf) { kfree_skb(skb,FREE_READ);/*如果接受缓冲区剩余空间小,就丢包*
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- TCPIP 作业