第六章 UNIX的网络通信初步_第1页
第六章 UNIX的网络通信初步_第2页
第六章 UNIX的网络通信初步_第3页
第六章 UNIX的网络通信初步_第4页
第六章 UNIX的网络通信初步_第5页
已阅读5页,还剩102页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第六章第六章 unix的网络通信初步的网络通信初步 (1) unix操作系统为进程通信提供了相应设施,如管道(pipe)、命名管道(named pipe)和软中断信号(signal),消息(message)、共享存储区(shared memory)和信号量(semaphore)等,但这些都只限于用在本机进程间的通信。 (2) 为了实现计算机全面联网与信息的异地处理,需要为用户构建client/server 应用的通讯结构,通过网络接口编程,以解决不同主机进程间的通信问题。6.1 网络接口网络接口 在unix系统中,网络接口有两类:一类是源自bsd unix的sockets(套接口),另一类是u

2、nix system v 的tli(transmission layer interface)。tli是根据工业标准“iso传输服务定义(iso 8072)”实现的,由于svr3只包括了流以及tli构建模块而并没有任何的如tcp/ip之类的协议,因此tli具有与协议无关性,关键技术是定义了一组对许多传输协议公共的服务。目前tli的修正版xtl在unix系统中仍然得到广泛的使用。socket api是基于各种传输协议之上的,目前已经成为网络编程的既成事实标准。 基于sockets api的通用性,本章只讨论sockets api的应用。 目前最通用的提供远程进程间通信的api是伯克利套接字(be

3、rkeley socket)接口。 所谓的套接字是一种抽象数据结构,用以创建一条在没有相关联的进程间发送、接收消息的通道(连接点)。这些进程在通信前各自建立一个socket,并通过对socket的读写操作实现通信功能。 当使用基于套接字的连接时,服务器端进程创建一个套接字,并把它映射到一个本地地址上,然后等待(监听)客户端的请求。客户端进程也创建自己的套接字,并确定服务器端的具体位置(比如主机名,端口号等)。依靠传输连接方式的应答,客户端进程就可以开始发送和接收数据,而不用管是否接收到服务器进程的正式确认(应答)。 每个套接字都有其类型和一个与之相连的进程。当应用程序创建套接字时,套接字系统调

4、用返回句柄,即所谓套接字描述字,它和文件描述字是有所区别的:当文件描述字,由open命令创建时,它被耦合到特定的文件或设备;当套接字描述字由socket命令创建时,它并不被耦合到任何位置。当套接字用作面向连接的网络传输接口时,应用程序可用bind命令将套接字明确地耦合到一个地址。当套接字用作无连接的网络传输接口时,应用程序可以在用sendto命令发送数据报时动态地提供地址。 6.1.1 套接口的类型套接口的类型 unix 提供下列四种类型的socket: 数据流套接字(sock_stream),它提供双向的、面向连接的、可靠的、有序的并且不重复的无记录边界数据流。一对相连的流socket提供几

5、乎类似于管道的接口。流式socket针对于tcp服务应用,如文件传送协议(ftp)。 数据流套接字采用tcp协议,这是个有连接的协议,在数据正式传输前必须建立连接,此连接是个稳定的双向线路,可以保证提供无错误的传送管道,因为只要封包在传送过程发生错误损毁、次序错乱或送错,tcp将会察觉问题并要求重新发送数据,因此适合在需要大量的数据传输并要求完全正确的状况时使用。 6.1.1 套接口的类型套接口的类型 数据报套接字(sock_dgram),它也支持双向数据流,但数据以独立包形式被发送,无可靠性保证、无序、数据可能丢失或重复。数据报socket提供一个无连接服务,对应于无连接的udp服务应用,如

6、网络文件系统(nfs)、组播通信。 数据报套接字采用udp协议,这是个无连接的协议,发送主机直接将封包送至目的主机,无需事先建立连接。因为避免了建立连接所需的高代价,采用数据报方式效率较高,但数据报方式自身不能处理数据传输过程出现的错误,因此使用数据报方式的应用程序必须自己处理这些问题。一般在比较简单的网络应用程序中使用数据报方式。 6.1.1 套接口的类型套接口的类型 原始套接字(sock_raw),它提供对支持socket概念的基本通信协议的访问。该接口允许用户访问支持套接字抽象的底层通信协议,如ip、icmp直接访问,常用于检验新的协议实现或访问现有服务中配置的新设备。 顺序报套接字:这

