IP包的生成和发送接口(1)

http://blog.sina.com.cn/s/indexlist_1657348185_2.html

 

 

IP包的生成和发送接口

====================

(1) Linux内核中有3种基本的IP包生成器, 它们分别为ip_build_xmit(), ip_queue_xmit(),

ip_build_and_send_pkt(). ip_build_and_send_pkt()是一简单的IP包头封装接口,

它接照输入包的路由添加一个IP包头后直接输出,不进行分片处理, 用于tcp_v4_send_synack()中.

ip_send_reply()是基于ip_build_xmit()的一个函数,

用于tcp_v4_send_ack()和tcp_v4_send_reset()中.

 

(2) ip_build_xmit()使用用户定义的回调函数直接读取用户数据片段生成IP包输出.

如果需要分片,ip_build_xmit()按照最后一个片段到第一个片段的顺序来生成IP包,

这是因为第一个IP包片段的数据区可能包含对整个IP包数据区的校验码,

在回调函数中用户可能会计算输出数据的校验码,

采用从后向前的输出顺序可使校验码自然地写到第一个片段中.

 

(3) ip_queue_xmit()完成面向连接套接字输出包的路由和IP包头封装. 当套接字处于连接状态时,

所有从套接字发出的包都具有确定的路由, 无需为每一个输出包查询它的目的入口,

可将套接字直接绑定到路由入口上, 这由套接字的目的缓冲指针(dst_cache)来完成.

ip_queue_xmit()首先为输入包建立IP包头, 经过本地包过滤器后,

再将IP包分片输出(ip_fragment), 如果需要的话.

 

(4) IP包生成器的输出经过本地包过滤器后输入包的路由入口, 对于点播地址来说,

输入到IP输出器中(ip_output); 对于广播或同播地址来说, 输入到IP同播输出器(ip_mc_output).

在IP输出器中, 再经过路由后过滤器,

进入路由的"邻居"入口(dst->neighbour->output)或硬件帧头缓冲入口(dst->hh->hh_output).

邻居是指与主机自已在网络接口设备层次上直达的相邻主机.

邻居负责解析输出包的硬件投送地址, 将包投递给相邻的目的主机或网关主机.

当邻居成功解析包的硬件投送地址时, 将在包的目的入口上创建硬件帧头缓冲结构(dst->hh),

使得后继包可以直接使用组装好的帧头, 直接将包传递给包调度器(dev_queue_xmit).

包调度器按照包的优先级进行重排, 最后将包提交给设备驱动程序发送(dev->hard_start_xmit).

 

 

IP包生成接口

------------

; net/ipv4/ip_output.c:

 

int sysctl_ip_default_ttl = IPDEFTTL; 缺省的IP包生存期为64

 

 

int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,对包的数据体添加IP头后直接输出

                       u32 saddr, u32 daddr, struct ip_options *opt)

{

       struct rtable *rt = (struct rtable *)skb->dst;

       struct iphdr *iph;

 

      

       if (opt)

              iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);

       else

              iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));

 

       iph->version  = 4;

       iph->ihl      = 5;

       iph->tos      = sk->protinfo.af_inet.tos;

       iph->frag_off = 0;

       if (ip_dont_fragment(sk, &rt->u.dst)) 如果IP包的目的入口禁止分片

              iph->frag_off |= htons(IP_DF);

       iph->ttl      = sk->protinfo.af_inet.ttl; 取套接字协议选项中的生存期

       iph->daddr    = rt->rt_dst; 取IP包路由的目的地址

       iph->saddr    = rt->rt_src; 取IP包路由的源地址

       iph->protocol = sk->protocol; 取套接字IP协议代码

       iph->tot_len  = htons(skb->len); IP包总长度

       ip_select_ident(iph, &rt->u.dst); 为IP包分配标识号, 禁止分片的IP包标识为零

       skb->nh.iph   = iph;

 

       if (opt && opt->optlen) {

              iph->ihl += opt->optlen>>2;

              ip_options_build(skb, opt, daddr, rt, 0); 设置IP选项区

       }

       ip_send_check(iph); 设置IP包头的校验和

 

      

       return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,

                     output_maybe_reroute); 过滤输出并且目的路径可能会被改变

}

 

