Unix 网络编程(2)——TCP API

TCP C/S套接口函数一般调用过程及基本函数

Unix 网络编程(2)——TCP API

如上图所示的TCP连接的基本过程。一般来说,服务器先于客户端运行,服务器程序运行的基本过程是:

  1. socket()函数创建服务器段socket。
  2. bind()函数进行端口绑定,或者不调用bind()函数而在调用listen()或者connect()函数时,内核为套接口分配一个临时端口。
  3. listen()函数进行监听。
  4. accept()函数在接受客户端的连接请求后,创建一个所谓的已连接socket,这个已连接socket在双方连接过程中存在,当服务完成之后就被关闭。

以上是连接建立的过程中服务器的流程,而客户端有一些不同:

  1. 同样的在客户端创建socket。
  2. 调用connect函数尝试连接服务器。如connect失败,则该套接口不在可用必须关闭,重新调用socket()。

fork和并发服务器

#include <sys/types.h>
#include <unistd.h>
/*
功能:复制进程
参数:无
返回值:  成功:  父进程:返回子进程id
                子进程:返回0
         失败:  返回-1
*/
pid_t fork(void);

由fork创建的新进程被称为子进程(child process)。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程 id。将子进程id返回给父进程的理由是:因为一个进程的子进程可以多于一个,没有一个函数使一个进程可以获得其所有子进程的进程id。 对子进程来说,之所以fork返回0给它,是因为它随时可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。(进程id 0总是由交换进程使用,所以一个子进程的进程id不可能为0 )。

fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置(两进程的程序计数器pc值相同,也就是说,子进程是从fork返回处开始执行的),但有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。

服务器在服务多用户时,可用fork子进程来服务每一个客户。并发服务器的典型过程是:

  1. 服务器创建监听socket。
  2. 绑定端口bind()并监听。
  3. 父进程调用accept()函数。
  4. fork子进程。此时fork的子进程可以共享之前父进程拥有的所有资源,当然也包括accept返回的已连接socket。
  5. 子进程关闭监听socket。注意:此处子进程关闭的监听socket是从父进程共享而来,因为子进程要负责自己的任务,不再接纳其它。子进程创建之后监听socket的引用计数为2,那么此时子进程关闭监听socket只是让该socket的引用计数减1,并不会真正关闭,因为父进程还需要使用这个监听socket。
  6. 父进程关闭已连接socket。和监听socket一样,此时的已连接socket也只是计数器减1,相当于父进程把连接全权交给了子进程(之前相当于是一对二的关系)。
  7. 子进程完成任务。
  8. 子进程关闭已连接socket。子进程完成任务之后关闭已连接socket,这时便真正关闭socket了。