计算机网络通信、线程、tcp、udp通信及信号量等读书笔记

2023-04-26,,

一、计算机网络
 1、什么是计算机网络:把分布在不同地理位置的计算机与专门的网络设备用通信线路互相连成一个规模大、功能强的系统,从而使众多计算机可以方便地互相传递信息、共享软件、硬件、数据信息等。简单来说,计算机网络就是由通信线路互相连接的许多自主工作的计算机构成的的集合,它是计算机技术和通信技术相结合的产物。
 
 2、计算机网络的功能
  a、数据通信
  b、资源共享
  c、提高系统的可靠性
  d、分布式网络处理和负载均衡
 3、计算机网络的组成
  1、通信子网:由网卡、线缆、集线器、中继器、交换机、路由器等。
  2、资源子网:网络中的计算机、打印机等一些可以提供服务的设备。
  3、计算机网络软件
   协议软件:它规定了计算机之间通信的准则,TCP/IP协议簇。
   网络通信软件:网络中实现计算机与设备之间通信的软件。
   网络操作系统:可以提供网络服务的计算机操作系统,
    Windows server2008、UNIX、Linux
   网络管理软件和网络应用软件
    防火墙、SELinux
    浏览器、迅雷、ftp客户端
 4、计算机网络的分类
  1、按网络的作用范围划分
   局域网、城域网、广域网
  2、按网络传播技术划分
   广播式网络
   点到点网络
  3、传输介质划分
   有线网、无线网、微波通信、卫星通信
 5、计算机网络的拓扑结构
  1、星型拓扑结构
  2、树型拓扑结构
  3、总线型拓扑结构
  4、环型拓扑结构
  5、网状型拓扑结构
 6、计算机网络的发展过程
  1、以计算机为中心的联机系统
  2、分组交换网络的诞生
  3、网络体系结构与协议标准化。
   20世纪80年代 ISO组织提出 开放式系统互联参考模型OSI,由于这个模型照顾到了各方和利益所以太过庞大,因此至今没有推出成熟的产品,TCP/IP是一套符合OSI标准的协议。
   异构:windows C/C++->socket->TCP/IP ->TCP/IP ->socket->Linux JAVA
  4、高速计算机网络
 7、网卡:网络接口卡或网络适配器、它负责将数据发送网络中去,也负责从网络中获取数据,每个网卡上会有一个独一无二MAC地址。
 
 8、OSI/RM结构与TCP/IP模型
  网络协议是为网络数据交换而制定的规则、约定、标准, 一个功能完备的计算机网络需要制定一整套复杂的协议集,网络协议是按层次结构来组织的,网络层次结构模型与各层协议的集合称为网络体系结构。
  OSI/RM结构七层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
  TCP/IP模型四层:
   物理层:负责通信网络收发数据包
   网络层:选择、流量控制、与网络拥塞问题,IP协议是该层核心。
   传输层:机器之间建立用于会话的端到端连接(用于数据的传输),该层的核心协议是TCP、UDP协议。
   应用层:主要为用户提供针对性的服务,该层代表性的协议有:HTTP、SMTP、FTP、TELNET。
  
 9、ipv4地址
  在计算机网络中的每一台计算机都必须有一个唯一的标识符它就是ip地址。目前由4个不超过255的整数组成,一般用点分十进制表示(192.168.2.180)。
  ip地址的分类:
   A类:第一个二进制位必须是0
    0.0.0.0
    127.255.255.255
   B类:前两位的二进制位必须是10
    128.0.0.0
    191.255.255.255
   C类:前三位的二进制位必须是110
    192.0.0.0
    223.255.255.255
   D类:前四位的二进制位必须是1110
    224.0.0.0
    239.255.255.255
   E类:前四位的二进制位必须是1111
    240.0.0.0
    255.255.255.255
 10、公有ip和私有ip
  公有ip:在网络服务提供商登记过的ip地址叫公有ip
  私有ip:由一此公司或组织自己分配的,不能在网络公开直接访问的ip。
 11、子网掩码:目前由4个不超过255的整数组成,一般用点分十进制表示(255.255.255.0)。
  子网掩码&ip地址 = 网络地址
  如果两人个ip地址的网络地址相同,那么说明它们在同一个子网内,就可以直接通信而不需要路由。
 12、网关地址:负责子网出口的计算机,一般由路由器提任(路由器就是一台具有路由功能的计算机)。
 13、端口号:操作系统为需要通信的进程分配一个独一无二的编号,端口号可能确定与哪个进程来通信。
  1~1024基本上已经被操作系统占用,一般编程要使用1024以上的。
  http:80
  ftp:21
  telnet:23