int ip_build_xmit(struct sock *sk,

                int getfrag (const void *,

                            char *,

                            unsigned int,    

                            unsigned int), 取数据片段的函数指针

                const void *frag, 以上函数的调用参数

                unsigned length,

                struct ipcm_cookie *ipc, IP包配置信息

                struct rtable *rt,

                int flags) 从用户数据建立IP包

{

       int err;

       struct sk_buff *skb;

       int df;

       struct iphdr *iph;

 

      

 

       if (!sk->protinfo.af_inet.hdrincl) { 如果IP包头不由用户创建

              length += sizeof(struct iphdr); 取IP包总长

 

             

              if (length > rt->u.dst.pmtu || ipc->opt != NULL) 如果包长度大于目的入口的最大片断长

                     return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags);

       } else {

              if (length > rt->u.dst.dev->mtu) { 如果包长大于目的入口设备的最大片段长

                     ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, rt->u.dst.dev->mtu);

                     return -EMSGSIZE;

              }

       }

       if (flags&MSG_PROBE) 测试操作

              goto out;

 

      

       df = 0;

       if (ip_dont_fragment(sk, &rt->u.dst)) 如果禁止分片

              df = htons(IP_DF);

 

      

       {

       int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;

 

       skb = sock_alloc_send_skb(sk, length+hh_len+15,

                              0, flags&MSG_DONTWAIT, &err); 为套接字分配发送包

       if(skb==NULL)

              goto error;

       skb_reserve(skb, hh_len); 保留硬件帧头空间

       }

 

       skb->priority = sk->priority; 取套接字的优先级

       skb->dst = dst_clone(&rt->u.dst); 取路由的目的入口

 

       skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);

 

       if(!sk->protinfo.af_inet.hdrincl) {

              iph->version=4;

              iph->ihl=5;

              iph->tos=sk->protinfo.af_inet.tos;

              iph->tot_len = htons(length);

              iph->frag_off = df;

              iph->ttl=sk->protinfo.af_inet.mc_ttl;

              ip_select_ident(iph, &rt->u.dst);

              if (rt->rt_type != RTN_MULTICAST)

                     iph->ttl=sk->protinfo.af_inet.ttl;

              iph->protocol=sk->protocol; 

              iph->saddr=rt->rt_src;

              iph->daddr=rt->rt_dst;

              iph->check=0;

              iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);

              err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);

              ; 读取用户一片数据

       }

       else 如果IP包头由用户创建, 直接将用户数据读入IP头所在位置

              err = getfrag(frag, (void *)iph, 0, length);

 

       if (err)

              goto error_fault;

 

       err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,

                    output_maybe_reroute);

       if (err > 0)

              err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : 0;

       if (err)

              goto error;

out:

       return 0;

 

error_fault:

       err = -EFAULT;

       kfree_skb(skb);

error:

       IP_INC_STATS(IpOutDiscards);

       return err;

}

static int ip_build_xmit_slow(struct sock *sk,

                int getfrag (const void *,

                            char *,

                            unsigned int,    

                            unsigned int),

                const void *frag,

                unsigned length,

                struct ipcm_cookie *ipc,

                struct rtable *rt,

                int flags) 建立IP选项区或者分片输出