7、种类型的套接字类似数据流套接字,不同的是其传送的数据具有记录边界。6.1.2 套接口支持的协议套接口支持的协议 1套接字协议簇套接字协议簇(family) (1)af-unix :unix domain协议,在该域中创建的套接字只能为同在一个主机的进程所用; (2)af-inet:internet协议,这个域里的套接字允许在不同主机上的不相关的进程间进行通讯; 2套接字协议套接字协议(protocol) (1)tcp协议(传输控制协议):负责保证两台主机之间传输的分组到达目的地,保证分组以正确的顺序(确切的说,分组按正确的顺序重新编排)并无差错地到达目的地。当分组在两台主机间的路径上丢失时,t

8、cp协议确保重传丢失的分组; (2)udp协议(用户数据报协议):类似tcp协议,但是它是不可靠的。udp不对分组进行检查、重新排序和重传; 6.1.2 套接口支持的协议套接口支持的协议af_inetaf_inet6af_localaf_routeaf_key流套接口tcptcpy数据报套接口udpudpy原始套接口ipv4ipv6yy6.1.3 套接口地址结构套接口地址结构 大多数套接口函数都需要一个指向套接口地址结构的指针作参数,而在实际应用中各协议的地址结构是不同的,例如ipv4是32位的,而ipv6则是128的。由于早期定义的原因,unix的系统函数都只支持通用的地址结构而无法区分特定

9、协议的地址结构,因此在调用这些函数时必须将指向具体协议的套接口地址结构的指针类型转换成指向通用套接口地址结构的类型。如serv是ipv4的地址格式,可以通过(struct sockaddr *)&serv来转换成通用地址格式。通用套接口地址结构:(在头文件中定义)struct sockaddr u_char sa_len; /* 地址总长度*/ u_short sa_family; /* 协议族,af_xxx */ char sa_data14; /* 具体协议地址 */ ; 其中:sa_family为套接字协议簇类型;sa_data中存储具体的协议地址,不同的协议簇有不同的地址结构,

10、如tcp/ip协议的套接字地址结构是在文件中定义的sockaddr_in结构,这个结构定义如下:ipv4套接口地址结构:(在头文件中定义)struct sockaddr u_char sin_len; /* 32位的ipv4地址总长度*/ u_short sin_family; /* 协议族,af_inet */ u_short sin_port; /*协议端口号*/ char sin_zero8; /* 保留,置0 */ ; 要注意的是,套接口的地址结构是按网络字节顺序而不是按主机存储字节顺序来存储的(网络字节是从高到低的顺序,而主机是从低到高的顺序),因此,在internet上传输数据时就

11、需要进行转换,否则就会出现数据不一致。下面是几个常用的字节顺序转换函数: htonl():把32位值从主机字节序转换成网络字节序 ntohl():把32位值从网络字节序转换成主机字节序 通用套接口地址结构 ipv4套接口地址结构长度 af_iniet16位端口号 sin_portsin_zero8长度 af_iniet16位端口号 sin_portsa_data166.2 网络通信的系统函数网络通信的系统函数 6.2.1 建立网络连接的数据结构 一个用户为了执行网络i/o操作,它首先要调用函数socket ()。这个函数陷入内核后执行sys_socket()系统调用,后者为用户创建一个名为so

12、cket的数据结构,并对其进行一些简单的初始化工作,然后返回一个小的正整数,代表这个socket结构。这个小的正整数与文件描述符功能类似,所以把它称作套接字描述符。6.2.1 建立网络连接的数据结构struct socket socket_state state; unsigned long flags; struct proto_ops *ops; struct inode *inode; struct fasync_struct *fasync_list; struct file *file; struct spck *sk; wait_queue_head_t wait; short t

13、ype; unsigned char passcred; unsigned char tli;其中:state描述该套接字的状态信息; flags表示连接时的一些控制信息; ops指向oroto_ops 结构类型的指针; inode是指向inode 结构类型的指针; fasync_list存在异步进行处理的不同文件的指针; sk指向socket结构的指针; wait表示等待在该socket 结构上的人物列表; type表示数据包的类型。6.2.2 网络连接的建立和关闭网络连接的建立和关闭 1 建立一个套接字描述符 函数格式如下: int socket(int family, int type,

14、 int protocol); socket( )调用中有三个参数,第一个参数family是一个整数型的量,指定协议簇;第二个参数是type,表示套接字的类型;第三个参数protocol用来表示在指定协议族中使用哪种特定协议,大多情况下,该参数被设为0,让系统自己去选择基于协议族的协议。socket()调用成功则返回一个正整数,即套接字描述符,用以标识该套接字;如果调用失败,会返回1,并设置全局变量errno为相应的错误类型。 sockek()函数的执行流程socket( )sys_socketcall( )sys_socket( )socket_create( )socket_alloc(

15、)inet_creat()sk_alloc( )sock_alloc()用来创建一个socket 结构sk_alloc( )用来创建一个sock 结构系统调用接口sock_create(.) sock_alloc(); inet_creat(); 例1:创建一个套接字对#include #include #include #include #include main(void) int sock2, cpid, i; /* 套接字对 */ static char bufbuf_sz; /* 消息的临时缓冲区 */ if(socketpair(pf_unix,sock_tream, 0, soc