二、套接字(socket:插座)
 是一种可以进行网络通信的内核对象,它有一个唯一的标识符,一般称它为socket描述符,跟文件描述符类似,也可以用read/write/close操作。
 
 int socket(int domain, int type, int protocol);
 功能:创建socket对象
 domain:通信地址类型
  AF_UNIX/AF_LOCAL:本地进程间通信
  AF_INET:使用ipv4地址通信
  AF_INET6:使用ip6地址通信
 type
  SOCK_STREAM:数据流协议,TCP面向连接的通信协议
   优点是安全可靠,数据不会丢失,但速度慢。
   一般常用于安全性较高的场景。
  SOCK_DGRAM:数据报协议,UDP面向无连接的通信协议
   优点是速度快,数据可能丢失,安全性和可靠性与tcp相比不同。
   一般用于安全性要求不高但是对速度有要求的场景。
 protocol:特殊协议,一般不使用,直接写0。

 准备通信地址:
基本的通信地址
struct sockaddr {
sa_family_t sa_family;
char sa_data[];
} 本地通信地址
struct sockaddr_un
{
// 通信地址类型
sun_family_t sun_family;
// socket文件的路径
char sun_path[];
}; 网络通信地址
struct sockaddr_in
{
// 通信地址类型
short int sin_family;
// 端口号
in_port_t sin_port;
// ip地址
struct in_addr sin_addr;
}

  
三、本地socket进程间通信
 A(B的地址+消息内容)-》B
 B(A的地址+消息内容)-》A
 进程A:创建socket->准备地址->绑定->连接>接收/发送->关闭socket->删除socket
 进程B:创建socket->准备地址->连接->绑定->发送/接收->关闭
 进程B可以给进程A发送消息,进程A也可以给进程B发消息。
 一个socket对象只能绑定一个地址。
 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
 功能:把socket对象与通信地址建立联系
 
 int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
 功能:连接通信目标
四、网络通信(UDP)
 网络通信地址
        struct sockaddr_in
  {
   // 通信地址类型
   short  int sin_family;
   // 端口号
   in_port_t sin_port;
   // ip地址
   struct in_addr sin_addr;
  }
  
 个人计算机系统数据的存储方式可能是大端,也可能是小端,网络通信时需要的是大端数据,必须把数据转换成大端。
    uint32_t htonl(uint32_t hostlong);
    功能:把32位的主机字节序转换成32位网络字节序
   
       uint16_t htons(uint16_t hostshort);
       功能:把16位的主机字节序转换成16位网络字节序
      
       uint32_t ntohl(uint32_t netlong);
       功能:把32位网络字节序转换成32位的主机字节序
      
       uint16_t ntohs(uint16_t netshort);
       功能:把16位网络字节序转换成16位的主机字节序
      
 生成端口号:
  端口号就是一个16位的无符整数因此:
  uint16_t htons(uint16_t hostshort);
  
 生成ip地址:
  in_addr_t inet_addr(const char *cp);
  功能:把点分十进制的字符串ip地址转换成32位的无符号整数
  char *inet_ntoa(struct in_addr in);
  功能:把32的的网络字节序的ip地址转换成点分十进制的字符串ip地址。
  
 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
 功能:接收数据并获取发送端的地址
 
 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
 功能:发送数据到指定的目标
 
 进程A:创建socket对象->准备地址->绑定->接收数据和来时的地址->原路返回数据->关闭socket。
 
 进程A:创建socket对象->准备地址->向目标发送数据->接收数据->关闭socket。
 
 当socket对象被全部关闭,会在内核中停留一段时间(会给重新连接的机会),如果再使用同样的ip地址和端口号时就会失败(延时关闭)。

一、基本概念
 进程中的资源有哪些:代码的指令、只读段、全局段、静态数据段、堆、栈、命令行参数、环境变量表、执行者(线程)。
 什么线程:在进程中负责执行代码的一个单位,它是进程的一部分,一个进程至少要一个线程(主线程),进程也可以有多个线程(创建)。
 进程中的代码的指令、只读段、全局段、静态数据段、堆、命令行参数、环境变量表、文件描述符、信号处理函数等资源对线程来说都是共享,线程之间栈空间是私有的。
 
 线程是进程的一个实体,是操作系统独立调度和分派任务的基本单位。
 
