4.unix网络编程的大体流程

4.unix网络编程的大致流程

在服务器端:

1.首先是调用socket函数创建一个socket,此时返回该socket描述字(前面介绍过)。此时的服务器socket处于closed状态。

 

2.此时socket被建立,但是socket内部除了协议外,IP地址和端口号均由系统指定。因此接下来将会自己声明一个网络套接字(该套接字结构取决于你要使用什么协议,常用的IPV4协议,结构为struct sockaddr_in),该结构在使用之前一般先使用bzero函数清0(你也可以使用memset等其它函数,清0即可)。然后给该结构指定协议族,IP地址(一个机器有几个IP地址,比如127.0.0.1或者内网地址,可能还有外网地址。当你在套接口中指定地址后,别的机器都必须通过这个IP才能访问。举个简单例子,比如说你指定了127.0.0.1,则其它机器只能使用127.0.0.1: 端口号来访问服务器进程,遗憾的是127.0.0.1通过不了路由。以此类推。当你想使用任何地址都可以访问时候,指定IP地址为INADDR_ANY,指定函数前面介绍过)。端口号(服务器一般都是要指定端口)。然后调用bind将你自己定义的地址描述结构绑定到之前定义的socket,此时服务器socket仍然处于closed状态。

 

3.调用listen函数,将该socket设置成被动打开,此时,若客户机socket没有发来建立连接请求,该socket处于listen状态。

 

4.调用accept函数,此时字面理解函数为接受。其实是内核为监听socket创建了2个队列,一个队列未完成队列:这个队列存储的内容是对方发来了3路握手请求,但3路握手尚未接受,链接尚未建立的链接(这么叫不好,不过我想不到好的修饰词)。一个队列就是完成链接队列,意思很明显,就是3路握手结束,链接已经建立。accept就会从已链接队列中取出一个链接,并返回这个链接的描述符,双方可以通过描述符通信。此时socket处于established。若已完成队列没有链接,则该函数会将服务器进程阻塞。

 

5.调用read或者write进行通信。

 

一般服务器不会关闭监听套接口,除非关闭服务器。

 

 

客户端

 

1.第一步通过socket函数建立socket,并返回描述字。closed状态

 

2.(可以省略),和服务器一样为自己socket 指定IP和端口(指定IP没有意义,指定端口表示从指定的端口链接,而不是由内核分配的端口),因为客户端不需要指定,所以一般都不做这一步。

 

3.创建socket描述结构(描述服务器),指定服务器IP和端口,通过connect函数想服务器发出链接请求(此时会触发3路握手)。当函数返回时候表示链接建立(也就是当前链接已经被加入到服务器socket完成链接的队列中),并返回链接描述符。established状态

 

4.调用read或者write进行数据通信。

 

5.关闭链接。进入closed状态。

 

 

 

注:网络通信中read,write,readline不同于通常的文件I/O。当你write大量数据时候,接受端read函数可能只是接受了一部分。这不是错误情况。

TCP中,数据首先放在应用进程缓冲区,然后由write拷贝到了套接口发送缓冲区。当有大批量数据传送时候,比如现在进程缓冲区有10M数据,而套接口只有4M大小,那么 write就会将4M写入,有下面的协议发送。等对法确认取得后,再继续写入,直到10M数据全部写入,明显最后有2M写入,但这2M写入了套接口缓冲区,并不代表对方已经收到。但是write函数已经返回,他返回只是意味着你可以继续使用套接口缓冲区,而不意味对方已经收到。从而缺陷很明显对方先收到4M,明显要少于你发送的10M数据,这时你就要多次调用read函数来获取剩余的数据。

 

UDP中,UDP没有发送缓冲区。但是UDP发送的数据包有上限,所以UDP发送缓冲区大小实际就是UDP数据包的上限,一旦用户发送的数据大于UDP数据包上限,就直接返回错误,UDP本身是不可靠协议,他无需为客户保存数据因此也不需要发送缓冲区。如果客户的数据小于上限,则UDP会将数据传送到下面数据链路层,一旦下层拷贝了数据,UDP就直接丢弃。