如上图所示的TCP链接的基本过程。通常来讲,服务器先于客户端运行,服务器程序运行的基本过程是:服务器
- socket()函数建立服务器段socket。
- bind()函数进行端口绑定,或者不调用bind()函数而在调用listen()或者connect()函数时,内核为套接口分配一个临时端口。
- listen()函数进行监听。
- accept()函数在接受客户端的链接请求后,建立一个所谓的已链接socket,这个已链接socket在双方链接过程当中存在,当服务完成以后就被关闭。
以上是链接创建的过程当中服务器的流程,而客户端有一些不一样:并发
- 一样的在客户端建立socket。
- 调用connect函数尝试链接服务器。如connect失败,则该套接口不在可用必须关闭,从新调用socket()。
#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 )。socket
fork以后,操做系统会复制一个与父进程彻底相同的子进程,虽然说是父子关系,可是在操做系统看来,他们更像兄弟关系,这2个进程共享代码空间,可是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也彻底相同,子进程拥有父进程当前运行到的位置(两进程的程序计数器pc值相同,也就是说,子进程是从fork返回处开始执行的),但有一点不一样,若是fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,若是fork不成功,父进程会返回错误。函数
服务器在服务多用户时,可用fork子进程来服务每个客户。并发服务器的典型过程是:spa
- 服务器建立监听socket。
- 绑定端口bind()并监听。
- 父进程调用accept()函数。
- fork子进程。此时fork的子进程能够共享以前父进程拥有的全部资源,固然也包括accept返回的已链接socket。
- 子进程关闭监听socket。注意:此处子进程关闭的监听socket是从父进程共享而来,由于子进程要负责本身的任务,再也不接纳其它。子进程建立以后监听socket的引用计数为2,那么此时子进程关闭监听socket只是让该socket的引用计数减1,并不会真正关闭,由于父进程还须要使用这个监听socket。
- 父进程关闭已链接socket。和监听socket同样,此时的已链接socket也只是计数器减1,至关于父进程把链接全权交给了子进程(以前至关因而一对二的关系)。
- 子进程完成任务。
- 子进程关闭已链接socket。子进程完成任务以后关闭已链接socket,这时便真正关闭socket了。