多年前写的笔记,同步上传下。
一、基础socket函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include  <sys/socket.h>  int  socket (int  domain, int  type, int  protocol) ;         int  bind (int  sockfd, struct  sockaddr *myaddr, socklen_t  addrlen) ;    int  listen (int  sockfd, int  backlog) ; int  accept (int  sockfd, struct  sockaddr *addr, socklen_t  *addrlen) ;int  connect (int  sockfd, struct  sockaddr *serv_addr, socklen_t  addrlen) ;  
1.1常用TCP连接方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include  <stdio.h>  #include  <sys/socket.h>  int  main (int  argc, char  *argv[]) {	if (argc!=3 ){ 		printf ("Usage : %s <ip> <port>\n" , argv[0 ]); 		exit (1 ); 	} 	const  char * ip = argv[1 ];   char * port = argv[2 ];      struct  sockaddr_in  serv_addr ;      int  serv_sock=socket(PF_INET, SOCK_STREAM, 0 ); 	   int  ret = bind(serv_sock, (struct  sockaddr*) &serv_addr, sizeof (serv_addr); 	   ret = listen(serv_sock, 5 ); 	   struct  sockaddr_in clnt_addr; 	socklen_t  clnt_addr_size = sizeof (clnt_addr); 	int  connfd = accept(serv_sock, (struct  sockaddr*)&clnt_addr, &clnt_addr_size); 	assert(connfd>0 ); 	printf ("a client has been connected!\n" );         } 
1.2 socket选项 1 2 3 4 5 6 #include  <sys/socket.h>  int  getsockopt (int  sockfd, int  level, int  option_name, void * option_value, 						socklen_t * restrict  option_len) ;						 int  setsockopt (int  sockfd, int  level, int  option_name, void * option_value, 						socklen_t * restrict  option_len) ;
二、网络地址信息初始化 结构体sockaddr_in以及IP地址转换函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include  <arpa/inet.h>  struct  sockaddr_in  {    sa_family_t      sin_family;        uint16_t         sin_port;          struct  in_addr   sin_addr;          char             sin_zero[8 ];   } struct  in_addr  {    in_addr_t       s_addr;         } unsigned  short  htons (unsigned  short ) unsigned  short  ntohs (unsigned  short ) unsigned  long  htonl (unsigned  long ) unsigned  long  ntohl (unsigned  long ) in_addr_t  inet_addr (const  char * string) int  inet_aton (const  char * string, struct  in_addr* addr) char * inet_ntoa (struct  in_addr adr) int  inet_pton (int  af, const  char * src, void * dst) const  char * inet_ntop (int  af, const  void * src, char * dst, socklen_c cnt) 
2.1套接字创建过程中常见的网络地址信息初始化方法: 步骤:
声明一个sockaddr_in(用于IPV4) 
将struct全部置为0 
sin_family  
Sin_addr.s_addr (将点分十进制IP转为32位整数) 
Sin_port (将字符串转为整数,并转为大端序) 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include  <arpa/inet.h>  #include  <string.h>  #include  <stdlib.h>  char  *ip = "192.168.0.109" ;          char  *port = "9190" ;                  struct  sockaddr_in  addr ;memset (&addr, 0 , sizeof (addr)); 	addr.sin_family = AF_INET;                addr.sin_addr.s_addr = inet_addr(ip);  addr.sin_port = htons(atoi(port));  
三、基础IO函数 1 2 3 4 5 6 7 8 #include  <sys/types.h>   #include  <sys.stat.h>  #include  <fcntl.h>         int  open (const  char  *path, int  flag) ;                  
注意包含的头文件区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <unistd.h>                          int  close (int  fd) ;ssize_t  read (int  fd, void * buf, size_t  nbytes) ; ssize_t  write (int  fd, const  void * buf, size_t  nbytes) ;
sock编程接口提供了几个专门用于socket数据读写的系统调用,它们增加了对数据读写的控制,比如传输带外数据。
1 2 3 4 5 6 #include  <sys/types.h>  #include  <sys/socket.h>  ssize_t  recv (int  sockfd, void * f, size_t  len, int  flags) ;ssize_t  send (int  sockfd, const  void * buf, size_t  len, int  flags) ;
四、IO复用基础函数 4.1 EPOLL函数 三步走,注册内核事件表->向事件表中添加要监听的文件描述符->调用epoll_wait监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include  <sys/epoll.h>  int  epoll_create (int  size) ;int  epoll_ctl (int  epfd, int  op, int  fd, struct  epoll_event* event) ;int  epoll_wait (int  epfd, struct  epoll_event* events, int  maxevents, int  timeout) ;
其中涉及的epoll_event结构体
1 2 3 4 5 6 7 8 9 10 11 12 struct  epoll_event {     __uint32_t  events;     epoll_data_t  data; } typedef  union  epoll_data   // 注意:epoll_data  是一个联合不是结构体{     void * ptr;     int  fd;     __uint32_t  u32;     __uint64_t  u64;     }epoll_data_t ; 
4.1.1 epoll的常规用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include  <sys/epoll.h>  #define  MAX_EVENT_NUMBER 1024 epoll_event events[MAX_EVENT_NUMBER];  int  epollfd = epoll_create(5 );assert(epollfd!=-1 ); epoll_event event; event.data.fd = socket; event.events = EPOLLIN; epoll_ctl(epollfd, EPOLL_CTL_ADD, socket, &events); int  ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1 ); int  i;for (i=0 ; i<ret; i++){  int  sockfd = events[i].data.fd;    } 
4.2 POLL函数用法 poll函数与select函数相似,也是指定时间轮询一定数量的文件描述符。
1 2 3 4 5 6 7 8 9 10 11 12 #include  <poll.h>  int  poll (struct  pollfd* fds, nfds_t  nfds, int  timeout) ;struct  pollfd {  int  fd;   short  events;	   short  revents;  } typedef  unsigned  long  int  nfds_t ;
4.2.1 poll的常规用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include  <poll.h>  #define  MAX_EVENT_NUMBER 64 pollfd fds[MAX_EVENT_NUMBER]; fds[0 ].fd = sockfd; fds[0 ].events = POLLIN; fds[0 ].revents = 0 ; fds[1 ].fd = sockfd2; fds[1 ].events = POLLIN; fds[1 ].revents = 0 ; int  ret = poll(fds, MAX_EVENT_NUMBER, -1 ); int  i;for (i=0 ; i<MAX_EVENT_NUMBER; i++) {   if (fds[i].revents & EPOLLIN)   {     int  sockfd = fds[i].fd;        } } 
五、高级IO函数 5.1 pipe函数 创建管道
1 2 3 4 #include  <unistd.h>  int  pipe (int  fd[2 ]) ;
创建双向管道
5.2 spilice函数 splice函数用于在两个文件描述符间移动数据,零拷贝操作(CPU不需要先将数据从某处内存复制到另一个特定区域)。
1 2 3 4 5 6 7 #include  <fcntl.h>  ssize_t  splice (int  fd_in, loff_t * off_in, int  fd_out, loff_t * off_out, 							size_t  len, unsigned  int  flags) ;
注意: splice函数中的两个文件描述符必须有一个是管道文件描述符。
当文件描述符为管道文件描述符时,偏移量必须设置为NULL。
5.3 fcntl函数 fcntl函数提供了对文件描述符的各种控制操作。
1 2 3 4 #include  <fcntl.h>  int  fcntl (int  fd, int  cmd, ...) ;
常用来设置非阻塞套接字。
1 2 3 4 5 6 int  setnonblocking (int  fd	int  old_option = fcntl(fd, F_GETFL); 	int  new_option = old_option|O_NONBLOCK; 	fcntl(fd, F_SETFL, new_option); 	return  old_option; } 
六、线程相关函数 6.1 线程 6.1.1 线程创建 1 2 3 4 5 6 7 8 #include  <pthread.h>  int  pthread_create (pthread_t * restrict  thread, const  pthread_attr_t * restrict  attr,                     void *(* start_routine)(void  *), void * restrict  arg) ;
restrict  是 C99 引入的一种类型限定符,它告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容。
 
