TCPIP网络编程小结

一、TCP/IP网络通信原理

1.1 TCP/IP参考模型

  1. 网络接口层
    • 负责接收从网络层交来的IP数据报并通过低层物理网络发送出去。或者从低层物理网络上接收物理帧并从中抽出IP数据报交给网络层。
  2. 网络层
    • 负责将源主机的报文分组发送到目的主机,源主机与目的主机可以在一个网络上,也可以在不同的网络上。
    • 网络层协议的IP协议,IP协议是一种不可靠的、无连接的数据包传送服务的协议,提供尽力而为的服务
    • IP协议的协议数据单元是IP分组(也称为IP数据报)
  3. 传输层
    1. TCP协议
      • 可靠的面向连接的协议,允许将一台主机的字节流无差错的传送到目的主机
      • TCP协议将应用层的字节流分成多个字节段,然后将一个个的字节段传送到网络层,并最终发送到目的主机
    2. UDP协议
      • 不可靠的无连接协议,用于不要求分组顺序到达的传输服务之中,分组的传输顺序检查与排序由应用层完成。
  4. 应用层
    • 网络终端协议Telnet
    • 文件传输协议FTP
    • 域名系统DNS
    • 超文本传输协议HTTP

1.2 TCP/IP参考模型通信原理

传输层需要解决在网络环境中分布式进程通信所需解决的问题:

  1. 进程命名与寻址
    1. 同一台计算机中,每一个进程被分配了一个唯一的端口和端口号。端口是一个信息缓存区,用于保留socket中的输入输出信息。端口号是16位无符号整数,范围是0-65535,用于区别不同的应用程序进程
    2. 端口分配方式有两种
      1. 全局分配
        • 由公认的权威中央机构根据用户的需要进行统一分配,并将结果公之于众,这样的端口号被称为熟知端口号
      2. 本地分配
        • 当进程需要访问传输层服务时,向本地操作系统提出申请,再有操作系统返回本地唯一的端口号
  2. 多重协议识别
    • 一个应用进程最终需要用一个三元组<协议、本地地址、本地端口号>来唯一标识,称为一个半相关
    • 五元组<协议、本地地址、本地端口号、远程地址、远程端口号>称为一个相关

1.3 Linux网络通信原理

  • linux操作系统对基本的I/O进行了扩展,使得基本IO系统调用可以操作套接字描述符和文件描述符

二、套接字API

2.1 socket()

int socket(int domain, int type, int protocol);
  • domain:用于指明建立socket所使用的的协议族
  • type:用于指定创建该socket的应用程序所希望采用的通信服务类型
  • protocol:用于指明该socket所使用的具体协议的协议号
  • 调用socket()函数后,返回一个socket描述符,将建立一个socket,意味着为一个socket数据结构分配了存储空间,返回的socket描述符是一个指向该内部数据结构的指针
  • 面向连接的socket客户端通过执行connect(),在socket数据结构中保存本地和远端信息
  • 面向连接的socket服务端调用bind()来配置本地信息

2.2 connect()

  • 用于配置socket并与远端服务器建立一个TCP连接,只有面向连接的客户程序使用socket时会调用connect将socket与远端主机相连

2.3 bind()

  • 用于将socket与本机的一个端口地址相关联

2.4 listen()

  • 用于使socket处于被动的监听模式,并为该socket建立一个输入数据队列,将到达的客户服务请求保存在此队列中,直到程序处理他们

2.5 accept()

  • 用于从等待连接队列中抽取第一个客户连接请求,建立与客户之间的连接,并为该连接创建一个新的套接字。

三、服务器的并发机制

3.1 基于多进程的服务器并发机制

3.1.1 创建一个进程

  • Linux中,一个现有进程可以调用fork()函数创建一个新进程
  • 父进程和子进程共享代码空间,数据空间互相独立,子进程的数据空间中的内容是父进程的完整拷贝,指令指针也完全相同
  • 子进程从fork()函数返回处开始执行
  • fork成功返回后,子进程的返回值为0,父进程的返回值为子进程的进程ID
  • fork调用不成功,父进程返回值为-1

3.1.2 僵尸进程

3.1.2.1 定义
  • 僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。

  • 一个进程在调用exit()函数结束自己的生命的时候,操作系统内核仍然会在进程表中为其保留一定的信息(包括进程号、退出状态、运行时间等)。由于这类进程已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅继续占用了系统的进程表资源,除此之外不再占有任何的内存空间,因此被称为僵尸进程(Zombie Process)。

  • 在 LINUX中,利用命令ps命令可以看到有标记为Z的进程就是僵尸进程。

3.1.2.2 影响
  • 僵尸进程占用系统的进程表资源,Linux系统对运行的进程数量有限制,如果有过多的僵尸进程占用了可用的进程号,将会导致新的进程无法生成
3.1.2.3 清除
  • 僵尸进程的清除工作一般由其父进程来负责进行。

    • 方法一:父进程可用通过调用wait()或waitpid()等函数来等待子进程结束,从而避免产生僵尸进程。但是会导致父进程被挂起(阻塞等待)
    • 方法二:调用signal()函数为SIGCHLD信号安装handler来避免产生僵尸进程。因为当子进程结束后,内核会发送SIGCHLD信号给其父进程,父进程收到信号后,可以在handler中调用wait()函数来进行回收
    • 方法三:如果父进程不关心子进程何时结束,那么可以通过调用signal ( SICCHLD,SIG_IGN)函数来通知内核,表明自己对子进程的结束不感兴趣,那么子进程结束后将会被内核自动回收,且不会再给父进程发送SICCHLD信号,由此可以避免产生僵尸进程。
    • 方法四:由于当一个父进程死后,其子进程将成为“孤儿进程”,从而会被过继给1号进程init,init是系统中的一个特殊进程,其进程ID为1,主要负责在系统启动时启动各种系统服务及子进程的清理,只要有子进程终止,init就会调用wait函数清理它。
  • wait()

    • 进程一旦调用了wait()函数,就立即阻塞自己,由wait()函数自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait()函数就会收集这个子进程的信息,并把它彻底销毁后返回。如果没有找到这样一个子进程,wait( )函数就会一直阻塞在这里,直到有这样一个子进程出现为止。
    • 如果wait()函数调用成功,将会返回被收集的子进程的进程ID,如果调用失败则返回-1。
  • waitpid()

    • waitpid()的返回值比 wait稍微复杂一些,一共有三种情况:
      • 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
      • 如果设置了选项WNOHANG,而调用中waitpid 发现没有已退出的子进程可收集,则返回0;
      • 如果调用中出错,则返回-1,这时ermo会被设置成相应的值以指示错误所在。

3.2 基于多线程的服务器并发机制

3.2.1 创建一个线程

  • 线程的创建是通过pthread_create()函数来实现的,当创建线程成功时,该函数返回0,若不为0则说明创建线程失败。

  • int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void * arg);
    
  • 若创建线程成功,则新创建的线程将运行由pthread_create()函数中第三个参数和第四个参数所确定的函数,而原来的线程则继续运行下一行代码。

posted @   Cedrus  阅读(266)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示