二、POSIX线程
 UNIX和Linux天生骄傲(不支持线程),通过添加额外的线程库可以使用,在编译多线程代码时需要添加 -lpthread,pthread.h。
 
 对线程的操作:
  创建线程
  销毁线程
  分离线程
  联合线程
  查询线程属性
  设置线程属性
  
 对于线程来说最要的是解决脏数据问题(线程同步),对于进程来说是解决通信(IPC)问题。
 
三、创建线程
 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
 功能:创建线程
 thread:返回值,获取线程id
 attr:返回值,获取线程属性
 start_routine:参数,注册线程的入口函数
 arg:给线程入口函数的参数
 
 1、同一个进程的多个线程都在同一个地址空间内活动,因此相对于进程,线程的系统开销小,任务切换快,它们可以执行相同的代码,也可以执行不同的代码。
 
 2、线程间的数据交换不需要依赖于类似IPC的特殊通信机制,简单而高效,每个线程拥有自己独立的线程ID、寄存器信息、函数栈、错误码和信号掩码,线程之间存在优先级的差异。
 
 3、main函数即主线程,main函数返回即主线程结束,主线程结束即进程结束,进程一但结束其所有的线程即结束。
 4、应设法保证在线程过程函数执行期间,其参数所指向的目标持久有效。
 
四、对线程的操作
 1、等待线程
  int pthread_join (pthread_t thread, void** retval);
  功能:等待thread参数所标识的线程结束
  retval:返回值,线程入口函数的返回值
  返回值:成功返回0,失败返回错误码。
  
  线程过程函数将所需返回的内容放在一块内存中,返回该内存的地址,要保证这块内存在函数返回后,即线程结束,以后依然有效;
  若retval参数非NULL,则pthread_join函数将线程过程函数所返回的指针,拷贝到该参数所指向的内存中;
  若线程过程函数所返回的指针指向动态分配的内存,则还需保证在用过该内存之后释放之。
  
 2、获取线程id
  pthread_t pthread_self (void);
  功能:返回当前线程的id,此函数不会执行失败。
 
 3、比较两个线程
  int pthread_equal (pthread_t t1, pthread_t t2);
  功能:比较两个id是否是同一个线程
  返回值:两个id相等,则返回非零,否则返回0。
  某些实现的pthread_t不是unsigned long int类型,可能是结构体类型,无法通过“==”判断其相等性。
  
 4、终止线程
  1) 从线程过程函数中return。
  2) 调用pthread_exit函数。
  void pthread_exit (void* retval);
  retval和线程过程函数的返回值语义相同。
  
 5、线程的执行轨迹
  1) 同步方式(非分离状态):
  创建线程之后调用pthread_join函数等待其终止,并释放线程资源。
  2) 异步方式(分离状态):
  无需创建者等待,线程终止后自行释放资源。
  int pthread_detach (pthread_t thread);
  功能:使线程进入分离(DETACHED)状态。
  返回值:成功返回0,失败返回错误码。
  处于分离状态的线程终止后自动释放线程资源,且不能被pthread_join函数等待。
 6、取消线程
  1) 向指定线程发送取消请求
  int pthread_cancel (pthread_t thread);
​  成功返回0,失败返回错误码。
​  注意:只是向线程发出取消请求,并不等待线程终止。

  缺省情况下,线程在收到取消请求以后,并不会立即终止,而是仍继续运行,直到其达到某个取消点。
  在取消点处,线程检查其自身是否已被取消了,并做出相应动作。
  当线程调用一些特定函数时,取消点会出现。
  
  2) 设置调用线程的可取消状态
  int pthread_setcancelstate (int state,int* oldstate);
  成功返回0,并通过oldstate参数输出原可取消状态(若非NULL),失败返回错误码。

  state取值:
      PTHREAD_CANCEL_ENABLE:接受取消请求
      PTHREAD_CANCEL_DISABLE:忽略取消请求。
      
  3) 设置调用线程的可取消类型
  int pthread_setcanceltype (int type, int* oldtype);

  成功返回0,并通过oldtype参数输出原可取消类型(若非NULL),失败返回错误码。

  type取值:
     PTHREAD_CANCEL_DEFERRED延迟取消(缺省)。
       被取消线程在接收到取消请求之后并不立即响应,
        而是一直等到执行了特定的函数(取消点)之后再响应该请求。
     PTHREAD_CANCEL_ASYNCHRONOUS - 异步取消。
       被取消线程可以在任意时间取消,不是非得遇到取消点才能被取消。
        但是操作系统并不能保证这一点。
 

