Socket function
setsockopt
获取或者设置与某个套接字关联的选 项。选项可能存在于多层协议中,它们总会出现在最上面的套接字层。当操作套接字选项时,
选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该 将层的值指定为SOL_SOCKET。为了操作其它层的选项,控制选
项的合适协议号必须给出。例如,为了表示一个选项由TCP协议解析,层应该设定为协议 号TCP。
SO_LINGER选项
struct linger
{
int l_onoff;
int l_linger;
};
- l_onoff = 0, 数据保持发送完成后立即返回
- l_onoff = 1, l_linger = 0, 立即放回 放弃发送, 发送rst 自身立即复位
- l_onoff = 1, l_linger = 1, 阻塞到超时或数据发送完成, 保持尝试发送,超时后立即结束
SO_REUSEADDR选项
改变了通配绑定时处理源地址冲突的处理方式, 让端口释放后立即就可以被再次使用
- 允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在
- 允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可
- 允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。一般不用于tco服务器
- 允许完全重复的捆绑
Re:
https://www.cnblogs.com/my_life/articles/5174585.html
https://www.jianshu.com/p/141aa1c41f15
https://blog.csdn.net/u010144805/article/details/78579528
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
fcntl函数有5种功能:
- 1.复制一个现有的描述符(cmd=F_DUPFD).
- 2.获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
- 3.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
- 4.获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
- 5.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
Re: https://www.cnblogs.com/xuyh/p/3273082.html
非阻塞I/O使我们的操作要么成功,要么立即返回错误,不被阻塞。
对于一个给定的描述符两种方法对其指定非阻塞I/O:
- 1.调用open获得描述符,并指定O_NONBLOCK标志
- 2.对已经打开的文件描述符,调用fcntl,打开O_NONBLOCK文件状态标志。
flags = fcntl( s, F_GETFL, 0 ) )
fcntl( s, F_SETFL, flags | O_NONBLOCK )
Re: https://blog.csdn.net/zhulinfeiba/article/details/5011573
- htonl()–“Host to Network Long int” 32Bytes
将主机的无符号长整形数转换成网络字节顺序。//将无符号长整型网络字节序转换为主机字节序
- ntohl()–“Network to Host Long int” 32Bytes
将一个无符号长整形数从网络字节顺序转换为主机字节顺序。
- htons()–“Host to Network Short int” 16Bytes
将主机的无符号短整形数转换成网络字节顺序。//将无符号短整型主机字节序转换为网络字节序
- ntohs()–“Network to Host Short int” 16Bytes
将一个无符号短整形数从网络字节顺序转换为主机字节顺序。
- inet_addr()
将一个点间隔地址转换成一个in_addr
- inet_ntoa()
是编程语言,功能是将网络地址转换成“.”点隔的字符串格式。
- inet_aton()
与inet_ntoa()作用相反。本函数将点分十进制转换为整数
- atoi()
array to integer将字符串转换为整形数
新型网路地址转化函数inet_pton和inet_ntop 这两个函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。
#include <arpe/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr); //将点分十进制的ip地址转化为用于网络传输的数值格式
返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len); //将数值格式转化为点分十进制的ip地址格式
返回值:若成功则为指向结构的指针,若出错则为NULL
-
1.这两个函数的family参数既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。如果,以不被支持的地址族作为family参数,这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT.
-
2.第一个函数尝试转换由strptr指针所指向的字符串,并通过addrptr指针存放二进制结果,若成功则返回值为1,否则如果所指定的family而言输入字符串不是有效的表达式格式,那么返回值为0.
-
3.inet_ntop进行相反的转换,从数值格式(addrptr)转换到表达式(strptr)。inet_ntop函数的strptr参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。len参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。如果len太小,不足以容纳表达式结果,那么返回一个空指针,并置为errno为ENOSPC。
inet_pton(AF_INET, ip, &foo.sin_addr); // 代替 foo.sin_addr.addr=inet_addr(ip);
char str[INET_ADDRSTRLEN];
char *ptr = inet_ntop(AF_INET,&foo.sin_addr, str, sizeof(str)); // 代替 ptr = inet_ntoa(foo.sin_addr)
示例代码
int main()
{
char IPdotdec[20]; // 存放点分十进制IP地址
struct in_addr s; // IPv4地址结构体
// 输入IP地址
printf("Please input IP address: ");
scanf("%s", &IPdotdec);
// 转换
inet_pton(AF_INET, IPdotdec, (void *)&s);
printf("inet_pton: 0x%x\n", s.s_addr); // 注意得到的字节序
// 反转换
inet_ntop(AF_INET, (void *)&s, IPdotdec, 16);
printf("inet_ntop: %s\n", IPdotdec);
}
Run:
Please input IP address: 127.0.0.1
inet_pton: 0x100007f
inet_ntop: 127.0.0.1
Re: https://blog.csdn.net/zyy617532750/article/details/58595700
https://www.cnblogs.com/wuyuxuan/p/10772779.html
int send( SOCKET s,char *buf,int len,int flags )
功能:不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。
-
参数一:指定发送端套接字描述符;
-
参数二:存放应用程序要发送数据的缓冲区;
-
参数三:实际要发送的数据的字节数;
-
参数四:一般置为0。
int recv( SOCKET s, char *buf, int len, int flags)
功能:不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。
-
参数一:指定接收端套接字描述符;
-
参数二:指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
-
参数三:指明buf的长度;
-
参数四 :一般置为0。
Re: https://blog.csdn.net/lanzhihui_10086/article/details/40681617
read(sock, buf, sizeof(buf) -1)
read从套接字文件中读取数据, fd为要读取的文件的描述符,buf为要接收数据的缓冲区地址,nbytes为要读取的数据的字节数。
/*
* 本函数向服务ip发起请求 服务器ip port 保存在sockaddr_in中
* int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen); //Linux
* int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen); //Windows
* sock 为 socket 文件描述符,addr 为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。
*/
connect(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr));
linux mutex block
pthread_mutexattr_t mattr
int pthread_mutexattr_init(pthread_mutexattr_t *mattr)
pthread_mutex_init() 函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。如果参数attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁 。互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。
- Success: return 0
POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
动态方式是采用pthread_mutex_init()函数来初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性, pthread_mutex_destroy()
用于注销一个互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex)
销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()
除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。
2.属性 3. 锁操作
Re: https://www.cnblogs.com/lidabo/p/4566693.html
互斥锁的类型:有以下几个取值空间:
-
PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
-
PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
-
PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
-
PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。 *pthread_mutexattr_settype(pthread_mutexattr_t attr , int type) **pthread_mutexattr_gettype(pthread_mutexattr_t attr , int type)
Re: https://blog.csdn.net/happylzs2008/article/details/89067028
销毁互斥锁属性对象
pthread_mutexattr_destroy(3C)
可用来取消分配用于维护pthread_mutexattr_init()
所创建的属性对象的存储空间。
对于互斥锁属性对象,必须首先通过调用pthread_mutexattr_destroy(3C)
将其销毁,才能重新初始化该对象。pthread_mutexattr_init() 调用会导致分配类型为 opaque 的对象。如果未销毁该对象,则会导致内存泄漏
Re: https://blog.csdn.net/jasmineal/article/details/8807744
对锁的操作主要包括加锁 pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个。
- int pthread_mutex_lock(pthread_mutex_t *mutex)
- int pthread_mutex_unlock(pthread_mutex_t *mutex)
- int pthread_mutex_trylock(pthread_mutex_t *mutex)
pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待
pthread_mutex_lock
用于解决由于竞争产生的优先级反转问题。没锁更新所有权,锁住
Re: https://blog.csdn.net/jasmineal/article/details/8807744
https://blog.csdn.net/oqqYuJi12345678/article/details/100585669
backtrace
backtrace_symbols
backtrace_symbols_fd
To: https://vcvvvc.github.io/post/backtrace/
pthread_cond_broadcast(&cond1)
的作用是唤醒所有正在pthread_cond_wait(&cond1,&mutex1)
的线程。
while(lock_status_[lid] == LOCKED)
{
pthread_cond_wait(&c_, &m_);
}
pthread_cond_broadcast(&c_);
一旦某个锁被释放,所有的阻塞线程都会被唤醒,但唯有阻塞在这个锁的线程才能真正被唤醒。
pthread_cond_signal(&cond1)
的的作用是唤醒所有正在 pthread_cond_wait(&cond1,&mutex1)
的至少一个线程。(虽然我还没碰到过多于一个线程的情况,但是man帮组手册上说的是至少一个)
Re:
https://www.cnblogs.com/XiaoXiaoShuai-/p/11855408.html
https://www.cnblogs.com/zhouzhuo/p/3781511.html
struct hostent{
char *h_name; //official name
char **h_aliases; //alias list
int h_addrtype; //host address type
int h_length; //address lenght
char **h_addr_list; //address list
}
- h_name:官方域名(Official domain name)。官方域名代表某一主页,但实际上一些著名公司的域名并未用官方域名注册。
- h_aliases:别名,可以通过多个域名访问同一主机。同一 IP 地址可以绑定多个域名,因此除了当前域名还可以指定其他域名。
- h_addrtype:gethostbyname() 不仅支持 IPv4,还支持 IPv6,可以通过此成员获取IP地址的地址族(地址类型)信息,IPv4 对应 AF_INET,IPv6 对应 AF_INET6。
- h_length:保存IP地址长度。IPv4 的长度为 4 个字节,IPv6 的长度为 16 个字节。
- h_addr_list:这是最重要的成员。通过该成员以整数形式保存域名对应的 IP 地址。对于用户较多的服务器,可能会分配多个 IP 地址给同一域名,利用多个服务器进行均衡负载。
gethostbyname
struct hostent *gethostbyname(const char *hostname);
gethostbyaddr 根据ip地址获取主机的完整信息
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);
Re:
http://c.biancheng.net/view/2357.html
getservbyname 根据名称获取某个服务的完整信息
struct servent *getservbyname(const char *name, const char *proto)
getservbyport 根据端口号获取某个服务的完整信息
struct servent *getservbyport(int port, const char *proto)
getaddrinfo 通过主机名获得IP地址也能通过服务名获得端口号
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
之后用freeaddrinfo 释放getaddrinfo所分配的内存
void freeaddrinfo(struct addrinfo *res);
getnameinfo 通过socket地址同时获得字符串表示的主机名和服务名
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);
readv 将数据从文件描述符读到分散的内存块中
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
writev 将多块分散的数据一并写入文件描述符中
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
sendfile 在两个文件描述符之间传递数据 避免内核缓冲区和用户之间的数据拷贝-零拷贝
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
mmap 申请一段内存空间 将这段内存作为进程间通信的共享内存,可以将文件直接映射到其中
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
munmap 释放mmap创建的内存空间
int munmap(void *addr, size_t length);
splice 用于在两个文件描述符之间移动数据-零拷贝
ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);
syslog和rsyslogd 守护进程通信
void syslog(int priority, const char *format, ...);
openlog改变syslog默认输出方式 进一步结构化日志内容
void openlog(const char *ident, int option, int facility);
setlogmask 设置日志掩码
int setlogmask(int mask);
closelog关闭日志功能
void closelog();
get_uid获取和设置当前进程真实用户
uid_t getuid(void); //真实用户ID
uid_t geteuid(void); //有效用户ID
gid_t getgid(void); //真实组ID
gid_t getegid(void); //有效组ID
int setuid(uid_t uid); //设置真实用户ID
int seteuid(uid_t euid); //设置有效用户ID
int setgid(gid_t gid); //设置真实组ID
int setegid(gid_t egid); //设置有效组ID
getpgid 获取进程组ID
pid_t getpgid(pid_t pid);
setpgid 设置用户组ID
int setpgid(pid_t pid, pid_t pgid);
setsid 创建一个会话
pid_t setsid(void);
getsid 读取sid
pid_t getsid(pid_t pid);
rlimit 系统资源读取\设置
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
struct rlimit
{
rlim_t rlim_cur;
rlim_t rlim_max;
}
cwd 获取进程当前工作目录和改变进程工作目录
char *getcwd(char *buf, size_t size);
int chdir(const char *path);
chroot 改变进程根目录
int chroot(const char * path);
daemon 守护进程
int daemon(int nochdir, int noclose);
基本框架
reactor 要求主线程只负责监听文件描述上是否有事情发生 有的话立即将该事件通知工作线程
proactor 将所有I/0操作都交给主线程和内核来处理 工作线程仅仅负责业务逻辑
同步模拟的Procator
并发模式
半同步/半异步模式
存在的缺点
高效模式
领导者/追随者模式 是多个工作线程轮流获得事件源集合, 轮流监听、分发并处理事件的一种模式
1. 句柄集 句柄表示I/O资源 在linux下通常是一个文件描述符 句柄集管理众多句柄
2. 线程集 负责各线程之间的同步 以及新领导者线程的推选. 线程集里的线程在任何时间都必须处于一下三种状态之一
领导者/追随者 工作流程图
select 在一段指定时间内 监听用户感兴趣的文件描述符上的可读、可写、异常等事件, readfds\writefds\exceptfds分别指向可读、可写、异常
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
poll 指定时间内轮询一定数量的文件描述符 测试其中是否有就绪者
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
epoll 把用户关心的文件描述符上的事件放在内核里的一个事件表中, 从而无需每次调用都要重复传入文件描述符集或事件集, 但epoll需要额外的文件描述符, 来唯一标识内核中的事件表
epoll_create 创建文件描述符
int epoll_create(int size);
操作epoll的内核事件表
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll_wait 在一段超时时间内等待一组文件描述符上的事件
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
LT 水平触发是默认工作模式、ET边缘触发
select\poll\epoll 区别比较
kill 一个进程给其他进程发送信号
int kill(pid_t pid, int sig);
kill出错的情况
信号处理函数SIG_DFL使用信号的默认处理方式、SIG_IGN表示忽略目标信号
//原型: typedef void (* __sighandler_t) (int);
#define SIG_DFL((__sighandler_t) 0)
#define SIG_IGN((__sighandler_t) 1)
signal信号设置处理函数
sighandler_t signal(int signum, sighandler_t handler);
signal更健壮的接口
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
信号集
int sigemptyset(sigset_t * _set) //清空信号集
int sigfillset(sigset_t * _set) //在信号集中设置所有信号
int sigaddset(sigset_t * _set, int _signo) //将信号 _signo添加至信号集中
int sigdelset(sigset_t * _set, int _signo) //将信号 _signo从信号集中删除
int sigismember(const sigset_t * _set, int _signo) //测试 _signo是否在信号集中
设置或查看进程信号掩码
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
sigpending 获得进程当前被挂起的信号集 成功返回0 失败-1
int sigpending(sigset_t *set);
SO_RCVTIMEO接收数据超时时间 SO_SNDTIMEO发送数据超时时间
高性能定时器 时间轮
fork 复制当前进程, 在内核进程表中创建一个新的进程表项
pid_t fork(void);
exec 在子进程中执行其他程序, 即替换当前进程映像
int execl(const char* path, const char * arg, ...);
int execlp(const char* file, const char* arg, ...);
int execle(const char *pathname, const char *arg, ... /*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
wait 在父进程中调用, 等待子进程的结束, 并获取子进程的返回信息,避免了僵尸进程的产生或使子进程的僵尸态立即结束, wait函数将阻塞进程,直到进程的某个子进程结束运行为止
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
semget 系统调用创建一个新的信号量集, 或者获取一个已经存在的信号量集
int semget(key_t key, int nsems, int semflg);
semop 对信号量的操作是对这些内核变量的操作
int semop(int semid, struct sembuf *sops, size_t nsops);
semctl 系统调用允许调用者对信号量进行直接控制
int semctl(int semid, int semnum, int cmd, ...);
shmget 系统调用创建一段新的共享内存,或者获取一段已存在的共享内存
int shmget(key_t key, size_t size, int shmflg);
shmat shmdt 共享内存被创建\获取后,不能立即访问,而是需要先将它关联到进程的地址空间中,使用完共享内存之后,也需要将它从进程地址空间中分离
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
shmctl系统调用控制内存的某些属性
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shm_open 创建或打开POSIX共享内存对象. Linux提供了一种利用mmap在无关进程之间共享内存的方式,这种方式无须任何文件支持,但它需要先使用shm_open 创建\打开一个POSIX共享内存对象
//编译时需要指定链接: -lrt
int shm_open(const char *name, int oflag, mode_t mode);
shm_unlink 删除创建的共享内存对象
int shm_unlink(const char *name);
socketpair()函数用于创建一对无名的、相互连接的套接子。
https://blog.csdn.net/weixin_40039738/article/details/81095013
int socketpair(int d, int type, int protocol, int sv[2]);
如果函数成功,则返回0,创建好的套接字分别是sv[0]和sv[1];否则返回-1,错误码保存于errno中。
基本用法:
-
- 这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往sv[0]中写,从sv[1]中读;或者从sv[1]中写,从sv[0]中读;
-
- 如果往一个套接字(如sv[0])中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sv[1])上读成功;
-
- 读、写操作可以位于同一个进程,也可以分别位于不同的进程,如父子进程。如果是父子进程时,一般会功能分离,一个进程用来读,一个用来写。因为文件描述副sv[0]和sv[1]是进程共享的,所以读的进程要关闭写描述符, 反之,写的进程关闭读描述符。
sendmsg、recvmsg、send函数的使用**
#sendmsg()用来将数据由指定的socket传给对方主机. 失败返回-1
int sendmsg(int s, const strcut msghdr *msg, unsigned int flags);
#函数说明:recvmsg()用来接收远程主机经指定的socket 传来的数据. 失败返回-1
int recvmsg(int s, struct msghdr *msg, unsigned int flags);
#send()用来将数据由指定的socket 传给对方主机. 失败返回-1
int send(int s, const void * msg, int len, unsigned int falgs);
函数send参数flags 一般设0, 其他数值定义如下:
- MSG_OOB 传送的数据以out-of-band 送出.
- MSG_DONTROUTE 取消路由表查询
- MSG_DONTWAIT 设置为不可阻断运作
- MSG_NOSIGNAL 此动作不愿被SIGPIPE 信号中断.