当前位置:   article > 正文

UNIX网络编程详解之TCP套接字编程_tcp header unix

tcp header unix

  1. 概述

我们还讲解并发服务器,它是在同时有大量的客户连接到同一服务器上时用于提供并发性的一种常用Unix技术。每个客户连接都迫使服务器为它派生(fork)一个新的进程。本章中我们只考虑使用fork实施的每客户单进程模型,然而当在第26章讨论线程时,我们将考虑称为每客户单线程的另外一种模型。

图4-1给出了在一对TCP客户与服务器进程之间发生的一些典型事件的时间表。服务器首先启动,稍后某个时刻客户启动,它试图连接到服务器。我们假设客户给服务器发送一个请求,服务器处理该请求,并且给客户发回一个响应。这个过程一直持续下去,直到客户关闭连接的客户端,从而给服务器发送一个EOF(文件结束)通知为止。服务器接着也关闭连接的服务器端,然后结束运行或者等待新的客户连接。

  1. socket函数

为了执行网络IO,一个进程必须做的第一件事情就是调用socket函数指定期望的通信协议类型(使用IPv4的TCP、使用IPv6的UDP、Unix域字节流协议等)

其中family参数指明协议族,它是图4-2中所示的某个常值。该参数也往往被称为协议域。type参数指明套接字类型,它是图4-3中所示的某个常值。protocol参数应设为图4-4所示的某个协议类型常值,或者设为0,以选择所给定family和type组合的系统默认值。

并非所有套接字family与type的组合都是有效的,图4-5给出了一些有效的组合和对应的真正协议。其中标为“是”的项也是有效的,但还没有找到便捷的缩略词。而空白项则是无效组合。

对比AF_XXX和PF_XXX

AF_前缀表示地址族,PF_前缀表示协议族。历史上曾有这样的想法:单个协议族可以支持多个地址族,PF_值用来创建套接字,而AF_值用于套接字地址结构。但实际上,支持多个地址族的协议族从来就未实现过,而且头文件<sys/socket.h>中为一给定协议定义的Pr_值总是与此协议的AF_值相等。尽管这种相等关系并不一定永远成立,但若有人试图给已有的协议改变这种约定,则许多现存代码都将崩溃。为与现存代码保持一致,本书中我们仅使用AF_常值,尽管(主要是)在调用socket时我们可能会碰到PF_值。

  1. connect函数

TCP客户用connect函数来建立与TCP服务器的连接。

sockfd是由socket函数返回的套接字描述符,第二个、第三个参数分别是一个指向套接字地址结构的指针和该结构的大小。套接字地址结构必须含有服务器的IP地址和端口号。

  1. bind函数

bind函数把一个本地协议地址赋予一个套接字。对于网际网协议,协议地址是32位的IPv4地址或128位的IPv6地址与16位的TCP或UDP端口号的组合。

第二个参数是一个指向特定于协议的地址结构的指针,第三个参数是该地址结构的长度。对于TCP,调用bind函数可以指定一个端口号,或指定一个IP地址,也可以两者都指定,还可以都不指定。

  1. listen函数

listen函数仅由TCP服务器调用,它做两件事情。

(1)当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字。listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。根据TCP状态转换图(图2-4),调用listen导致套接字从CLOSED状态转换到LISTEN状态。

(2)本函数的第二个参数规定了内核应该为相应套接字排队的最大连接个数。

  1. accept函数

accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接(图4-6)。如果已完成连接队列为空,那么进程被投入睡眠(假定套接字为默认的阻塞方式)。

参数cliaddr和addrlen用来返回已连接的对端进程(客户)的协议地址。addrlen是值-结果参数(3.3节):调用前,我们将由*addrlen所引用的整数值置为由cliaddr所指的套接字地址结构的长度,返回时,该整数值即为由内核存放在该套接字地址结构内的确切字节数。

如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。在讨论accept函数时,我们称它的第一个参数为监听套接字(listening socket)描述符(由socket创建,随后用作bind和listen的第一个参数的描述符),称它的返回值为已连接套接字(connected socket)描述符。区分这两个套接字非常重要。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字(也就是说对于它的TCP三路握手过程已经完成)。当服务器完成对某个给定客户的服务时,相应的已连接套接字就被关闭。

本函数最多返回三个值:一个既可能是新套接字描述符也可能是出错指示的整数、客户进程的协议地址(由cliaddr指针所指〉以及该地址的大小(由addrlen指针所指)。如果我们对返回客户协议地址不感兴趣,那么可以把cliaddr和addrlen均置为空指针。

  1. fork和exec函数

fork在子进程返回0而不是父进程的进程ID的原因在于:任何子进程只有一个父进程,而且子进程总是可以通过调用getppid取得父进程的进程ID。相反,父进程可以有许多子进程,而且无法获取各个子进程的进程ID。如果父进程想要跟踪所有子进程的进程ID,那么它必须记录每次调用fork的返回值。

父进程中调用fork之前打开的所有描述符在fork返回之后由子进程分享。我们将看到网络服务器利用了这个特性:父进程调用accept之后调用fork。所接受的已连接套接字随后就在父进程与子进程之间共享。通常情况下,子进程接着读写这个己连接套接字,父进程则关闭这个已连接套接字。

fork有两个典型用法。

  1. 一个进程创建一个自身的副本,这样每个副本都可以在另一个副本执行其他任务的同时处理各自的某个操作。这是网络服务器的典型用法。

(2)一个进程想要执行另一个程序。既然创建新进程的唯一办法是调用fork,该进程于是首先调用fork创建一个自身的副本,然后其中一个副本(通常为子进程)调用exec把自身替换成新的程序。这是诸如shell之类程序的典型用法。

存放在硬盘上的可执行程序文件能够被Unix执行的唯一方法是:由一个现有进程调用六个exec函数中的某一个。(当这6个函数中是哪一个被调用并不重要时,我们往往把它们统称为exec函数。) exec把当前进程映像替换成新的程序文件,而且该新程序通常从main函数开始执行。进程ID并不改变。我们称调用exec的进程为调用进程(calling process),称新执行的程序为新程序(new program)。

  1. 并发服务器P105

  1. close函数 P108

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/158759
推荐阅读
相关标签
  

闽ICP备14008679号