1、客户端开发流程
- 创建socket,得到一个通信的文件描述符
- 绑定------不是必须的(内核随机分配)
- connect服务端:在给IP和端口赋值时需要注意主机字节序和网络字节序的转换
- 循环收发数据:读或者写都是操作内核的缓冲区,并不负责发送或者接收数据,发送或者接受是由内核负责的
- 关闭文件描述符close
2、服务端开发流程
- 创建socket,创建监听描述符
- 设置端口复用
- 绑定----必须的:ip加端口号可以确定唯一一个服务
- 监听listen
- 接受新连接accept
- 循环收发数据
- 关闭套接字
3、同步和异步:异步效率比同步高
同步:例如,客户端发送数据给服务端,发送完成后,就read阻塞等待数据,读数据不完成则不完成后续操作
异步:客户端发送数据给服务端,发送完成后,没有等待read数据,而是直接处理后续操作
4、阻塞和非阻塞
阻塞:比如accept和read都是阻塞函数,条件不满足,就一直阻塞等待
5、同步非阻塞、同步阻塞、异步阻塞和异步非阻塞
异步阻塞:select\epoll\poll
6、长连接和短链接
长连接:通常用于通信双方数据交换频繁的情况
-------心跳包
短链接:通常用于通信双方交换数据完成后就断开连接
同步短链接:
异步长连接:
7、网络知识回顾
- 一个文件描述符对应两个内核文件缓冲区
- 三次握手、四次挥手、滑动窗口:主要进行流量控制
- TCP状态转化图
- 多进程、多线程
- epoll反应堆
- 线程池
8、通信效率:单位时间客户端/服务端接受/发送数据的量
客户端通信效率分析:
- 单进程,只处理一个链接
- 多线程使用同一个链接
- 多线程使用多个连接
- 连接池+线程池:连接池不能用于服务端,链接只有客户端发起请求之后才有,只有客户端可以用 :
连接池:
- 有一个数据结构保存链接
- 创建连接池操作----poolInit()
- 获取链接的操作
- 将连接放回的操作
- 动态调整连接池的数量
- 销毁连接池
9、客户端类设计思想
10、服务端设计思想
11、进程间通信回顾
- 管道pipe
#include <unistd.h> int pipe(int pipefd[2]);
特点:1、只能用于有血缘关系的进程间通信 2、管道有两端,管道数据流向由管道的写端到管道的读端 3、管道的本质时一个内核缓冲区 4、数据从管道中读走以后就不存在了 5、管道的实现实际上是环形队列 6、默认下管道的读写端都是阻塞的
-
命名管道:fifo 特点:1、既可以用于有血缘关系的进程间通信,也可以用于无血缘关系的通信 2、创建的fifo文件大小为0,是linux文件7种之一 3、使用fifo需要先创建fifo文件 4、使用fifo完成通信两个进程必须打开相同的FIFO文件 5、效率比pipe低
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
-
mmap:共享映射区,本质是将文件内容映射到内存
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
特点:1、有无血缘关系都可以使用 2、如果完成没有血缘关系的必须使用文件 3、若使用MAP_SHARED,对内存的修改会反映到文件中 4、需要注意mmap存在调用失败的情况 5、匿名映射只能用于有血缘关系的进程间
-
信号:进程A给进程B发送信号,本质上是进程A给内核发送信号然后内核给B发送信号
#include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); 相关函数: signal kill sigaction abort raise alarm|setitmer sigemptyset sigaddset sigfillset sigdelset sigismember sigpromask sigpending SIGINT SIGQUIT SIGALRM SIGTERM SIGKILL SIGSTOP SIGUSR1 SIGUSR2 SIGPIPE SIGCHILD信号
特点:1、信号不能携带大量信息 2、信号优先级比较高,会打断程序执行 3、不建议使用信号完成进程间通信 4、一般使用kill命令给一个进程发送信号,进程收到信号之后调用信号处理函数 信号处理动作:1、忽略信号 2、执行默认处理动作 3、执行用户自定义函数
-
本地socket
#include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> tcp_socket = socket(AF_UNIX, type, 0); tcp/udp都可以
1、如果使用tcp通信,bind时需要指定一个文件,若文件存在会报错,unlink删除 2、独活写其实都是通过文件描述符去操作内核缓冲区 3、编写流程可以直接参考tcp/udp开发流程
-
共享内存:实质是将内核的一块内存映射到进程中的内存,操作本地内存就相当于操作共享内存(key值区分)
使用共享内存步骤:
- 创建共享内存
- 关联共享内存
- 使用共享内存-----读写共享内存
- 断开与共享内存的关联
- 删除共享内存
创建共享内存:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
创建或者获得共享内存ID
参数:key:是一个无符号整型值,唯一标识一份共享内存
size:创建共享内存的大小
shmflg: IPC_CREAT:创建共享内存 IPC_EXCL:只能和前面的一起使用,如果共享内存存在则报错,还需要指定权限IPC_CREAT|0644
返回值:成功:返回共享内存ID值,失败返回-1并设置errno
1、共享内存存在
key_t key=0x1234;
int shmID=shmget(key,0,0);
2、共享内存不存在
int shmID=shmget(key,100,IPC_CREAT|IPC_EXCL|0755);
3、无法确定是否存在
int shmID=shmget(key,100,IPC_CREAT|0755);
连接共享内存:
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:shmid:shmget得到的ID值
shmaddr:NULL让内核自己分配地址
shmflg:0:读写都行 SHM_RDONLY:只能读
返回值:成功返回关联的内存地址,失败返回(Void*)-1
断开与共享内存的链接:
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
参数:shmaddr:shmat返回值
返回值:成功返回0,失败返回-1
设置或者删除共享内存:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
shmid:shmget返回的ID值
cmd:IPC_STA:获得共享内存的信息,拷贝到结构体中
IPC_SET:设置
IPC_RMID:删除:只有在最后一个进程断开连接才可以删掉 最后一个参数填NULL
当删除共享内存时,如果共享内存关联计数大于0,则不会被真正删除,但是key值变为0,当一个进程与共享内存关联之后关联引用会加1,断开后关联计数减1
shm和mmap的区别:
- mmap如果用于没有血缘关系的进程间通信必须使用文件,shm不需要
- shm操作数据比mmap快
- mmap比shm安全
- 进程退出,共享内存依然存在,进程退出,内存映射去就不存在了
相同点:mmap和shm都使用的内存地址,都可以用于有无血缘关系的进程间通信,mmap和shm读取数据后并不会消失
ftok函数:使用文件名生成key值
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
函数参数:
pathname:带文件路径的文件名(必须文件是存在的,对文件权限没有要求)
proj_id:只是用一个字节,取值范围:0-255
返回值:成功返回key_t key,失败返回-1
共享内存操作指令:
ipcs -a
ipcs -m
ipcs -s
ipcs -q
ipcrm -m shmid/-M keyid
消息队列:mssget 等等
信号量:semget等等