16、k) 0) perror(“generation error ”); exit(1); switch(cpid=(int)fork() case -1: perror(“back fork ”); exit(2); case 0: /* 子进程 */ close(sock1); for(i=0; i 10; i +=2) sleep(1); sprintf(buf, “c: %dn”,i); write(sock0, buf, sizeof(buf); read(sock0, buf , buf_sz); printf(“c %s”, buf); close(sock0); break; de

17、fault: /* 父进程 */ close(sock0); for(i=0; i 10; i +=2) sleep(1); read(sock1, buf , buf_sz); printf(“p%s”, buf); sprintf(buf, “p: %dn”,i); write(sock1, buf, sizeof(buf); close(sock1); return 0;6.2.2 网络连接的建立和关闭网络连接的建立和关闭 2 指定本机地址及端口 bind() int bind(int sockfd, struct sockaddr * my_addr, int addrlen); /*

18、 0成功;-1出错*/ 其中:参数sockfd是调用socket()返回的套接字,参数my_addr是通用地址结构指针,参数addrlen是该结构的长度,常被设置为sizeof(struct sockaddr)。该函数为套接字分配一个本地协议地址,对于ip协议来说是ip地址和tcp或udp端口号的组合。 bind( )调用在unix域中用来联系套接字和它的名字(一个文件名),在因特网域用来将本地地址和套接字绑定在一起,包括ip地址和端口号。它是依据第二个参数的值的不同而不同的;套接字和本地地址的绑定采用组合的方式,如下表 程序类型ip地址端口号说明服务器inaddr-any非0指定服务器公认端

19、口,表示服务器愿意接收来自任何网络设备的客户机连接服务器本地ip非0指定服务器ip地址和公认端口,表示它只接收来自于这个ip地址的特定网络设备接口的客户机连接。多用于有多个网络设备接口,限制服务器的接收范围客户机inaddr-any非0指定客户机的连接端口,一般使用保留端口号客户机本地ip非0指定客户机的ip地址和连接端口号,表示客户机使用指定的网络设备接口和端口号进行通信客户机本地ip0指定客户机的ip地址,表示客户机和指定的网络设备接口通信,系统自动为客户机选择一个未使用的端口号注:inaddr-any在unix系统中被映射为0的常量6.2.2 网络连接的建立与关闭网络连接的建立与关闭 使

20、用bind函数时,可以用下面的赋值实现自动获得本机ip地址和随机获取一个没有被占用的端口号: my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */ my_addr.sin_addr.s_addr = inaddr_any; /* 填入本机ip地址 */ 6.2.2 网络连接的建立与关闭网络连接的建立与关闭 3 客户启动连接connect() int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); /*返回:0成功;-1出错*/ sockfd是调用socket()返回的套接字;serv

21、_addr是包含远端服务器ip地址和端口号的通用地址结构的指针;addrlen是远端地址结构的长度。tcp客户用connect函数建立一个与远端tcp服务器的连接,正是connect激发了tcp三次握手的连接过程。 由于协议族总被包含在套接字地址结构的前两个字节中,并通过socket()调用与某个协议族相关。因此bind()和connect()无须协议作为参数。 connect( )用户空间核心空间sys_socketcall( )sys_connect( )inet_stream_coonect( )tcp_v4_connect( )ip_route_output( )ip_route_ou

22、tput_key( )ip_route_connect( )ip_output( )ip_route_optput_slow( )系统调用传输层(tcp协议)网络层(ipv4或ipv6)6.2.2 网络连接的建立与关闭网络连接的建立与关闭 4 监听连接listen() int listen(int sockfd, int backlog); /*返回:0成功;-1出错*/ sockfd是调用socket()建立的套接字,它是一个socket调用成功后的返回值;backlog是指定在请求队列中允许的最大请求数,一般大于5的均设为5 。 进入的连接请求将在队列中等待accept()调用建立与客户的

23、连接。该函数仅被tcp服务器调用,它总是使套接口处于被动的监听模式,并为该套接口建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们。如果一个服务请求到来时,输入队列已满,该套接口将拒绝连接请求并显示出错信息。 listen( )调用将一个尚未连接的主动套接字转换成为一个被动套接字,使其可以接收连接请求,因为由socket( )调用所创建的套接字(主动套接字)只可以用来进行主动连接,不能接收连接的请求。 5 服务器接受连接accept() int accept(int sockfd, struct sockaddr *addr, int *addrlen); /*返回:0成功

24、;-1出错*/ accept( )调用从倾听套接字的连接队列中接收第一个连接,生成一个新的套接字来完成客户机的要求,原来的套接字继续监视网络,等待用户的连接。 accept( )调用的第一个参数sockfd是用来标识从哪个套接字中接收连接的 ,addr是一个指向客户方套接字地址结构的变量指针,该变量用来接收提出连接请求服务的客户的协议地址,指明某台主机从某个端口发出该请求;addr的确切格式由套接字创建时建立的地址族决定。addrten通常为一个指向值为sizeof(struct socka ddr) 的整型指针变量,指明客户方套接字地址结构的长度(字节数)。 accept()只用于tcp服务

25、器,且在调用前应该先调用过listen()。当listen()侦听到有连接请求到达时,accept()调用将请求连接队列上的第一个客户的套接字地址及长度放入addr和addrlen,并与该客户在sockfd建立连接。 6.2.2 网络连接的建立与关闭网络连接的建立与关闭 6 6 关闭套接字关闭套接字close()close() close(int sockfd); 参数 sockfd是待关闭的套接字。close()是标准的关闭函数,在tcp服务中激发该套接字的连接关闭。函数功能只是对该套接字作“关闭标识”表明不可用,而连接的另一方还在试图发送排队的数据。只有当对方发现通信的套接字已不可用,自己

26、也调用close()关闭本机的套接字,才真正地结束数据的发送。 6.2.3 发送数据发送数据 unix内核为用户提供的发送数据的系统调用有5个,它们分别是: 1) write():与文件系统中的write()完全一致; 2) writev():与write功能相似,所不同的是writev可以在一次函数调用中写多个缓冲区(集中写) 3) send():面向连接的发送数据过程(tcp); 4) sendto():面向无连接的发送数据过程(udp) 5) sendmsg():直接使用msghdr 结构发送数据。在功能上可以代替以上四个输出函数。1 1 发送数据的系统调用接口发送数据的系统调用接口wr