一、竞争与同步
 当多个线程同时访问其所共享的进程资源时,需要相互协调,以防止出现数据不一致、不完整的问题。这就叫线程同步。
 
二、互斥量、

 int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
功能:初始化互斥量
//亦可 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int pthread_mutex_lock (pthread_mutex_t* mutex);
功能:加锁

int pthread_mutex_unlock (pthread_mutex_t* mutex);
功能:解锁

int pthread_mutex_destroy (pthread_mutex_t* mutex);
功能:销毁互斥量

1) 互斥量被初始化为非锁定状态;
 2) 线程1调用pthread_mutex_lock函数,立即返回,互斥量呈锁定状态;
 3) 线程2调用pthread_mutex_lock函数,阻塞等待;
 4) 线程1调用pthread_mutex_unlock函数,互斥量呈非锁定状态;
 5) 线程2被唤醒,从pthread_mutex_lock函数中返回,互斥量呈锁定状态;
 
三、信号量
 信号量是一个计数器,用于控制访问有限共享资源的线程数。
 注意:线程使用的信号量不在pthread.h中,而是semaphore.h

 // 创建信号量
int sem_init (sem_t* sem, int pshared,unsigned int value);
sem - 信号量ID,输出。
​ pshared - 一般取0,表示调用进程的信号量。
非0表示该信号量可以共享内存的方式,
为多个进程所共享(Linux暂不支持)。
value - 信号量初值。

// 信号量减1,不够减即阻塞
int sem_wait (sem_t* sem);

// 信号量减1,不够减即返回-1,errno为EAGAIN
int sem_trywait (sem_t* sem);

// 信号量减1,不够减即阻塞,
// 直到abs_timeout超时返回-1,errno为ETIMEDOUT
int sem_timedwait (sem_t* sem,const struct timespec* abs_timeout);
struct timespec {
time_t tv_sec; // Seconds
long tv_nsec; // Nanoseconds [0 - 999999999]
};

// 信号量加1
int sem_post (sem_t* sem);

// 销毁信号量
int sem_destroy (sem_t* sem);

练习:指针图书馆有5本《大师兄语录》,创建20个线程,每个线程去借阅这本书的阅读时间(0~10)然后还书
 
四、死锁
 使用两把锁保护一个资源。
 创建:锁A、锁B
 线程A 线程B
 加锁A 加锁B
 s1  s1
 加锁B 加锁A
 解锁A 解锁B
 解锁B  解锁A
 
 练习:使用互斥量实现一个死锁程序,思考如何避免死锁。
  不要连续的加锁。
 
五、生产者与消费者模型
 一线程负责生产数据,另一部分线程负责消费数据。
 问题1:如何生产的快、消费的慢,生产者容易撑死
 问题2:如果生产的慢、消费的快,消费者容易饿死
 只有把问题1、和问题2协调好,才能最大限度的提高效率。
 
 生产者快-》数据池满-》生产者暂停-》消费者全部开始消费-》数据池空-》消费者暂停-》生产者全部开始生产
 
六、条件变量

 条件变量可以让调用线程在满足特定条件的情况下暂停。
int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr); //亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 使调用线程睡入条件变量cond,同时释放互斥锁mutex
int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);

// 使调用线程睡入条件变量cond,同时释放互斥锁mutex,并在时间到了之后即使没有被唤醒,也醒过来
int pthread_cond_timedwait (pthread_cond_t* cond,
pthread_mutex_t* mutex,
const struct timespec* abstime);

struct timespec {
time_t tv_sec; // Seconds
long tv_nsec; // Nanoseconds [0 - 999999999]
};

// 从条件变量cond中唤出一个线程,
// 令其重新获得原先的互斥锁
int pthread_cond_signal (pthread_cond_t* cond);
注意:被唤出的线程此刻将从pthread_cond_wait函数中返回,
但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。

// 从条件变量cond中唤出所有线程
int pthread_cond_broadcast (pthread_cond_t* cond);

// 销毁条件变量
int pthread_cond_destroy (pthread_cond_t* cond);

计算机网络通信、线程、tcp、udp通信及信号量等读书笔记的相关教程结束。

《计算机网络通信、线程、tcp、udp通信及信号量等读书笔记.doc》

下载本文的Word格式文档,以方便收藏与打印。