第一部分开始网络编程
Linux编译基本命令
gcc hello.c -o hello
-o 是用来指定可执行文件名的可选参数
Linux的文件操作
在Linux世界里,socket也没认为是文件的一种,因此在数据传输过程中自然可以使用文件i/o的相关函数
而在windows环境下是进行区分的
打开文件操作
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int open(const char *path, int flag);
//--->成功是返回文件描述符,失败返回-1
/*@param
*path 文件名的字符串地址。
*flag 文件名的打开模式信息。具体可选值如下图
*/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N7DLZS7x-1641215230584)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220101190724882.png)]
关闭文件
int close(int fd);
//--->成功时返回0,失败-1
//*fd 表示需要关闭文件或套接字的文件描述符
将数据写入文件(write)
#include<unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes);
//--->成功写入时返回写入的字节数,否则-1
/*@param
*fd 显示数据传输对象的文件描述符
*buf 保存要传输数据的缓冲地址值
*nbytes 要传输数据的 字节数 。
*/
注意:此函数定义中的size_t是通过typedef生命的unsigned int类型。对于ssize_t来说,size_t前面多加的s代表signed,是signed int 类型。
在项目中为了给基本数据类型赋予别名,一般会添加大量的typedef声明。而为了与程序员定义的新数据类型加以区分,操作系统定义的数据类型会添加后缀_t。
接受文件的数据(read)
#include<unistd.h>
ssize_t read(int fd,void *buf, size_t nbytes);
//--->成功时返回接受的字节数(但遇到文件结尾则返回),失败时返回-1.
//参数和write参数类似
windows套接字
进行winsock编程时,首先必须调用WSAStartup函数,设置程序中用到的Winsock版本,并初始化相关版本的库(linux)下不需要这么做
#include<winsock2.h>
int WSAStartup(WORD wVersionRequested, LPWSADATA IpWSAData);
//--->成功返回0,失败返回非零的错误代码值
//wVersionRequested 程序员用到的Winsock版本信息
//IpWSAData WSADATA结构体变量的地址值
MAKEWORD(1,2);
MAKEWORD(2,2);
//上述两个是宏函数,用于渐变的构造WORD类型的版本信息
第二个参数LPWSDATA是WSADATA的指针类型,因此winsock有如下的编程公式
int main(int argc,char* argv[]){
WSADATA wsaData;
...
if(WSAStartup(MAKEWORD(2,2), &wsaData)!=0)
ErrorHanding("WSAStartup() error!");
....
WSACleanup();//成功时返回0,失败是返回socket_error
}
文件I/O与标准I/O的区别
参考链接(12条消息) 底层文件I/O和ANSI标准I/O的区别_云鹤起舞的博客-CSDN博客_底层文件
简单来说文件I/O和操作系统有关,通过系统调用来直接操作文件,没有缓存
而标准I/O在文件I/O的基础上进行了封装,先进行缓冲区的读写,然后在通过系统调用写入文件
套接字详解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AqjOiMEA-1641215230585)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220101195221764.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gyo4y9HF-1641215230586)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220101195418603.png)]
套接字类型
SOCK_STREAM面向连接的套接字,面向连接的套接字不存在数据边界。
SOCK_DGRAM面向消息的套戒指
服务器端常见初始化过程
linux
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P0R0gVQo-1641215230586)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220101203236425.png)]
Windows
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vVws3WpN-1641215230586)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220101203414569.png)]
UDP连接
udp的发送数据函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iKVa77wX-1641215230587)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102171941169.png)]
udp的接受数据函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b7bnegj5-1641215230587)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102172044571.png)]
unp存在数据边界因此输入函数的调用次数,应该和输出函数的调用次数完全一致,这样才能保证结束到全部发送的数据。
未连接的udp套戒指每次sendto大致分为如下三个阶段
1 向udp套接字注册目标IP和端口号
2 传输数据
3 删除UDP套接字中注册的目标地址信息
已连接的udp套接字可以不用每次sendto时候注册目标端口和IP传送玩之后在删除UDP。可以用write,read函数进行通讯,但并不代表建立了tcp一样的连接。
TCP的半关闭
tcp断开连接的过程可能发生预想不到的情况,因此应准确掌握
close和closesocket是完全断开,完全款开无法传递数据,也无法接收数据
shutdown(Linux)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4X9RbNrs-1641215230587)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102191302495.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gxyKBORN-1641215230588)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102191311404.png)]
shutdown(Windows)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x7EUslz0-1641215230588)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102192105729.png)]
上面第二个参数知识名称不同,在LINUX和WINDOWS中他值都是一样的,断开输入流是0断开输出流是1断开io流是2
域名转换函数
利用域名获得ip地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D65KV8YI-1641215230588)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102193325218.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZ2dj95a-1641215230589)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102193419218.png)]
利用ip地址获得域名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3LTtOxuI-1641215230589)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102193917412.png)]
套接字的多种可选项
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aI6gxL3p-1641215230589)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102194225423.png)]
可选项的读取和设置
读取
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dakHaMnv-1641215230590)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102194347097.png)]
设置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i6BBjg8k-1641215230590)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102194401156.png)]
Time-wait状态
tcp连接下,任何一方通过强制断开连接,都会使socket进入time-wait状态,此时端口是正在使用状态,因此服务器强制断开连接会导致下次启动服务器的时候会bind失败(客户端由于每次都是随机的端口号,因此很少有这类问题出现)
解决办法就是在套接字中更给SO_REUSERDDR的状态,调整参数,可将TIME_Wait状态下的套接字端口重新分配给新的套接字。设置为1既可以。
nagle算法
nagle算法默认使用,最大限度地进行缓冲,直到收到ACK。不适用nagle算法将对网络流量产生负面影响。
**但有些时候不使用nagle算法反而有更快的传输速率。最典型的是传输大文件数据。**将文件传入输出缓冲不会花费太多时间,因此,即使不使用nagle算法,也会在装满输出缓冲时传输数据包。这不仅不会增肌数据包的数量,反而会在无需等待ack的前提下连续传输,因此可以大大提高传输速度。
禁用nagle算法只需要讲套接字的可选项tcp_nodelay改为1即可
int opt_val=1;
setsocketopt(sock, IPPROTO_TCP,TCP_NODELAY, (void *) &opt_val,sizeof(opt_val));
多进程服务端
具有代表性的并发服务器端实现模型和方法。
1 多进程服务器:通过创建多个进程提供服务
缺点,开销很大,大量的运算和内存空间,相互间的数据交换也需要相对复杂的方法IPC
2 多路复用服务器: 通过捆绑并统一管理I/O对象提供服务。
3 多线程服务器: 通过生成与客户端等量的线程提供服务。
期中1多进程服务器不支持windows只有在linux下才能实现多进程
多进程
通过fork来创建此进程,父进程返回子进程的ID之后fork后面的代码复制到子进程中(包括fock代码)子进程返回0。子进程退出后必须把函数退出返回值传输父进程,操作系统才会把子进程完全杀掉。否则会变成**僵尸进程。**父进程需要主动向操作系统请求子进程的退出返回值。有如下函数
wait[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KuL3GC1w-1641215230590)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102205321423.png)]
waitpid函数
wait函数没有终止的子进程会进入阻塞状态。会引起程序阻塞,还可以考虑调用waitpid函数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MGM4sArF-1641215230591)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102205450276.png)]
信号处理
上面的过程固然可以,但是如果子操作系统在子进程销毁时告诉父进程是不是更好?那就用到了信号处理机制
。。。。。。略。
基于多任务的并发服务器linux多进程版本,见参考书《TCP/IP网络变成》第173页
分割i/o设计实际上,在一个进程内同时进行数据的收发逻辑需要考虑更多的细节。程序越复杂,这种区别越明显,因此可以在父进程中辫子额接收数据的代码,子进程中子需要编写发送数据的代码即可实现I/O分割。
进程间通讯详见《TCP/IP网络变成》第183页
!!!复杂功能的服务端程序并不是用的进程和管道,有两种更为强大的模型
I/O复用
select函数可以将多个文件描述符集中到一起统一监视。
select函数的调用方法和顺序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xpBI1cv1-1641215230591)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102211732210.png)]
设置文件描述符:也就是把不同的监视项分类,把同一类别的文件描述符集中在一起。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZgSrkCNH-1641215230591)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102211904004.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vXxGM7pE-1641215230591)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102212006488.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HQB4HGPW-1641215230592)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102212130008.png)]
设置检查(监察)范围及超时
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GYHVhtTn-1641215230595)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220102212201665.png)]
也就是说,监视范围为最大的文件描述符值加一在传递到select函数的第一个参数上即可。
超时时间是一个timeval结构数组,有两个参数 tv_sec秒,tv_usec毫秒。然后把该结构体的地址值传递到select函数的最后一个参数即可。第二到第四个参数分别传入声明的3个fd_sed型变量(分别向其注册文件描述符信息)的地址。
调用select函数后查看结果
如果select函数的返回值大于0说明相应数量的文件描述符发生了变化(注意是相应数量的文件描述符,不是对应的文件描述符)发生变化后,把向其传递的fd_set变量中原来为1的所有位变成了0,发生变化的文件描述符对应的位置除外。
以下为采用I/O复用的服务端程序(回声程序)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<winsock2.h>
#pragma comment(lib, "ws2_32")
#define BUF_SIZE 1024
void ErrorHandling(char *message);
int main(int argc,char *argv[])
{
WSADATA wsaDara;
SOCKET hServSock,hClntSock;
SOCKADDR_IN servAdr,clntAdr;
TIMEVAL timeout;
fd_set reads,cpyReads;
int adrSz;
int strLen,fdNum,i;
char buf[BUF_SIZE];
if(argc!=2){
printf("Usage: %s <port>\n",argv[0]);
}
//使用windows网络编程相关库
if(WSAStartup(MAKEWORD(2,2),&wsaDara)!=0)
ErrorHandling("WSAStartup() error!");
//创建服务端的socket
hServSock = socket(PF_INET,SOCK_STREAM,0);
memset(&servAdr,0,sizeof(servAdr));
//设置服务端的端口 ip 协议信息
servAdr.sin_family=AF_INET;
servAdr.sin_port=htons(atoi(argv[1]));
servAdr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
if(bind(hServSock,(SOCKADDR*)&servAdr,sizeof(servAdr))==SOCKET_ERROR)
ErrorHandling("bing() error");
if(listen(hServSock,5)==SOCKET_ERROR)
ErrorHandling("listen() error");
//设置set数组(初始化)
FD_ZERO(&reads);
//把服务端套接字句柄注册在set数组里面
FD_SET(hServSock,&reads);
while(1){
//赋值以下set数组,以备select监听到修改后还原注册的socket句柄
cpyReads=reads;
timeout.tv_sec=5;
timeout.tv_usec=5000;
//调用select函数
if((fdNum=select(0,&cpyReads,0,0,&timeout))==SOCKET_ERROR)
break;
if(fdNum==0)
continue;
//循环查看已注册的socket句柄时候有变化
for(i=0;i<reads.fd_count;i++){
//如果有变化
if(FD_ISSET(reads.fd_array[i],&cpyReads)){
//服务端的变化代表有链接请求
if(reads.fd_array[i]==hServSock){
adrSz=sizeof(clntAdr);
hClntSock = accept(hServSock,(SOCKADDR*)&clntAdr, &adrSz);
FD_SET(hClntSock,&reads);
printf("connected client: %d \n",hClntSock);
}
//否则代表有发送数据的客户
else{
strLen=recv(reads.fd_array[i],buf,BUF_SIZE-1,0);
if(strlen==0){
FD_CLR(reads.fd_array[i],&reads);
closesocket(cpyReads.fd_array[i]);
printf("closed client %d \n", cpyReads.fd_array[i]);
}
else{
send(reads.fd_array[i],buf,strLen,0);
}
}
}
}
}
closesocket(hClntSock);
WSACleanup();
return 0;
}
void ErrorHandling(char *message){
fputs(message,stderr);
fputc('\n',stderr);
system("pause");
exit(1);
}
多种I/O函数
linux下的send recv函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d3OlxQPG-1641215230596)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103152903766.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vZpiyYTi-1641215230597)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103152915133.png)]
send与recv函数的最后一个参数是发送数据时的可选项。用位或运算同时传递多个信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sfeUTq37-1641215230597)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103153039634.png)]
设置了MSG_PEEK可选项并调用recv函数时候,即使读取了输入缓冲的数据也不会删除。通常与MSG_DONTWAIT可选项同时使用,没有信息的时候不会阻塞,有信息的时候不会删除输入缓冲区的数据。
readv & writev 函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jFiaDH0p-1641215230597)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103154458196.png)]
使用场景:实际上,能使用该函数的所有情况都可以使用。比如需要传输或者接受的数据位于多个不同的缓冲(数组)时,可以考虑使用这两个函数。而有些服务器为了提高效率,禁用Nagle算法,通过write能分别发送了3个数据包,采用writev函数很有可能将所有的数据一次性写入输出缓存只传输一个数据包。
windows下的紧急情况传输及接受
紧急数据的传输,和linux相同,发送数据的时候设置对应的可选项即可。
紧急数据的接受。利用select监视发生异常的套接字来实现紧急消息的接受(linux下是通过信号处理机制实现的)
**!!! windows下没有writev和readv实现多个缓冲区数据的收发!!! **
!!! 需要用到重叠I/O (在后面) !!!
多播
理论上支持多播的路由器既可以完成多播,但是因为拥塞控制可能故意阻止多播。因此为了在不支持多播的路由器中完成多播通信,也会使用隧道技术。我们只讨论支持多播服务的环境下的编程方法。
路由和ttl
为了传递多播数据包必须设置TTL
int send_sock;int time_live=64;...send_sock=socket(PF_INET,SOCK_DGRAM,0);setsocktop(send_sock ,IPPROTO_IP,IP_MULTICAST_TTL, (void*)&time_live,sizeof(time_live));
加入多播组
通过set客户端socket的可选项既可以加入多播组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JJB6bwME-1641215230597)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103160910189.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x1di3r7W-1641215230598)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103161107792.png)]
多播发送与接受的过程
多播发送者需要把数据发送到多播组地址上的对应端口那里,接收者bind自己的socket后加入多播组,(自己必须要使用和多播地址对应的端口相同的端口号)。然后接受多播数据。
广播
- 直接广播(除了网络地址外,全部设置1)
- 本地广播(255.255.255.255,向本主机所在网络的所有主机传输数据)
数据通信中使用的IP地址时与UDP示例的唯一区别。默认生成的套接字不支持接受广播数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8s7adDV-1641215230598)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103162550589.png)]
第二部分Linux特有的网络编程
详情看《TCP/IP网络编程》P246页
套接字和标准IO
标准IO的优点
标注IO具有良好的移植性
标准IO函数可以利用缓冲提高性能
标准IO的缺点
不容易进行双向通信
有时可能频繁的调用fflush函数
需要以FILE结构体指针的形式返回文件描述符
关于IO流分离的其他内容
分离IO的两种方法
第一种时通过fork函数复制出一个文件描述符,比区分输入和输出中使用的文件描述符。
第二种方法时两次fdopen函数的调用,创建了读和写模式的不同file指针。
分离IO流的好处
- 通过分开输入过程和输出过程降低实现难度
- 与输入无关的输出操作可以提高速度
- 为了将file指针按照读写模式加以区分
- 通过区分IO缓冲提高缓冲性能
Linux下可以把文件描述符的读写分离(通过fdopen)
但这种情况下衍生出一个问题就是怎么进行socket的半关闭,如果直接关闭对应的file指针,都会关闭文件描述符,因此采用的办法就是复制一次文件描述符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZqVKN9fD-1641215230598)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103165004154.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-In3fMXQO-1641215230598)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103165204426.png)]
无论复制出多少文件描述符都应该调用shutdown函数发送eof进入版关闭状态。
优于select的epoll
select的缺点
- 首先就是调用select函数后常见的针对所有文件描述符的循环与及
- 更严重的是每次调用select函数时都需要向该函数传递监视对象的信息
传递监视对象的信息的含义是:每次调用select函数时向操作系统出啊你监视对象的信息,这一步无法优化,并且应用程序向炒作系统传递信息会对程序照成很大的负担
select的优点
- 大多出操作系统都支持select函数
- 服务端接入者比较少的时候并不会感到明显的变慢
- select的程序兼容性更高
针对select的缺点进行改变
仅向操作系统传递一次监视对象,监视范围或内容发生变化时只通知发生变化的事项。
linux支持的方式时epoll,windows支持的方式时IOCP
windows下的网络编程特点
线程的相关内容
线程的使用
线程的创建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2xTHCyCN-1641215230599)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103181725827.png)]
如何知道线程什么时候执行完
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYpeViud-1641215230599)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103182359275.png)]
互斥量的创建和使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jFNs05hz-1641215230599)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103184020888.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q6xGwSIE-1641215230599)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103185113435.png)]
信号量的创建和使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4HfOEaIt-1641215230600)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103185257567.png)]
上锁相当于调用wait函数
线程的销毁和多线程并发服务器端的实现
linux线程并不是在线程main函数返回时自动销毁因此需要手动销毁,否者会一直占用内存
- 调用pthread_join函数(不仅会等待线程结果,还会销毁线程,但会照成阻塞)
- 调用pthread_detach函数(不会引起线程进入阻塞状态,调用后不能再针对相应的先册灰姑娘调用pthread_detach函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rSGRkAyr-1641215230600)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103185910440.png)]
Windows平台下线程的使用
非显示创建线程的程序是“单一线程模型的应用程序”
显示创建线程的程序是”多线程模型的应用程序“
因此main函数实际上是线程完成的。
线程的创建和使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OnTueL3a-1641215230600)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103191943729.png)]
不同于linux Windows线程在首次调用的线程main函数返回时销毁。还有其他方法可以终止线程,但最好的方法就是让线程main函数终止。
如果线程要使用C/C++标准函数,需要通过如下的方法创建线程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vwisw7vl-1641215230600)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103192218351.png)]
查看内核对象的状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xOZWIbv4-1641215230600)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103193130226.png)]
该函数在发生事件(变为signaled状态)返回时,有时会把相应内核对象再次改编为non-signaled状态。
接下来的函数可以查看多个内核对象状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pvUeoIxR-1641215230601)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103193314959.png)]
线程同步
线程同步分为两个,一个是用户模式下同步,一个是内核模式同步
用户模式下同步最大的有点是速度快,使用方法相对简单,但是存在一定局限性
基于用户模式下的同步
初始化[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UK3zXVEA-1641215230601)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103202307475.png)]
获得该函数的参数类型的对象的函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Th7tJ2Cz-1641215230601)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103202403026.png)]
内核模式的同步方法–互斥量
互斥量对象的函数创建互斥量[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PZkxzBYL-1641215230601)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103202535614.png)]
销毁互斥量(这是销毁内核对象的函数,因此也可以用来销毁信号量及事件)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ML7UXjvN-1641215230601)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103202903380.png)]
获取互斥量是通过WaitForSingleObject函数来实现的
释放互斥量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MLWyciMe-1641215230602)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103203024425.png)]
内核状态下的同步------基于信号量的对象同步
和linux下的信号量类似,二者都是利用名为信号量值的整数值完成同步的,而且值都不能小于0
创建信号量对象的函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4x1W5lbt-1641215230602)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103203324364.png)]
信号量为0时进入non-signaled状态,大于0进入signaled状态
信号量的释放函数(信号量的获得也是通过WaitforSingleObject()函数获得
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWzvZHdh-1641215230602)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103203449003.png)]
内核状态下的同步基于事件对象的同步
创建事件对象的函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-esTLd1gS-1641215230602)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103203714022.png)]
需要通过如下两个函数用来明确更改对象状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-11ng8RkU-1641215230602)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103203905146.png)]
WIndows下的实现多线程服务端以及收发分离的客户端程序
参考《TCP/IP网络编程》P339页
windows提供的扩展I/O模型
理解同步于异步
send&recv函数进行同步I/O。调用send函数时,完成数据传输后才能从函数返回(确切的说,只有把数据完全传输到输出缓冲区后才能返回);而调用recv函数时,只有读到期望大小的数据后才返回。
同步情况下,函数没有返回,就不能进行其他任务,因此异步方式能够比同步方式更加有效的使用CPU
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ScArbAKt-1641215230603)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103205038104.png)]
使用WSAEventSelect 函数和通知
它是select函数的异步IO版本
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W3EDcINi-1641215230603)(C:\Users\顾子茵\AppData\Roaming\Typora\typora-user-images\image-20220103205344447.png)]