27、ite()writev()send()sendto()sendmsg()sys_write()sys_writev()sys_socketcall()sock_write()sock_write()sys_send()sys_sendmsg()sys_sendto()sock_sendmsg()inet_sendmsg()系统调用应用层bsd 套接字接口inet 套接字(传输层)2. 2. 从从inteinte协议层到协议层到ipip层层inet_sendmsg()tcp_sendmsg()tcp_sendmsg()tcp_transmit_skb()ip_queue_xmit()网络层bsd

28、 套接字接口inet 套接字(传输层)2. ip2. ip层到硬件层的数据发送过程层到硬件层的数据发送过程 ip_queue_xmit2()ip_output()ip_finish_output()ip_queue_xmit()net/core/dev.cip层dev_queue_xmit()neigh_resolve_output_()ip_finish_output2()net/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/core/neighbour.c数据链路与硬件层4. 4. 硬件层的数据发送过程硬件层的数据发送过程de

29、v_queue_xmit()ei_start_xmit()qdisc_run()ei_start_xmit()物理设备硬件层net/pkt_sched.h硬件上有队列吗?以太网卡(例如ne2000)6.2.4 接收数据接收数据unix内核为用户提供的接收数据的系统调用也是5个,它们分别是: 1) read():与文件系统中的read()完全一致; 2) readv():与read功能相似,所不同的是readv可以在一次函数调用中读多个缓冲区; 3) recv():面向连接的接收数据过程(tcp); 4) recvfrom():面向无连接的接收数据过程(udp) 5) recvmsg():直接使

30、用msghdr 结构来接收数据。在功能上可以代替以上四个输出函数。1 1 接收数据的系统调用接口接收数据的系统调用接口read()readv()recv()recvfrom()recvmsg()sys_read()sys_readv()sys_socketcall()sock_read()sock_readv()sys_recv()sys_recvmsg()sys_recvfrom()sock_recvmsg()inet_recvmsg()系统调用应用层bsd 套接字接口inet 套接字(传输层)2. 硬件层接收数据分析 netif_rx()ei_recieve()ei_interrupt()

