Live2D

【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\n",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首部有可选部分,则紧跟着可选部分)

这里写图片描述

这里写图片描述

这里写图片描述

posted @ 2020-04-05 23:01  WSquareJ  阅读(348)  评论(0编辑  收藏  举报