{

       unsigned int fraglen, maxfraglen, fragheaderlen;

       int err;

       int offset, mf;

       int mtu;

       u16 id = 0;

 

       int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;

       int nfrags=0;

       struct ip_options *opt = ipc->opt;

       int df = 0;

 

       mtu = rt->u.dst.pmtu;

       if (ip_dont_fragment(sk, &rt->u.dst))

              df = htons(IP_DF);

 

       length -= sizeof(struct iphdr);

 

       if (opt) {

              fragheaderlen = sizeof(struct iphdr) + opt->optlen;

              maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;

       } else {

              fragheaderlen = sizeof(struct iphdr);

 

             

 

              maxfraglen = ((mtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;

       } 求最大IP包长

 

       if (length + fragheaderlen > 0xFFFF) {

              ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);

              return -EMSGSIZE;

       }

 

      

 

       offset = length - (length % (maxfraglen - fragheaderlen));取最后一个片段的数据偏移量

 

      

 

       fraglen = length - offset + fragheaderlen; 求取后一个片段IP包全长

 

       if (length-offset==0) { 如果用户数据恰好是最大单片数据长度的整数倍

              fraglen = maxfraglen;

              offset -= maxfraglen-fragheaderlen;

       }

 

      

 

       mf = 0;

 

      

 

       if (offset > 0 && sk->protinfo.af_inet.pmtudisc==IP_PMTUDISC_DO) {

              ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);

             return -EMSGSIZE;

       }

       if (flags&MSG_PROBE)

              goto out;

 

      

 

       do {

              char *data;

              struct sk_buff * skb;

 

             

 

              skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &err);

              if (skb == NULL)

                     goto error;

 

             

 

              skb->priority = sk->priority;

              skb->dst = dst_clone(&rt->u.dst);

              skb_reserve(skb, hh_len);

 

             

 

              data = skb_put(skb, fraglen);

              skb->nh.iph = (struct iphdr *)data;

 

             

 

              {

                     struct iphdr *iph = (struct iphdr *)data;

 

                     iph->version = 4;

                     iph->ihl = 5;

                     if (opt) {

                            iph->ihl += opt->optlen>>2;

                            ip_options_build(skb, opt,

                                           ipc->addr, rt, offset);

                     }

                     iph->tos = sk->protinfo.af_inet.tos;

                     iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4);

                     iph->frag_off = htons(offset>>3)|mf|df;

                     iph->id = id;

                     if (!mf) {

                            if (offset || !df) {

                                  

                                   __ip_select_ident(iph, &rt->u.dst);

                                   id = iph->id;

                            }

 

                           

                            mf = htons(IP_MF);

                     }

                     if (rt->rt_type == RTN_MULTICAST)

                            iph->ttl = sk->protinfo.af_inet.mc_ttl;

                     else

                            iph->ttl = sk->protinfo.af_inet.ttl;

                     iph->protocol = sk->protocol;

                     iph->check = 0;

                     iph->saddr = rt->rt_src;

                     iph->daddr = rt->rt_dst;

                     iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);

                     data += iph->ihl*4;

              }

 

             

 

              if (getfrag(frag, data, offset, fraglen-fragheaderlen)) {

                     err = -EFAULT;

                     kfree_skb(skb);

                     goto error;

              }

 

              offset -= (maxfraglen-fragheaderlen); 片段从后向前进行分割, 是为了方便TCP包的校验

              fraglen = maxfraglen;

 

              nfrags++;

 

              err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,

                           skb->dst->dev, output_maybe_reroute);

              if (err) {

                     if (err > 0)

                            err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : 0;

                     if (err)

                            goto error;

              }

       } while (offset >= 0);

 

       if (nfrags>1)

              ip_statistics[smp_processor_id()*2 + !in_softirq()].IpFragCreates += nfrags;

out:

       return 0;

 

error:

       IP_INC_STATS(IpOutDiscards);

       if (nfrags>1)

              ip_statistics[smp_processor_id()*2 + !in_softirq()].IpFragCreates += nfrags;

       return err;

}

 

 

void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg,

                 unsigned int len)