31、ip_rev()net/core/dev.cdrivers/net/8390.cdrivers/net/8309.c 硬件层net_rx_action()ip层物理设备ne网卡向队列写入数据backing接收数据队列从队列中读出数据2. 硬件层接收数据分析3. 从ip层接收数据 tcp_v4_rcv()ip_local_deliver_finish()ip_local_deliver()tcp_rev_established()inet层net_rx_action()ip_rev()ip_rcv_finish()net/ipv4/tcp_et/ipv4/ip_et/ipv4/ip_et/ipv

32、4/input.c 硬件层tcp_v4_do_rcv()receive_queue队列 ip层4. 4. 从从inteinte层接收数据层接收数据6.3 套接字编程方法套接字编程方法 流套接字的服务器进程和客户机进程在进行通信前必须建立一条连接,其中,初始化连接的是客户端进程,接收连接的进程是服务器端的进程。建立连接和通信的主要步骤如下:(1)服务进程首先调用socket( )创建一个流套接字;(2)服务进程调用bind( )公开服务器地址,将服务器地址与套接字绑定在一起;(3)服务进程调用listen( )将套接字转换成倾听套接字,此时该套接字可以接收来自客户机的请求;(4)通过accept

33、( )阻塞服务进程,此时该服务器进入一个无限循环,等待客户进程建立连接;(5)客户进程也通过socket( )创建一个流套接字,然后调用connect( )与服务进程建立连接;(6)当客户进程的连接请求到达服务器后,服务进程进程被唤醒,生成一个新的套接字,服务进程用这个新的套接字按预先定义的协议调用read( )和write( )进行通信,处理客户进程的要求;而服务进程最早生成的套接字则继续用于监听网络上的服务请求;(7)处理完成后,服务进程和客户进程调用close( )关闭这个连接和套接字;服务器socket()、bind()、listen()accept()read()write()rea

34、d()close()阻塞,等待客户机连接请求 coonect()write()read()socket()close()客户建立连接(tcp三路握手)发送数据请求接收数据通信结束、关闭连接 (1)服务器一方main(void) if(创建一个流套接字返回值0) 出错提示; 退出; if(命名套接字返回值0) 出错提示; 退出; if(监听连接请求返回值0) 出错提示; 退出; for( ; ; ) 新的套接描述符=取得第一个连接请求返回代码; if(新的套接描述符0) 出错提示; 退出; 接收数据信息; 处理请求; 将应答发送给客户机; 关闭套接字; (2)客户一方 main(void) if

35、(创建一个流套接字返回值0) 出错提示; 退出; if(连接服务器返回值0) 出错提示; 退出; for( ; ; ) 发送数据信息; 接收服务器方应答; 关闭套接字; 1 数据报套接字的服务器进程和客户机进程在进行通信前不用建立连接。通信的主要步骤如下: (1)服务进程首先调用socket ( )创建一个数据报套接字; (2)服务进程调用bind( )将服务器地址绑定在在这个套接字上; (3)通过recvfrom( )阻塞服务进程,等待客户进程发来的请求; (4)客户机首先调用socket( )创建一个数据报套接字; (5)客户机进程调用bind( )将客户机地址绑定在在此套接字上; (6)

36、调用sendto( ),客户机进程向服务进程发出请求; (7)服务进程接到客户机数据报后被唤醒,执行完客户机请求后调用sendto( )将处理结果返回给客户机; (8)客户机调用recvfrom( )接收服务进程返回的请求处理结果; (9)服务进程和客户进程调用close( )撤消套接字; socket()服务bind()recvfrom()进程请求sendto()socket()sendto()read()close()客户recvfrom()数据(请求)数据(回答)阻塞直至收到来自客户的数据报 (1)服务器一方 main(void) if(创建一个数据报套接字返回值0) 出错提示; 退出;

37、 if(命名套接字返回值0) 出错提示; 退出; for( ; ; ) 接收客户机数据报(请求); 处理请求; 将数据(结果)发送给客户机; 关闭套接字; (1)客户端一方 main(void) if(创建一个数据报套接字返回值0) 出错提示; 退出; if(命名该套接字返回值0) 出错提示; 退出; for( ; ; ) 发送数据报(请求)给服务器; 接收服务器应答; 关闭套接字; 6.5 6.5 基于客户基于客户/ /服务器模式的网络编程服务器模式的网络编程1 客户/服务器的工作流程 (1) 必须使用标准函数库实现系统调用,以陷入内核获得完成用户任务所需要的系统软、硬件资源; (2) 系统

