• 【socket网络编程】记录博(Linux、C)


    最近在写网络安全实验,感觉欠缺的东西太多了,边写边总结记录一下。暂时有点乱,等实验写完搞懂了再整理

    Linux+C

    地址转换

    • 头文件<arpa/inet.h>

    • 函数原型in_addr_t inet_addr(const char* cp):将字符串形式的IP地址 -> 网络字节顺序 的整型值

    • 返回值in_addr_t一般为32位unsigned int,若字符串有效,则将字符串转换为32位二进制网络字节序的IPV4地址;否则,为INADDR_NONE

    char *inet_ntoa(struct in_addr):网络字节顺序的整型值 ->字符串形式的IP地址(点分十进制)

    • 参数:in_addr为结构体,表示32位IP地址
      struct in_addr{
               in_addr_t s_addr;        //32位unsigned int IPv4地址
      }
    

    sockaddr和sockaddr_in

    sockaddr :

    • 头文件<sys/socket.h>
    • 结构体定义
    struct sockaddr {  
         sa_family_t sin_family;//地址族
        char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息              }; 
    

    sockaddr_in:

    • 头文件<netinet/in.h>或<arpa/inet.h>
    • 结构体定义
    struct sockaddr_in{
        sa_family_t		sin_family;		//套接字地址结构的地址族
        uint16_t		sin_port;		//16位TCP/UDP端口号
        struct in_addr	sin_addr;		//32位IP地址
        char			sin_zero[8];	//不使用
    }
    

    区别与联系

    • sockaddr通用套接字地址,bind、connect、recvfrom、sento等函数参数
    • sockaddr_in:网络编程环境下套接字的地址形式;
    • 可以先把sockaddr_in变量赋值后,强制类型转换后传入sockaddr作参数的函数 例如:
    struct sockaddr_in mysock;
    sockfd = socket(AF_INET,SOCK_STREAM,0);  //获得fd 
    bzero(&mysock,sizeof(mysock));  //初始化结构体
    mysock.sin_family = AF_INET;  //设置地址家族
    mysock.sin_port = htons(800);  //设置端口
    mysock.sin_addr.s_addr = inet_addr("192.168.1.0");  //设置地址
    bind(sockfd,(struct sockaddr *)&mysock,sizeof(struct sockaddr); /* bind的时候进行转化 */
    

    IPv4头部:

    //struct ip和struct iphdr是同一基础结构的两个不同定义,它们是从不同的地方引入的。
    //struct ip在<netinet / ip.h>中定义,它是UNIX系统上的合理标准头。
    //struct iphdr在<linux / ip.h>中定义。该头文件(和结构)特定于Linux,在其他操作系统中将不存在。
    
    struct iphdr {
    #if defined(__LITTLE_ENDIAN_BITFIELD)
        __u8    ihl:4,
                version:4;
    #elif defined (__BIG_ENDIAN_BITFIELD)
        __u8    version:4,
                ihl:4;
    #else
    #error "Please fix <asm/byteorder.h>"
    #endif
        __u8    tos;
        __be16 -tot_len;
        __be16 -id;
        __be16 -frag_off;
        __u8    ttl;
        __u8    protocol;
        __be16 -check;
        __be32 -saddr;
        __be32 -daddr;
    };
    
    struct ip {
    #if BYTE_ORDER == LITTLE_ENDIAN 
        u_char  ip_hl:4,        /* header length */
            ip_v:4;         /* version */
    #endif
    #if BYTE_ORDER == BIG_ENDIAN 
        u_char  ip_v:4,         /* version */
            ip_hl:4;        /* header length */
    #endif
        u_char  ip_tos;         /* type of service */
        short   ip_len;         /* total length */
        u_short ip_id;          /* identification */
        short   ip_off;         /* fragment offset field */
    #define IP_DF 0x4000            /* dont fragment flag */
    #define IP_MF 0x2000            /* more fragments flag */
        u_char  ip_ttl;         /* time to live */
        u_char  ip_p;           /* protocol */
        u_short ip_sum;         /* checksum */
        struct  in_addr ip_src,ip_dst;  /* source and dest address */
    };
    

    IPV4数据报头部格式

    IPv4数据报头头部格式表

    TCP头部

    struct tcphdr {
     __be16 source;      	//16位源端口号
     __be16 dest;        	//16位目的端口号
     
                          	//每个tcp段都包源和目的端口号,用于寻找发送端和接受端的应用进程。
    						//这两个端口号加上ip报头中的源ip和目的ip,来确定一个唯一的TCP连接。
     __be32 seq;         	
        		//此次发送的数据在整个报文段中的起始字节数。此序号用来标识从tcp发送端向tcp接受端发送的数据字节流,
    			//seq表示在这个报文段中的第一个数据字节。如果将字节流看做在两个应用程序间的单向流动,
    			//则tcp用序号对每个字节进行计数。32 bit的无符号数。为了安全起见,它的初始值是一个随机生成的数,	
    			//它到达2的32次方-1后又从零开始。
     __be32 ack_seq;     	
        		//是下一个期望接收的字节,确认序号应当是上次已成功接收的序号+1,只有ack标志为1时确认序号字段才有效。
    			//一旦一个连接已经建立了,ack总是=1
    #if defined(__LITTLE_ENDIAN_BITFIELD)  	//小端
     __u16 res1:4, 	 // 保留位
      doff:4,  		
        		//tcp头部长度,指明了在tcp头部中包含了多少个32位的字。由于options域的长度是可变的,
    			//所以整个tcp头部的长度也是变化的。4bit可表示最大值15,故15*32=480bit=60字节,所以tcp首部最长60字节
    			//然后,没有任选字段,正常的长度是20字节
      fin:1, 		//发端完成发送任务
      syn:1, 		//同步序号用来发起一个连接
      rst:1, 		//重建连接
      psh:1, 		//接收方应该尽快将这个报文段交给应用层
      ack:1,  		//一旦一个连接已经建立了,ack总是=1
     
      urg:1,  		//紧急指针有效
      ece:1, 
      cwr:1;
    #elif defined(__BIG_ENDIAN_BITFIELD)
     __u16 doff:4,
      res1:4,
      cwr:1,
      ece:1,
      urg:1,
      ack:1,
      psh:1,
      rst:1,
      syn:1,
      fin:1;
    #else
    #error "Adjust your <asm/byteorder.h> defines"
    #endif 
     __be16 window;   	
        		//窗口大小,单位字节数,指接收端正期望接受的字节,16bit,故窗口大小最大为16bit=1111 1111 1111 1111(二进制)
    			//=65535(十进制)字节
     __sum16 check;  	
        		//校验和校验的是整个tcp报文段,包括tcp首部和tcp数据,这是一个强制性的字段,一定是由发端计算和存储,
    			//并由收端进行验证。
     __be16 urg_ptr;		//指示紧急数据在当前数据段中的位置
    };
    
     |----------------|----------------|-------------
     |     source     |     dest       |
     |----------------|----------------|
     |               seq               |
     |---------------------------------|
     |               ack_seq           | 20 Bytes
     |----|----|------|----------------|
     |doff|res1|      |     window     |
     |----|----|------|----------------|
     |     check      |     urg_ptr    |
     |----------------|----------------|-------------
     |             options             | 4 Bytes
     |---------------------------------|   
    

    TCP伪首部

    参考博客:jiangqin115

    • 概念:信息是从数据报所在IP分组头的分组头中提取的,既不向下传送也不向上递交,而仅仅是为计算校验和。
    • 既校验了TCP&UDP用户数据的源端口号和目的端口号以及TCP&UDP用户数据报的数据部分,又检验了IP数据报的源IP地址和目的地址。伪报头保证TCP&UDP数据单元到达正确的目的地址
    -----------------------------------------
    |         32bit Source IP address       |
    -----------------------------------------
    |         32bit Destination IP addr     |
    -----------------------------------------
    |  0   | 8bit Proto| 16bit header length|
    -----------------------------------------
    
    //伪头部:用于TCP/UDP计算CheckSum
    //填充字段值来自IP层
    typedef struct tag_pseudo_header
    {
    u_int32_t source_address; //源IP地址
    u_int32_t dest_address; //目的IP地址
    u_int8_t  placeholder; //必须置0,用于填充对齐
    u_int8_t  protocol; //8为协议号(IPPROTO_TCP=6,IPPROTO_UDP=17)
    u_int16_t tcplength; //UDP/TCP头长度(不包含数据部分)
    }PseudoHeader_S;
    

    网络字节顺序与本地字节顺序之间的转换

    本部分参考博客:https://blog.csdn.net/zhuguorong11/article/details/52300680

    • 头文件<arpa/inet.h>

    • 网络字节顺序NBO:按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题

    • 主机字节顺序HBO:不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关

    • 函数原型:

      //htonl()--"Host to Network Long"
       uint32_t htonl(uint32_t hostlong);   
      //ntohl()--"Network to Host Long"
       uint32_t ntohl(uint32_t netlong);    
      //htons()--"Host to Network Short"
       uint16_t htons(uint16_t hostshort);   
      //ntohs()--"Network to Host Short"
       uint16_t ntohs(uint16_t netshort); 
      

    建立套接字

    • 函数原型:

       #include <sys/types.h>
       #include <sys/socket.h>
       int socket(int domain, int type, int protocol);
      
      • domain:协议簇
        • AF_UNIX(本机通信)
        • AF_INET(TCP/IP – IPv4)
        • AF_INET6(TCP/IP – IPv6)
      • type
        • SOCK_STREAM(TCP流)
        • SOCK_DGRAM(UDP数据报)
        • SOCK_RAW(原始套接字)

    系统调用

    进程

    • 头文件<sys/types.h> and <unistd.h>

    • 函数原型1:pid_t getpid(void):返回当前进程表示;

    • 函数原型2:pid_t getppid(void):返回父进程标识;

    • 返回值:pid_t 定义如下,使用此标识可能是为了更好的移植性;不同的环境可能定义的类型不一样

    sys/types.h:
    typedef short           pid_t;       /* used for process ids */
    

    获取时间

    • 头文件<sys/time.h>
    • 函数原型int gettimeofday(struct timeval *tv, struct timezone *tz)
    • 返回值:tv返回目前的时间、tz所指的结构返回当时地区的信息;返回值 0 成功;-1失败,原因存于errno;
    struct timeval{
    	long tv_sec; //秒
    	long tv_usec;//微秒
    };
    
    struct timezone{
    	int tz_minuteswest;//和greenwich时间查了多少分钟
    	int tz_dsttime; //DST 时间的修正方式
    };
    

    获得本地接口信息ioctl

    ​ ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。

    • 头文件<sys/ioctl.h>
    • 函数原型int ioctl(int fd, int cmd, [int *argc,int argv])
    • 参数:fd为文件描述符,cmd交互协议,参数列表为可变参数arg,依据cmd指定长度和类型;
    • 返回值执行成功返回0,失败返回-1并设置全局变量errno值;常见的errno的值为ENOTTY(error not a typewriter)即第一个数fd指向的不是一个字符设备,不支持icotl操作,这时候应该检查前面的 open 函数是否出错或者设备路径是否正确。

    相关cmd:数据类型为第三个参数类型

    1586240953144

    BUFFSIZ的值

    定义:

    stdio.h:	#ifndef BUFSIZ
    stdio.h:	# define BUFSIZ _IO_BUFSIZ
     
    libio.h:	#define _IO_BUFSIZ _G_BUFSIZ
    _G_config.h:#define _G_BUFSIZ 8192
    

    使用的时候包含<stdio.h>即可

    获取接口信息

    • 应用icotl(socket,SIOCGIFCONF,&ifconf); //获取接口清单
    • ifreq用来配置ip地址,激活接口,配置MTU等接口信息的。其中包含了一个接口的名字和具体内容(是个共用体,有可能是IP地址,广播地址,子网掩码,MAC号,MTU或其他内容)。
    • ifreq包含在ifconf结构中。而 ifconf结构通常是用来保存所有接口的信息的。
    //ifconf 结构体   <net/if.h>   保存所有接口信息 用于检测机器接口的配置
    struct ifconf
    {
    	int ifc_len;        /* size of buffer */
    	union
    	{
    		char *ifcu_buf;     /* input from user->kernel*/
    		struct ifreq *ifcu_req;      /* return from kernel->user*/
    	} ifc_ifcu;
    };
    #define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
    #define ifc_req ifc_ifcu.ifcu_req /* array of structures *
    
    //ifreq用来保存某个接口的信息
    struct ifreq 
    {
    #define IFHWADDRLEN 6
        union{
            char ifr_name[IFNAMSIZ];                /*	if name 例如“en0"*/
        }ifr_ifrn;
    	
    	union {
    		struct sockaddr ifru_addr;              /*	地址*/
    		struct sockaddr ifru_dstaddr;
    		struct sockaddr ifru_broadaddr;         /*广播地址*/
            struct	sockaddr ifru_netmask;			/*子网掩码*/
    		struct  sockaddr ifru_hwaddr; 			/* MAC address */
    		short	ifru_flags;
    		int	ifru_ivalue;
    		int	ifru_mtu; /*是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)*/
    		struct  ifmap ifru_map;
    		char	ifru_slave[IFNAMSIZ];	       /* Just fits the size */
    		char	ifru_newname[IFNAMSIZ];
    		void __user *	ifru_data;             /*当前被使用的接口*/
    		struct	if_settings ifru_settings;
    	} ifr_ifru;
    };
    #define ifr_name	ifr_ifrn.ifrn_name	/* interface name 	*/
    #define ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* MAC address 		*/
    #define	ifr_addr	ifr_ifru.ifru_addr	/* address		*/
    #define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
    #define	ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address	*/
    #define	ifr_netmask	ifr_ifru.ifru_netmask	/* interface net mask	*/
    #define	ifr_flags	ifr_ifru.ifru_flags	/* flags		*/
    #define	ifr_metric	ifr_ifru.ifru_ivalue	/* metric		*/
    #define	ifr_mtu		ifr_ifru.ifru_mtu	/* mtu			*/
    #define ifr_map		ifr_ifru.ifru_map	/* device map		*/
    #define ifr_slave	ifr_ifru.ifru_slave	/* slave device		*/
    #define	ifr_data	ifr_ifru.ifru_data	/* for use by interface	*/
    #define ifr_ifindex	ifr_ifru.ifru_ivalue	/* interface index	*/
    #define ifr_bandwidth	ifr_ifru.ifru_ivalue    /* link bandwidth	*/
    #define ifr_qlen	ifr_ifru.ifru_ivalue	/* Queue length 	*/
    #define ifr_newname	ifr_ifru.ifru_newname	/* New name		*/
    #define ifr_settings	ifr_ifru.ifru_settings	/* Device/proto settings*/
    

    设置套接口的选项

    这一部分来源博客:晟夏的叶

    • 函数原型和头文件

      #include <sys/types.h >
      #include <sys/socket.h>
      int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
      
    • 参数说明

      sockfd:标识一个套接口的描述字

      level:选项定义的层次,支持SOL_SOCKET(套接字级别)、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6

      (IPPROTO_TCP 和 IPPROTO_IP代表两种不同的协议,分别代表IP协议族里面的TCP协议和IP协议)
      

      optname:需设置的选项,而有部分选项需在listen/connect调用前设置才有效,这部分选项如下:SO_DEBUG、SO_DONTROUTE、SO_KEEPALIVE、SO_LINGER、SO_OOBINLINE、SO_RCVBUF、SO_RCVLOWAT、SO_SNDBUF、SO_SNDLOWAT、TCP_MAXSEG、TCP_NODELAY

      optval:指针,指向存放选项值的缓冲区

      optlen:optval缓冲区长度

    • 设置socket的一些选项:

      • 设置收发时限
      int nNetTimeout=1000; // 1秒
      // 发送时限
      setsockopt(socket,SOL_S0CKET, SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
      // 接收时限
      setsockopt(socket,SOL_S0CKET, SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));
      
      • 强制关闭不经历TIME_WAIT
      int reuse=0;
      setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)& reuse,sizeof(int));
      
      • 设置socket缓冲区大小,当收发数据较大时,而避免了send(),recv()不断的循环收发
      // 接收缓冲区
      int nRecvBuf=32*1024;				 // 设置为32K
      setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
      // 发送缓冲区
      int nSendBuf=32*1024; 				// 设置为32K
      setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
      
      • 设置不经过系统缓冲区到socket缓冲区
      int nZero=0;
      setsockopt(socket,SOL_SOCKET,SO_SNDBUF,(char *)&nZero,sizeof(int));
      
      • 同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):

        int nZero=0;
        setsockopt(socket,SOL_SOCKET,SO_RCVBUF,(char *)&nZero,sizeof(int));
        
      • 一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性

      int bBroadcast = 1;
      setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(int));
      
      • 设置存活检测
      int opt = 1;
      if (setsockopt (m_nSock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(int)) == -1)
      {
          return 0;
      }
      
      • 延迟接收
        实际上就是当接收到第一个数据之后,才会创建连接。对于像http这类非交互式的服务器,这个很有意义,可以防御空连接攻击。
      int val = 5;
      setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val));
      

      打开这个功能后,内核在val时间之类还没有收到数据,不会继续唤醒进程,而是直接丢弃连接。
      从三次握手上讲,就是设置这个状态之后,就算完成了三次握手,服务器socket状态也不是ESTABLISHED,而依然是 SYN_RCVD,不会去接收数据。

    关于原始套接字

    参考博客:https://blog.csdn.net/yanyiyyy/article/details/6566871

    大致总结:使用原始套接字可以访问ICMP、IGMP等协议包,可以读写内核不处理的IP数据报,可以创建自定义的IP数据包首部,编写基于IP协议的通讯程序

    • 创建格式int sockfd;sockfd = socktet(AF_INET, SOCK_RAW, IPPROTO_ICMP)
    • :须在root权限下
    • 当需要编写自己的IP数据包首部时,可以在原始套接字上设置套接字选项IP_HDRINCL.在不设置这个选项的情况下,IP协议自动填充IP数据包的首部:
    int on = 1;
    if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0){ 
    	fprintf(stderr, "setsockopt IP_HDRINCL ERROR! /n");
    	exit(1);
    }
    
    • 如果没有设置IP_HDRINCL选项时,包内可写的内容为数据部分,内核将自动创建IP首部。如果设置了IP_HDRINCL选项,则包内要填充的内容为IP数据包和首部。

    关于errno:

    • errno,全局变量:记录系统的最后一次错误代码,为int型的值,由操作系统维护
    • 输出:printf("errno值:%d 错误提示信息:%s ",errno,sterror(errno))
    • 错误码对应的错误类型参考:“明明是悟空”的博客

    perror

    • 头文件:<stdlib.h>
    • 函数体void perror(const char*s)
    • perror ( )用 来 将 上 一 个 函 数 发 生 错 误 的 原 因 输 出 到 标 准 设备 (stderr) 。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno 的值来决定要输出的字符串。

    创建线程

    • 头文件:<pthread.h>
    • 函数原型:成功返回0,否则返回出错编号,
    int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
    
    • 函数参数:

      • restrict tidp:指向线程标识符指针

        typedef unsigned long int pthread_t;  //用途:pthread_t用于声明线程ID。
        sizeof (pthread_t) =4;
        
      • restrict_attr:用来设置线程属性

        typedef struct
        {
             int         			detachstate;	//线程的分离状态
             int                    schedpolicy;	//线程调度策略
             struct sched_param     schedparam;		//线程的调度参数
             int                    inheritsched;	//线程的继承性
             int                    scope;			//线程的作用域
             size_t                 guardsize;		//线程栈末尾的警戒缓冲区大小
             int                    stackaddr_set;
             void *                 stackaddr;      //线程栈的位置
             size_t                 stacksize;		//线程栈的大小
        }pthread_attr_t;
        
      • start_rtn:线程运行函数的地址

      • restrict arg:运行函数的参数

    • :在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库。

    内存拷贝函数

    • 函数原型:void *memcpy(void *destin, void *source, unsigned n)
    • 功能:从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,即从源source中拷贝n个字节到目标destin中。

    sendto 和 recvfrom

    sendto和recvfrom一般用于UDP协议中,但是如果在TCP中connect函数调用后也可以用.

    • 利用数据报方式进行数据传输
    • 在无连接的数据报socket方式下,由于本地socket并没有与远端机器连接,所以在发送数据时应指明目的地址。
    • 头文件<sys/types.h><sys/socket.h>
    • 返回值:如果成功则返回实际传送出去/接收到的字符数,失败则返回-1,错误原因存于errno中

    sendto

    • 函数原型:

      int sendto(int sockfd, const void *msg,int len, unsigned int flags, const struct sockaddr *to, int tolen); 
      
    • 参数:

      • sockfd:套接字
      • msg:消息数据报
      • len:数据报长度
      • flags:一般为0
      • to:目的地址,sockaddr类型(协议族、端口号、IP地址)
      • tolen:对方地址长度,一般为:sizeof(struct sockaddr_in)

    recvfrom

    • 函数原型:

      int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
      
    • 参数同上

    fflush方法

    参考博客:NUAA、无痕

    • 功 能: 清除读写缓冲区,需要立即把输出缓冲区的数据进行物理写入时

    • 头文件:<stdio.h>

    • 原型:int fflush(FILE *stream) 其中stream是要冲洗的流

    • fflush(stdin)刷新标准输入缓冲区,把输入缓冲区里的东西丢弃[非标准],一般用不到。

    • fflush(stdout)刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上

    • printf("字符串");后面加fflush(stdout);可提高打印效率!

    Linux的sleep()、usleep()、nanosleep()区别与联系

    参考博客:专注就可以深

    sleep:

    • 头文件:<unistd.h>
    • 函数原型:unsigned int sleep(unsigned int seconds);
    • 特点:非系统调用,在库函数中实现,通过alarm来设定报警时间,使用sigsuspend()将进程挂起在信号SIGALARM上。sleep()精确到秒级,会令目前的进程暂停,直到达到参数seconds所指定的时间,或是被信号所中断。
    • 返回值:若进程暂停到参数seconds 所指定的时间,成功则返回0,若有信号中断则返回剩余微秒数。

    usleep:

    • 头文件:<unistd.h>
    • 函数原型:unsigned int usleep(unsigned int useconds);
    • 特点:时间以微秒为单位;
    • 返回值:若进程暂停到参数seconds 所指定的时间,成功则返回0,若有信号中断则返回剩余微秒数。

    nanosleep:

    • 头文件:<time.h>

    • 函数原型:

       struct timespec
       {
             time_t  tv_sec;         /* 秒seconds */
             long    tv_nsec;        /* 纳秒nanoseconds */
       };
      int nanosleep(const struct timespec *req, struct timespec *rem)
      
    • 这个函数功能是暂停某个进程直到你规定的时间后恢复,参数req就是你要暂停的时间,其中req->tv_sec是以秒为单位,而tv_nsec以毫微秒为单位(10的-9次方秒)。由于调用nanosleep是是进程进入TASK_INTERRUPTIBLE,这种状态是会相应信号而进入TASK_RUNNING状态的,这就意味着有可能会没有等到你规定的时间就因为其它信号而唤醒。

    • 返回值: 若进程暂停到参数*req所指定的时间,成功则返回0,若有信号中断则返回-1,并且将剩余微秒数记录在*rem中。

    命令行选项函数gotopt()

    • 函数原型:

      int getopt(int argc, char * const argv[], const char *optstring);
      
    • 说明:getopt是用来解析命令行选项参数的,但是只能解析短选项: -d 100,不能解析长选项:--prefix

    • 参数:

      • argcmain()函数传递过来的参数的个数
      • argvmain()函数传递过来的参数的字符串指针数组
      • optstring:选项字符串,告知 getopt()可以处理哪个选项以及哪个选项需要参数
    • 返回值:如果选项成功找到,返回选项字母;如果所有命令行选项都解析完毕,返回 -1;如果遇到选项字符不在 optstring 中,返回字符 '?';如果遇到丢失参数,那么返回值依赖于 optstring 中第一个字符,如果第一个字符是 ':' 则返回':',否则返回'?'并提示出错误信息。

    • optstring 在传入之后,getopt 函数将依次检查命令行是否指定了 -a, -b, -c(这需要多次调用 getopt 函数,直到其返回-1),当检查到上面某一个参数被指定时,函数会返回被指定的参数名称(即该字母)

    socket

    • 函数原型:int socket(int domain, int type, int protocol);

    • 参数:

      • domain:协议域,协议簇(family)

        名称 目的
        AF_INET IPv4网络通信
        AF_INET6 IPv6网络通信
        AF_PACKET 链路层通信
        AF_UNIX, AF_LOCAL 本地通信
      • type:

        type 说明
        SOCK_STREAM(默认) 面向字节流套接字,基于TCP
        SOCK_DGRAM 数据报套接字,基于UDP
        SOCK_SEQPACKET 有序分组套接字
        SOCK_RAW 原始套接字
      • protocol:通常为0,表示选择当前family和type组合下protocol的系统默认值

        IPPROTO_TCP IPPTOTO_UDP IPPROTO_SCTP IPPROTO_TIPCTCP
        TCP传输协议 UDP传输协议 STCP传输协议 TIPC传输协议

    数据格式

    img

    img

    数据与填充拆分出来第一部分都是网络层IP数据报,IP首部:首部格式:

    img

    IP首部的数据部分拆出来可能是:传输层UDP、TCP、ICMP、IGMP。

    UDP:

    img

    img

    TCP:

    img

    img

    img

    ICMP:

    ICMP是在IP数据报的内部被传输的,紧跟着IP报文的首部(如果IP首部有可选部分,则紧跟着可选部分)

    这里写图片描述

    这里写图片描述

    这里写图片描述

  • 相关阅读:
    linux 内核优化
    ip_forward与路由转发
    mysql 集群 galera
    mysql 中间件 mycat
    mysql 主-主-从-从
    mysql 主从复制
    mysql 备份
    mysql 日志
    java中四种权限修饰符区别
    Java中关于Math的几个取整方法的区别
  • 原文地址:https://www.cnblogs.com/wwj321/p/12639778.html
Copyright © 2020-2023  润新知