6.1.2 线程销毁 1 2 3 4 5 6 #include  <pthread.h>  int  pthread_join (pthread_t  thread, void ** status) ;
注意:pthread_join会阻塞主函数,直到子线程结束。
1 2 3 4 5 #include  <pthread.h>  int  pthread_detach (pthread_t  thread) ;
== pthread_detach与join区别?==
销毁线程的两种方法 
Linux 并不会自动销毁由线程创建的内存空间,要使用如下两种方法来明确销毁线程:
调用 pthread_join  函数。此函数不仅会等待指定的线程终止,还会引导线程销毁。 
调用 pthread_detach  函数。此函数会将主线程与指定的子线程分离,分离后的子线程执行结束时,资源会自动回收。 
 
理解:pthread 有 joinable 和 unjoinable 两种状态:
joinable 状态:默认状态。当线程函数执行结束时或 pthread_exit 时不会释放线程所占用堆栈和线程描述符等资源。只有当调用了 pthread_join 之后这些资源才会被释放。 
unjoinable 状态:线程占用的资源会在线程函数退出时或 pthread_exit 时自动释放。pthread_detach() 函数就是分离线程,即将线程状态转换为 unjoinable 状态,以保证资源的释放。 
 
此外 unjoinable 属性也可以在 pthread_create 时指定。
6.2 线程同步 6.2.1 互斥锁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include  <pthread.h>  int  pthread_mutex_init (pthread_mutex_t * mutex, const  pthread_mutexattr_t * attr) ;int  pthread_mutex_destory (pthread_mutex_t * mutex) ;int  pthread_mutex_lock (pthread_mutex_t * mutex) ;    int  pthread_mutex_unlock (pthread_mutex_t * mutex) ;  
6.2.2 信号量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include  <semaphore.h>  int  sem_init (sem_t * sem, int  pshared, unsigned  int  value) ;int  sem_destory (sem_t * sem) ;int  sem_wait (sem_t * sem) ;  int  sem_post (sem_t * sem) ;  
6.2.3 条件变量 xx
七、进程相关函数 xx
八、信号处理 目标进程在收到一个信号时,需要定义一个信号处理函数,原型如下
1 2 3 #include  <signal.h>  typedef  void  (*__sighandler_t )  (int ) ;
Linux可用信号定义在 bits/signum.h头文件中,常用的信号有
SIGINT:输入CTRL+C 
SIGALARM 
SIGPIPE:向读端被关闭的管道写数据或socket连接中写数据 
SIGCHLD:子进程终止 
SIGTERM 
SIGURG:socket收到紧急数据 
SIGIO 
 
8.1 信号函数 为一个信号设置处理函数,可以利用以下的系统调用:
1 2 3 4 5 6 #include  <signal.h>  _sighandler_t  signal  (int  sig, _sighandler_t  _handler) ;
或者更robust的系统调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include  <signal.h>  int  sigaction (int  sig, const  struct  sigaction* act, struct  sigaction* oact) ;struct  sigaction {  _sighandler_t  sa_handler;   _sigset_t  sa_mask;   int  sa_flags; }; int  sigfillset (sigset_t  * set ) ;
8.2 统一事件源 将信号和IO统一处理,统一监听。典型的处理方案:
信号处理函数收到信号时,仅将信号传递给主循环 
主循环统一监听套接字,判断为信号时再处理 
 
如何将信号传递给主循环呢?
利用管道:信号处理函数往管道写端写入信号值,主循环监听管道读端。