38、调用都被内核入口点system_call函数截获,该函数根据所传递的参数确定应该执行哪些系统调用,并通过检查系统调用表,以确定相应的服务例程。最后把控制权转给该服务例程; (3) 该服务过程立即和相关的内核代码模块建立联系。这些模块可能进一步需要和其他内核模块或者底层硬件通讯; (4) 当系统调用结束后,结果按照相同的路径反方向返回。核心把控制交给用户程序; (5) 如果该实例有某些错误的操作,如用户堆栈溢出,处理器将引发一个异常通知内核,有内核执行相应的处理程序来处理异常事件。客户端服务器端1.解析域名生成服务器请求 2. 计算并显示服务内容和图像buffet alloctedsocket

39、buffer客户程序系统调用 connect() recv() send() fputs()tcp/ip协议内核nic缓冲网卡1.解析域名生成服务器请求 2. 计算并显示服务内容和图像buffet alloctedsocket buffertcp/ip协议nic缓冲accept() recv() send() open() read()文件系统ramordisk服务器程序内核网卡httptcpip网络协议internet操作系统支持web服务器的工作流程1 客户/服务器的工作流程 在客户端,如果运行的是web测览器,当向web浏览器的“地址”窗口处输入http:/.或者“www.”等形式的网址

40、并回车,就是由web浏览器向web服务器发出的一次客户请求。该请求经过解析后,通过系统调用由用户态转为核心态执行。在核心态操作系统中的tcp/ip协议代码和网卡驱动程序控制网卡把请求发送到相应的网络上,等待web服务器响应。当服务响应返回时,由网卡接收,并通过内核传送给客户程序。 在服务器端,内核通过网卡从网络上接收web请求,并通过系统调用传递给web服务器。web服务器根据此服务请求执行相应的服务过程,并由内核把结果放到网络上传送给客户。 web浏览器和web服务器使用url和url connection进行网络通信,这是一种较高层次的网络通信,为的是访问internet上的资源。通常,用

41、户也常常需要编写一个由操作系统提供的接口客户/服务器应用程序,由套接口实现的客户服务器应用程序是低级别的网络通信。 因此,从程序员的观点来看,操作系统所提供的系统调用定义了应用程序和协议栈之间的接口。应用程序无论使用哪种通信协议进行网络通信,都必须申请同操作系统交互才能得到服务。#include#include#include#include#include#include#include#includeint main(int argc,char *argv) int sockfd,numb, port = 8000; struct sockaddr_in s; struct hostent