{

       struct {

              struct ip_options    opt;

              char               data[40]; 存放IP选项块

       } replyopts;

       struct ipcm_cookie ipc;

       u32 daddr;

       struct rtable *rt = (struct rtable*)skb->dst;

 

       if (ip_options_echo(&replyopts.opt, skb)) 将包skb的IP选项刷新到replyopts结构中

              return;

 

       daddr = ipc.addr = rt->rt_src;

       ipc.opt = NULL;

 

       if (replyopts.opt.optlen) {

              ipc.opt = &replyopts.opt;

 

              if (ipc.opt->srr)

                     daddr = replyopts.opt.faddr;

       }

 

       if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), 0))

              return;

 

      

       bh_lock_sock(sk);

       sk->protinfo.af_inet.tos = skb->nh.iph->tos;

       sk->priority = skb->priority;

       sk->protocol = skb->nh.iph->protocol;

       ip_build_xmit(sk, ip_reply_glue_bits, arg, len, &ipc, rt, MSG_DONTWAIT);

       bh_unlock_sock(sk);

 

       ip_rt_put(rt);

}

struct ip_reply_arg {

       struct iovec iov[2];  

       int          n_iov;   

       u32             csum;

       int         csumoffset;

                             

};

 

static int ip_reply_glue_bits(const void *dptr, char *to, unsigned int offset,

                           unsigned int fraglen)

{

      struct ip_reply_arg *dp = (struct ip_reply_arg*)dptr;

       u16 *pktp = (u16 *)to;

       struct iovec *iov;

       int len;

       int hdrflag = 1;

 

       iov = &dp->iov[0];

       if (offset >= iov->iov_len) {

              offset -= iov->iov_len;

              iov++;

              hdrflag = 0;

       }

       len = iov->iov_len - offset;

       if (fraglen > len) {

              dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, len,

                                        dp->csum);

              offset = 0;

              fraglen -= len;

              to += len;

              iov++;

       }

 

       dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, fraglen,

                                        dp->csum);

 

       if (hdrflag && dp->csumoffset)

              *(pktp + dp->csumoffset) = csum_fold(dp->csum);

       return 0;        

}

 

int ip_queue_xmit(struct sk_buff *skb)

{

       struct sock *sk = skb->sk;

       struct ip_options *opt = sk->protinfo.af_inet.opt;

       struct rtable *rt;

       struct iphdr *iph;

 

      

       rt = (struct rtable *)__sk_dst_check(sk, 0); 取套接字所缓冲的发送包的目的路由入口

       if (rt == NULL) { 如果尚未缓冲

              u32 daddr;

 

             

              daddr = sk->daddr; 取套接字的对端地址作为目的地址

              if(opt && opt->srr) 如果具有信源路由选项

                     daddr = opt->faddr; 取信源路由的转发地址作为目的地址

 

             

              if (ip_route_output(&rt, daddr, sk->saddr,

                                RT_TOS(sk->protinfo.af_inet.tos) | RTO_CONN | sk->localroute,

                                sk->bound_dev_if)) 查询目的地址的路由目的入口

                     goto no_route;

              __sk_dst_set(sk, &rt->u.dst); 将该路由入口缓冲到套接字上

       }

       skb->dst = dst_clone(&rt->u.dst); 将路由入口绑定到发送包

 

       if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)

              goto no_route; 如果是指定严格信源路由并且其转发地址不等于网关地址,则操作失败

 

      

       iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen :0));

       *((__u16 *)iph)      = htons((4 << 12) | (5 << 8) | (sk->protinfo.af_inet.tos & 0xff));

       iph->tot_len = htons(skb->len);

       iph->frag_off = 0;

       iph->ttl      = sk->protinfo.af_inet.ttl;

       iph->protocol = sk->protocol;

       iph->saddr    = rt->rt_src;

       iph->daddr    = rt->rt_dst;

       skb->nh.iph   = iph;

      

 

       if(opt && opt->optlen) { 建立IP选项区

              iph->ihl += opt->optlen >> 2;

              ip_options_build(skb, opt, sk->daddr, rt, 0);

       }

 

       return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,

                     ip_queue_xmit2); 过滤输出

 

