1. 套接字超时
1.1 accept/read/write超时
- 如何设置accept()函数等待连接时的超时时长?
答:accept()
函数本身会一直阻塞到有连接请求而无法计时,因此可以设置由内核监听连接并计时。即由select()/epoll()/poll()
函数来计时。 - 如何设置read()/write()的等待时长?
答:read()
函数要一直监测缓冲区中有数据才能解除阻塞读数据,因此可以同accept()
函数一样,由内核监测。而write()
函数要一直监测缓冲区未满才能解除阻塞写数据,因此也可以由内核检测。
//1.设置监听时间
fd_set rdset;
FD_ZERO(&rdset);
FD_SET(fd, &rdset);
struct timeval timeout = {10, 0};
//2.设置为内核检测
int ret = select(fd+1, &rdset, %rdset, NULL, &timeout);
if(ret == 0) //若无描述符变化
{
//监测超时
}
else if(ret > 0) //若有变化
{
//accept则接受链接
struct sockaddr addr;
int len = sizeof(addr);
int cfd = accept(fd, &addr, &len);
//read则开始读数据
char buf[1024] = {0};
read(fd, buf, sizeof(buf));
//write则开始写数据
write(fd, buf, strlen(buf));
}
1.2 connect超时
- connect的连接过程?
答:调用connect()
函数, 客户端与服务器进行三次握手;此时由于通讯套接字cfd,所以是阻塞状态;若超过默认75s依然未连接上,则连接失败。 - connect如何自定义连接时间?
答:先将connect设置为非阻塞,即设置套接字cfd的属性;再根据Posix 标准进行设置。 - Posix标准connect连接时长?
4. Posix 定义了与 select/epoll 和 非阻塞 connect 相关的规定:
- 连接成功建立时,socket 描述字变为可写。(连接建立时,写缓冲区空闲,所以可写)
- 连接建立失败时,socket 描述字既可读又可写。 (由于有未决的错误,从而可读又可写)
5. 连接失败, 错误判定方式:
- 当用select检测连接时,socket既可读又可写,只能在可读的集合通过getsockopt获取错误码。
- 当用epoll检测连接时,socket既可读又可写,只能在EPOLLERR中通过getsockopt获取错误码。
- 根据Posix标准实现定义connect时长?
//1. 设置connect为非阻塞:即设置cfd属性
int fd1 = fcntl(cfd, F_GETFL);
fd1 |= O_NONBLOCK;
fcntl(cfd, F_SETFL, fd1);
//2. 建立连接请求
int ret = connect(cfd, serveraddr, addrlen);
//若未连接成功且正在连接中
if(ret == -1 && errno = EINPROCESS)
{
//开始连接计时:监测cfd变为可写
fd_set wrset;
FD_ZERO(&wrset);
FD_SET(cfd, &wrset);
struct timeval timeout = {10, 0}; //设置连接时间
int ret = select(cfd, NULL, &wrset, NULL, &timeout); //开始监测
if(ret == 0) //若cfd不可写,即连接超时
{
}
else if(ret > 0) //若cfd变为可写,则连接成功/失败
{
int opt;
int len = sizeof(opt);
getsockopt(cfd, SOL_SOCKET, SO_ERROR, &opt, &len); //判断是否连接成功
if(opt == 0)
{
//连接成功
}
else if(opt == -1)
{
//连接失败
}
}
}
2. 共享内存
- 共享内存的工作流程?
答:(1)进程申请一块指定大小的内存;(2)将申请的内存与当前进程相关联;(3)使用申请的内存;(4)解除改内存的关联并销毁。 - 如何申请一块指定大小的内存?
答:通过shmget()
函数。
int shmget(key_t key, size_t size, int shmflg);
参数:key-16进制非0整型数,用来创建内存;size-指定创建内存的大小;shmflg-内存属性,包括访问权限和附加属性,附加属性:IPC_CREAT创建/IPC_EXCL判断是否已创建;
返回值:成功-共享内存引用ID;失败--1;
//创建一块4K大小内存
int shmid = shmget(0x10, 4096, IPC_CREAT|0751);
//判断是否创建
int ret = shmget(0x10, 0, IPC_CREAT|IPC_EXCL|0751);
返回值:存在->0,内存ID; 不存在--1;
//打开创建的内存
shmget(0x10, 0, 0);
- 如何关联申请的内存?
答:用shmat()
函数。
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid-申请的内存id;shmaddr-申请内存的起始地址,一般写NULL由内核指定;shmflg-申请内存的属性,SHM_RDONLY只读属性必须有,若要读写权限写0;
返回值:成功-申请内存的起始地址;失败-(void *)-1;
//关联并使用申请的内存
void *ptr = shmat(shmid, NULL, 0);
char *s = "hello";
memcpy(ptr, s, strlen(s));
- 如何分离关联的共享内存?
答:用shmdt()
函数;
int shmdt(const void* shmaddr);
shmaddr-共享内存的起始地址;
返回值:成功-0;失败--1;
int ret = shmdt(shmaddr);
- 如何销毁申请的共享内存?
答:关机后自动销毁,若不关机可用shmctl()
函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid-共享内存ID;cmd-对共享内存的操作,IPC_STAT获取共享内存当前状态,
IPC_SET设置共享内存当前状态,IPC_RMID销毁当前共享内存;
buf-存储cmd信息,cmd是IPC_STAT时存储共享内存状态信息,IPC_SET时初始化并将buf内容设置到共享内存
IPC_RMIND时则无用,设置为NULL;
返回值:根据cmd不同,返回值含义不同。
//销毁共享内存
shmctl(shmid, IPC_RMID, NULL);
- 共享内存实现进程间通信的步骤?
//写操作
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
//1.创建共享内存
int shmid = shmget(0x10, 1034, IPC_CREAT|0751);
if(shmid < 0)
{
perror("shmget");
exit(0);
}
//2.建立连接
void *shmaddr = shmat(shmid, NULL, 0);
if(shmaddr == (void *)-1)
{
perror("shmat");
exit(0);
}
//3.读共享内存数据
char *s = "hello"
memcpy(shmaddr, s, strlen(s));
//4.取消关联
printf("按任意键取消链接...\n");
fgetc(stdin);
shmdt(shmaddr);
//5.销毁共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
//读操作
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib>
#include <stdio.h>
#include <string.h>
int main()
{
//1.打开共享内存
int shmid = shmget(0x10, 0, 0);
if(shmid < 0)
{
perror("shmget");
exit(0);
}
//2.建立连接
void *shmaddr = shmat(shmid, NULL, 0);
if(shmaddr == (void *)-1)
{
perror("shmat");
exit(0);
}
//3.读共享内存数据
char buf[1024] = {0};
memcpy(buf, shmaddr, sizeof(buf));
printf("%s\n", buf);
//4.取消关联
printf("按任意键取消链接...\n");
fgetc(stdin);
shmdt(shmaddr);
//5.销毁共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
- 操作系统如何知道共享内存有多少进程关联?
答:每调用一次shmat()
函数,内存信息中的引用计数就会+1,调用shmdt()
,引用计数-1; - 是否可以对同一共享内存删除多次?
答:可以,多个进程都可以调用shmctl()
进行删除,没调用一次就会做一次删除标记,当所有进程取消关联,共享内存就会销毁。
当调用一次shmdt()
函数后,已关联的进程可以继续使用,未关联的进程无法再关联。 - 共享内存与内存映射区进行进程通信的区别?
答:(1)通信效率:shm直接内存操作效率高;mmap须同步磁盘文件效率低,智能血缘关系之间通信;
(2)内存共享性:shm多个进程共享一块内存;mmap多个进程操作自己进程中的用户数据区,共享的是磁盘文件;
(3)数据安全:shm与进程无关,但与内存有关;mmap与内存无关,但与进程有关。因此若进程突然退出不影响shm,但mmap将失败;若突然断电关机不影响mmap,但shm将失败。 - 如何使用
ftok()
函数?
答:ftok()函数随机生成一个shm的键值
key_t ftok(const char *pathname, int proj_id);
pathname-路径,可随意指定
proj_jd-随意指定的字符
// string -> char*
// .c_str() / data()
key_t t = ftok("/home/", 'a');
shmget(t, 0, 0);
- 共享内存的API封装
答:
class BaseShm
{
public:
BaseShm(int key)
{
getShmID(key, 0, 0);
}
BaseShm(int key, int size)
{
getShmID(key, size, IPC_CREAT|0664);
}
BaseShm(string name)
{
key_t key = ftok(name.data(), 'x');
getShmID(key, 0, 0);
}
BaseShm(string name, int size)
{
key_t key = ftok(name.data(), 'x');
getShmID(key, size, IPC_CREAT|0664);
}
void* mapShm()
{
m_ptr = shmat(m_shmid, NULL, 0);
return m_ptr;
}
int unmapShm()
{
int ret = shmdt(m_ptr);
return ret;
}
int delShm()
{
int ret = shmctl(m_shmid, IPC_RMID, NULL);
return ret;
}
private:
int getShmID(int key, int size, int flag)
{
m_shmid = shmget(key, size, flag);
return shmid;
}
int m_shmid;
void* m_ptr;
}
来源:CSDN
作者:向测
链接:https://blog.csdn.net/weixin_45093118/article/details/104534057