42、 *host; char buf100; if(argc!=2) fprintf(stderr,usage:%s hostnamen,argv0); exit(1); if(!(host=gethostbyname(argv1) perror(error in resolving hostname); exit(1); sockfd=socket(af_inet,sock_stream,0); if(sockfdh_addr)-s_addr; s.sin_port=htons(port); if(connect(sockfd,(struct sockaddr*)&s,sizeof(s)

43、!=0) perror(connect); exit(1); printf(get connected!n); getchar(); strcpy(buf,give me the first file of the presidents office); if(send(sockfd,buf,strlen(buf),0)0) perror(send); exit(1); numb=recv(sockfd,buf,100,0); if(numb0) perror(recv); exit(1); bufnumb=0; printf(received=%sn,buf); close(sockfd);

44、 return 0; 在这个例子中,客户程序向服务器发出请求,建立与服务器的 tcp连接,并发送服务请求字符串“give me the first file of presidents offic”。 在client1.c客户程序中,先调用socket()函数创建一个套接口,然后调用connect()函数数建立与主机的连接。当connect()函数返回时,再通过调用send()函数向服务器发出请求或数据。接下来执行recv()函数等待接收服务器发回的响应。 下面的程序清单给出的是服务器程序server1.c。服务器程序创建一个永久套接口来侦听服务请求。当接收到客户的服务请求时,它将创建一个临时

45、套接口,传回一个字符串,然后将关闭临时套接口,循环等待下一个客户请求的到来。为了不影响前台其他程序的运行,可以把服务器程序写成一个守护进程(daemon),守护进程启动后一直运行在后台,监听客户的请求,从不退出,除非显式地将它关闭。#include#include#include#include#include#include#include#include#include #include int port=8000;void init_daemon(void) pid_t pid; int i; if (pid = fork()=-1) /*生成第1个子进程*/ exit(1); /* f

46、ork失败退出 */ if (pid 0) exit(0); /* 父进程退出,使shell成为前台进程*/ setsid(); /* 第1子进程成为新会话和新进程组的领头进程的同时也失去控制终端*/ /* 第1子进程执行下面的操作 */ for(i=0;inofile;+i) close(i); /* 关闭已打开的文件描述符 */ chdir(/rundir); /*改变当前运行的目录*/ umask(0); /*改变文件创建掩码*/ return;int main(int ac,char* av) int sockfd, tsockfd, addr_size; char buf100; s

47、truct sockaddr_in s, p; char temp_buf256; init_daemon(); sockfd=socket(af_inet,sock_stream,0); if(sockfd=-1) perror(socket building failure); exit(1); bzero(&s, sizeof(s); s.sin_family = af_inet; s.sin_addr.s_addr = inaddr_any; s.sin_port = htons(port); if (bind(sockfd, (struct sockaddr *)&s

48、, sizeof(s) = -1) perror(bind); exit(1); if(listen(sockfd,10)=-1) perror(listen); exit(1); for(;) tsockfd=accept(sockfd,(struct sockaddr*)&p,&addr_size); if(tsockfd0)printf(ft:%dn,errno); perror(accept); exit(1); if(recv(tsockfd,buf,100,0)0) perror(recv); exit(1); strcpy(temp_buf,here is the

49、 first file of the presidents office.); if(send(tsockfd,temp_buf,strlen(temp_buf),0)0) perror(send); exit(1); close(tsockfd); printf(send:%s,temp_buf); 在这个服务器程序的例子中,首先执行socket()系统调用,让内核创建一个永久的接口,以监听服务器请求。然后调用bind()函数将刚创建的套接口与众所周知的端口号8000连在一起。接着调用listen()函数将永久套接口变成一个被动套接口,开始监听来自客户机的连接请求。当函数返回时,程序将进入一

50、个无限循环,并阻塞在accept()系统调用上,等待连接请求的接入。一旦有接入请求,accept()马上返回一个临时套接口供服务器读取用户的请求数据。然后将用户需要的数据返回给客户。读取用户数据的系统调用是 recv()函数,发送数据的系统调用是send()函数。 网络上数据的传输方向是双向的。一个流向是把数据从图l6a的用户层传输到网卡,另一个流向是把数据从网卡传输到用户层。无论流向哪一方,中间都要穿过内核层。 下面给出编译并运行这两个程序的操作过程: $gcc -o ciient1 clientl.c $gcc -o server1 server1.c 操作如下: $./server1 $

51、 ./client1 localhost get connected!(回车) received=here is the first file of the presidents office3 客户/服务器程序的编程实现流程实现流程 下图是基本的面向连接的client/sever系统调用流程。 在客户端编程的基本步骤是: 创建套接字;利用创建的套接字向服务器发出连接请求,启动tcp的三次握手;如果连接成功,双方互操作开始。 服务器端通信软件的步骤是:创建套接字;在建立的套接字中绑定本机ip及一个周知的端口号;在该套接字进行监听;如收到连接请求,则启动与客户的连接;如果连接成功,双方互操作开始

52、。 tcp服务器socket ( )数据(请求)服务请求tcp三次握手建立连接子进程响应服务bind ( )listen ( )accept ( )处理fork ( )派生子进程send ( )tcp客户socket ( )connect ( )recv ( )send ( )send ( )recv ( )close ( )父进程数据(应答)close ( )文件结束通知close ( )子进程发现客户已关闭3 tcp3 tcp连接的建立和终止过程连接的建立和终止过程 tcp传输控制协议提供客户与服务器的连接。一个tcp客户与一个tcp服务器可以建立一个可靠的连接,并通过这个连接通路与服务器

53、交换数据,通信结束后终止连接。 (1) 建立tcp连接的过程 建立一个tcp连接,必须首先启动服务器。服务器程序通过调用函数socket()、bind()和listen(),事先做好接收客户的连接请求的准备。这个过程称为被动打开。稍后启动客户机程序,它调用函数socket()创建一个用于连接的套接口,然后调用函数connect()执行连接请求,从而引发了客户机与服务器之间一个tcp连接的建立过程,调用函数connect()的过程称为主动打开。下图给出了建立tcp连接的示意图。 客户机socket()accept()(阻塞)accept()(返回)socket()bind()listen()co

54、nnect()(阻塞)connect()(返回)read() (阻塞)服务器syn bsyn d ackb+1 ack d+1建立tcp连接的三次握手3 tcp3 tcp连接的建立和终止过程连接的建立和终止过程 建立一个tcp连接需要三路握手信号: (l) 客户机通过调用connect()执行,首先向服务器发送一个syn(同步序列号标志)分节。这里所说的分节是tcp传递给ip的数据单元。一个syn分节一般不携带数据,它只含有一个ip头部和一个tcp头部,还含有一些tcp的选项。这些选项告诉服务器在本次连接中发送数据的初始序列号。图1中给出的客户机初始序列号是b。 (2) 服务器在收到客户发来的

55、syn分节以后,必须给客户机一个回答信号,确认客户的syn分节。与此同时,服务器也要向客户机发送一个syn分节,通知客户机服务器将在这次连接中发送数据的初始序列号d。 (3) 在收到服务器发来的syn分节和回答信号后,客户也必须回答确认服务器的syn分节。 由于syn只占一个字节的序列号空间,所以客户、服务器通信双方的ack回答的确认序列号都是由所接收到对方的序列号加1得到。2 2 tcp tcp连接的建立和终止过程连接的建立和终止过程 (2) 终止 tcp连接的过程用户可以显式地调用close()函数来关闭一个tcp连接。关闭一个tcp连接需要4个分节。 关闭连接也有主动关闭和被动关闭之分。

56、先调用close()函数的一端称为主动关闭。当close执行时,发送一个fin(完成标志)分节,通知对方数据发送完毕。 fin是finish flap的缩写,它也是tcp分节之一。 接收fin分节的另一端称为被动关闭。fin分节作为文件结束符由另一端的read()函数来接收。因为接收到fin分节就意味着另一端的进程在此连接上再也不会接收到数据了,所以read()返回0。但这个fin分节是由被动关闭一端的tcp来确认。 read()函数返回后,应用进程也将调用close()函数来关闭相应的套接口。close()函数执行时,也会向对方发送一个fin分节,并由主动关闭一端的tcp对其进行确认。图2给

57、出关闭套接口时分组的交换过程,总共需要交换4个分组。客户机close()close()read() (阻塞)服务器fin xack x-1 tcp关闭连接时的分组交换过程fin zack z-13 tcp3 tcp连接的建立和终止过程连接的建立和终止过程 从tcp连接的建立和终止过程可以看出,如果在tcp协议的一次通信中,只发送一个单一分节的请求和接收一个单一分节的应答,则需要7个分节的额外开销。如果使用udp,只有“请求”和“应答”两个分组需要交换,但是udp不提供可靠的数据包传送,它把传输层的许多处理推给udp的应用程序。尽管如此,还是有许多应用程序使用udp协议,因为它没有tcp建立连接

58、和终止连接的额外开销。 4 编制编制tcp客户程序客户程序 客户机应用程序有几个特点:首先,客户程序一般不要求处理并发性,因为在同一时刻一个客户程序只能与一个服务器进行交互。第二,客户程序的执行不需要特定的保护,它和普通的应用程序一样执行,不需要特权。鉴于以上两点,可知设计和编写客户程序比较容易。 (1) tcp(1) tcp客户程序设计方法客户程序设计方法 设计一个tcp客户程序步骤如下: 1) 确定与之通信的服务器的ip地址和端口号。 2) 调用socket()函数创建一个tcp套接口。 3) 为这次连接分配一个在本地机器中的、未使用的协议端口号。 4) 调用connect()函数建立与服

59、务器的连接。 5) 用输人和输出函数进行通信。 6) 调用close()关闭连接。 4 编制编制tcp客户程序客户程序 (2) tcp(2) tcp客户程序的实现客户程序的实现 以下给出了一个客户程序的例子clientc,以便了解编制客户程序应该具有的基本语句和算法。在这个程序中,客户建立与服务器的tcp连接并向服务器发送一条消息询问当前时间,然后调用read()函数等待读取服务器送回的当前时间和日期。 #include #include #include #include #include char * host_name = ; /* local host */int p

60、ort = 8000;void main(int argc, char *argv) char buf8192, message256; int sockfd, n; struct sockaddr_in pin; struct hostent *shost_name; if (shost_name = gethostbyname(host_name) = 0) /*域名转换成主机地址*/ perror(error resolving local hostn); exit(1); /* 首先,客户机必须知道服务器主机首先,客户机必须知道服务器主机ip的地址,如果使用同一台计算机作为的地址,如果使用同一台计算机作为服务器运行,则本地计算机的标准服务器运

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论