no_route:

       IP_INC_STATS(IpOutNoRoutes);

       kfree_skb(skb);

       return -EHOSTUNREACH;

}

 

转载于:https://www.cnblogs.com/jinrize/archive/2009/11/28/1612584.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/410254.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.net复习之七

表A&#xff1a; 表B&#xff1a; 1&#xff0e; SELECT * FROM A JOIN B ON A.Id B.Id 將顯示 9 條數據。 Inner join(等值连接)只返回两个表中联结字段相等的行 2&#xff0e; SELECT * FROM A LEFT JOIN B ON A.Id B.Id 將顯示 12 條數據…

原生js实现tab栏切换效果

我是歌谣 放弃很容易 但是坚持一定很酷 微信公众号关注小歌谣一起学习前后端知识 运行效果 首先我们来看一下原生js实现的效果 下面就开始直接上代码了 index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"&…

线程八大核心+java并发核心知识体系精讲_Java从业者如果不懂这些,面试80%都会挂在这些核心知识上面...

JVM无论什么级别的Java从业者&#xff0c;JVM都是进阶时必须迈过的坎。不管是工作还是面试中&#xff0c;JVM都是必考题。如果不懂JVM的话&#xff0c;薪酬会非常吃亏(近70%的面试者挂在JVM上了)详细介绍了JVM有关于线程、内存模型、JVM运行时内存、垃圾回收与算法、Java中四种…

Ajax技术简单入门

随着Google公司推出的Gmail服务后,越来越多的人开始关注Ajax技术了,所谓Ajax(Asynchronous JavaScript and XML缩写)技术,就是指运用JavaScript和XML在不用刷新Web页的情况下与Web服务器通信的技术&#xff0e;一般来说&#xff0c;使用Ajax技术主要有两个原因&#xff1a;一是…

我所知的javascript之prototype

一&#xff1a;prototype大概概念和用途“prototype”字面翻译是“原型”&#xff0c;是javascript实现继承的主要手段。粗略来说就是&#xff1a;prototype是javascript中的函数(function)的一个保留属性&#xff0c;并且它的值是一个对象&#xff08;我们可以称这个对象为&qu…

哪些模块可用于python性能分析_Python调用C模块以及性能分析

一.c&#xff0c;ctypes和python的数据类型的对应关系ctypes type ctype Python typec_char char 1-character stringc_wchar wchar_t 1-character unicode stringc_byte char int/longc_ubyte unsigned char int/longc_short short int/longc_ushort unsigned short int/longc…

[html] webp与jpg、png比较,它有什么优劣势?如何选择?

[html] webp与jpg、png比较&#xff0c;它有什么优劣势&#xff1f;如何选择&#xff1f; 优势更优的图像数据压缩算法 带来更小的图片体积肉眼识别无差异的图片质量支持有损和无损压缩支持动画 透明色彩丰富 24-bit颜色数劣势存在兼容性问题选择​ 当 图片较少 体积不大 且存…

Alt Gr or Shift

This is interesting. The keyboard here is different from what we always use in China. Characters and layout are different. Hans gave me the password to logon the system. There s a “” character in it. With Chinese keyboard, I will use “Shift 2″ to gener…

Vue之前端页面使用json编辑框

转自: https://blog.csdn.net/Wjhsmart/article/details/85757045 转载于:https://www.cnblogs.com/jiushixihuandaqingtian/p/11310713.html

[html] html5的video如何附带字幕?

[html] html5的video如何附带字幕&#xff1f; <video controls width"400" height"300"> <source src"../hangge.mp4" type"video/mp4"> <track src"hangge.vtt" srclang"zh" kind"subtitl…

window.addeventlistener 不能调用方法_Java入门第十四课:如何定义”方法“

第十四课&#xff0c;学习定义方法。一个对象包含三种最常见的成员&#xff1a;构造器、Field和方法。Field用于定义状态数据&#xff0c;而方法是行为特征的抽象。那么什么是方法呢&#xff1f;在Java中&#xff0c;方法就是用来完成解决某件事情或实现某个功能的办法。方法实…

remmina连接xfce桌面的centos7

vnc无法连到linux server&#xff0c;但ssh可以的解决方法 原文引自&#xff1a;https://blog.csdn.net/h00ahaha/article/details/84440449 今天用vnc连远程服务器&#xff0c;一直给我提示Failed to connect to server.记录下解决该问题的步骤&#xff1a;1 确认ssh能登录&am…

[html] 你有使用过html5的rt标签吗?它有什么应用场景?

[html] 你有使用过html5的rt标签吗&#xff1f;它有什么应用场景&#xff1f; <ruby>汉 <rt>Hn</rt>字 <rt>Z</rt> </ruby>个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大…

深度测试与alpha混合(3)

alpha源混合系数通常设置为D3DBLEND_SRCALPHA&#xff0c;即当前绘制像素的alpha值。目标混合系数设置为D3DBLEND_INVSRCALPHA&#xff0c;即1减去当前绘制像素的alpha值。那么当前绘制像素的alpha值又是如何得到的呢&#xff1f;如果没有使用材质和纹理&#xff0c;当前绘制像…

python开发商城实战_python框架Django实战商城项目之工程搭建

项目说明该电商项目类似于京东商城&#xff0c;主要模块有验证、用户、第三方登录、首页广告、商品、购物车、订单、支付以及后台管理系统。项目开发模式采用前后端不分离的模式&#xff0c;为了提高搜索引擎排名&#xff0c;页面整体刷新采用jinja2模板引擎实现&#xff0c;局…

算法之排序算法-shell排序(移位法)

个人觉得移位法就是借希尔排序进行分组,插入排序进行排序 注释是上一篇的交换法. 而且这种移位排序的真的很快 package com.ebiz.sort;import java.text.SimpleDateFormat; import java.util.Date;/*** author YHj* create 2019-07-30 8:53* shell排序-交换法*/ public class S…

[html] 页面布局时你使用最多的标签是什么?div吗?在什么情况下会使用到div?

[html] 页面布局时你使用最多的标签是什么&#xff1f;div吗&#xff1f;在什么情况下会使用到div&#xff1f; 页面的的整体布局使用<header> <main> <aside> <footer> 等。 一些细分的布局使用<div>&#xff0c;并指定适当的class、role和ari…

Effulgent的《深入理解Direct3D9》整理版(转)

深入理解Direct3D9 深入理解D3D9对图形程序员来说意义重大&#xff0c;我把以前的一些学习笔记都汇总起来&#xff0c;希望对朋友们有些所帮助&#xff0c;因为是零散笔记&#xff0c;思路很杂&#xff0c;还请包涵。 其实只要你能完美理解D3DLOCK、D3DUSAGE、D3DPOOL、LOST DE…

我的世界光影mod怎么用_玩转光影!闪光灯、反光板怎么用才高级?

光线对于拍摄的重要性不言而喻&#xff0c;有人甚至说&#xff1a;掌握了光线&#xff0c;你就掌握了摄影。今天我们就来谈谈摄影中和“光”关系最密切的两个器材&#xff1a;闪光灯、反光板。闪光灯的种类之前也跟大家介绍过闪光灯的种类&#xff0c;如果只说较为常用的&#…

https证书pfx 生成 pem,crt,key

(1)将.pfx格式的证书转换为.pem文件格式:openssl pkcs12 -in xxx.pfx -nodes -out server.pem(2)从.pem文件中导出私钥server.key&#xff1a;openssl rsa -in server.pem -out server.key(3)从.pem文件中导出证书server.crt openssl x509 -in server.pem -out server.crt转载…