精华内容
下载资源
问答
  • 分组传输与报文传输时间计算

    千次阅读 2018-09-05 22:20:02
    如图所示网络。...1) 如果图中网络采用存储-转发方式报文交换,则A将2Mbits的文件交付给C需要多长时间?B将1Mbits的文件交付给D需要多长时间? 2) 如果图中网络采用存储-转发方式的分组交换,分...

    如图所示网络。A在t=0时刻开始向C发送一个2Mbits的文件;B在t=0.1+e秒(e为无限趋近于0的小正实数)向D发送一个1Mbits的文件。忽略传播延迟和结点处理延迟。

     

    请回答下列问题:

    1) 如果图中网络采用存储-转发方式的报文交换,则A将2Mbits的文件交付给C需要多长时间?B将1Mbits的文件交付给D需要多长时间?

    2) 如果图中网络采用存储-转发方式的分组交换,分组长度为等长的1kbits,且忽略分组头开销以及报文的拆装开销,则A将2Mbits的文件交付给C需要大约多长时间?B将1Mbits的文件交付给D需要大约多长时间?

    3) 报文交换与分组交换相比,哪种交换方式更公平?(即传输数据量小用时少,传输数据量大用时长)

     

    答案:

    1)A任务排在B任务的前面,因此路由器1先接收完A任务,先给路由器2传输,耗时:0.2+0.1+0.2 = 0.5s

    B任务耗时:0.1+0.1(等待时间)+0.05+0.1 = 0.35s,

    2)A任务分成2000个包,耗时0.0001*2+0.0001*2000 = 0.2002s

    B任务分成1000个包,耗时0.0001*2+0.0001*1000 = 0.1002s

    3)分组交换更公平

    展开全文
  • 前后端报文传输加密方案

    千次阅读 2019-05-22 09:58:36
    开发人员联系方式:251746034@qq.com 代码库:https://github.com/chenjia/vue-desktop 代码库:https://github.com/chenjia/vue-app 代码库:https://github.com/chenjia/lxt 示例:...

    开发人员联系方式:251746034@qq.com
    代码库:https://github.com/chenjia/vue-desktop
    代码库:https://github.com/chenjia/vue-app
    代码库:https://github.com/chenjia/lxt
    示例:http://47.100.119.102/vue-desktop
    示例:http://47.100.119.102/vue-app
    目的:前后端传输报文进行加密处理。

    一、开发环境
    前端技术:vue + axios
    后端技术:java
    加密算法:AES

    为什么选择采用AES加密算法?作者在各种加密算法都进行过尝试,发现AES有以下特点比较符合要求:
    1、加密解密执行速度快,相对DES更安全(原来采用的DES,结果部门的安全扫描建议用AES)
    2、对称加密
    3、被加密的明文长度可以很大,最多测试过10万长度的字符串。

    java端AES加密示例,参考 lxt/lxt-common/com/lxt/ms/common/utils/SecurityUtils.java

    public class SecurityUtils {
        public final static String letters = "abcdefghijklmnopqrstuvwxyz0123456789";
    
        public final static String key = "ed26d4cd99aa11e5b8a4c89cdc776729";
    
        private static String Algorithm = "AES";
    
        private static String AlgorithmProvider = "AES/ECB/PKCS5Padding";
    
        private final static String encoding = "UTF-8";
    
        public static String encrypt(String src) throws NoSuchAlgorithmException, NoSuchPaddingException,
                InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
            SecretKey secretKey = new SecretKeySpec(key.getBytes("utf-8"), Algorithm);
            //IvParameterSpec ivParameterSpec = getIv();
            Cipher cipher = Cipher.getInstance(AlgorithmProvider);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8")));
            return Base64Utils.encodeToString(cipherBytes);
        }
    
        public static String decrypt(String src) throws Exception {
            SecretKey secretKey = new SecretKeySpec(key.getBytes("utf-8"), Algorithm);
    
            //IvParameterSpec ivParameterSpec = getIv();
            Cipher cipher = Cipher.getInstance(AlgorithmProvider);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] hexBytes = Base64Utils.decodeFromString(src);
            byte[] plainBytes = cipher.doFinal(hexBytes);
            return new String(plainBytes, "utf-8");
        }
    
        public static String md5Encrypt(String str) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.update(str.getBytes());
                byte[] byteDigest = md.digest();
                int i;
                StringBuffer buf = new StringBuffer("");
                for (int offset = 0; offset < byteDigest.length; offset++) {
                    i = byteDigest[offset];
                    if (i < 0)
                        i += 256;
                    if (i < 16)
                        buf.append("0");
                    buf.append(Integer.toHexString(i));
                }
                //32位加密
                return buf.toString();
                // 16位的加密
                //return buf.toString().substring(8, 24);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public static String encryptKey(String key) throws Exception {
            String encryptedKey = "";
    
            String[] array = key.split("");
    
            Random random = new Random();
    
            for (int i = 0; i < array.length; i++) {
                encryptedKey += array[i];
                for (int j = 0; j < i % 2 + 1; j++) {
                    int index = random.nextInt(letters.length());
                    encryptedKey += letters.substring(index, index + 1);
                }
            }
            return Base64Utils.encodeToString(new StringBuilder(encryptedKey).reverse().toString().getBytes(encoding)).replaceAll("\n", "");
        }
    
        public static String decryptKey(String encryptedKey) {
            encryptedKey = new String(Base64Utils.decodeFromString(encryptedKey));
            String key = "";
    
            char[] c = new StringBuilder(encryptedKey).reverse().toString().toCharArray();
    
            for (int i = 0, j = 0; i < encryptedKey.length(); i++) {
                key += c[i];
                i += (j++ % 2 + 1);
            }
    
            return key;
        }
    

    前端AES加密,参考 vue-app/src/utils/security.js 或 vue-desktop/src/utils/security.js

    var CryptoJS = require("crypto-js");
    
    const encryptByAES = (message, key) => {
        var keyHex = CryptoJS.enc.Utf8.parse(key);
        var encrypted = CryptoJS.AES.encrypt(message, keyHex, {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        });
        return encrypted.ciphertext.toString(CryptoJS.enc.Base64).replace(/[\r\n]/g, '');
    }
    
    const decryptByAES = (ciphertext, key) => {
        var keyHex = CryptoJS.enc.Utf8.parse(key);
        var decrypted = CryptoJS.AES.decrypt({
            ciphertext: CryptoJS.enc.Base64.parse(ciphertext.replace(/[\r\n]/g, ''))
        }, keyHex, {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        });
        return decrypted.toString(CryptoJS.enc.Utf8);
    }
    
    const encryptKey = key => {
      let array = key.split('')
      let letters = 'abcdefghijklmnopqrstuvwxyz0123456789'
      let encryptedKey = ''
      for(let i=0;i<array.length;i++){
        encryptedKey += array[i]
        for(let j=0;j<i%2+1;j++){
          encryptedKey += letters.substr(parseInt(Math.random()*letters.length),1)
        }
      }
      return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encryptedKey.split('').reverse().join('')))
    }
    
    const decryptKey = encryptedKey => {
      encryptedKey = CryptoJS.enc.Base64.parse(encryptedKey).toString(CryptoJS.enc.Utf8).split('').reverse().join('')
      let str = ''
      for(let i=0,j=0;i<encryptedKey.length;i++){
        str += encryptedKey[i]
        i += (j++ % 2 + 1)
      }
      return str
    }
    
    export {encryptByAES,decryptByAES,encryptKey,decryptKey}
    

    好了,加密算法都有了,那怎么对报文进行加密呢?
    前端利用axios的拦截器就可以轻松实现。

    import axios from 'axios'
    import cache from './cache'
    import store from '../vuex/store'
    import {encryptByAES,decryptByAES,encryptKey,decryptKey} from './security'
    var CryptoJS = require("crypto-js");
    window.axios = axios
    
    let instance = axios.create({
      method: 'post',
      timeout: 60000,
      withCredentials: true,
      headers: {
        post: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      },
      transformRequest: [function(data) {
        let ret = ''
        for (let it in data) {
          ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
        }
        return ret
      }]
    })
    
    instance.interceptors.request.use(function(config) {
      let user = cache.get('user')
      let data = {
        head: {
          url: config.url,
          debug: true,
          userId: user ? user.userId : null,
          token: cache.get('token'),
          timestamp:new Date().getTime()
        },
        body: {
          data: config.data
        }
      }
      console.log('\n【request:'+config.url+'】', data, '\n\n')
      config.url = window.Config.server + config.url
    
      config.data = {
        request: encryptByAES(JSON.stringify(data), decryptKey(Config.key))
      }
      return config
    }, function(error) {
      console.log(error)
      return Promise.reject(error)
    })
    
    instance.interceptors.response.use(function(response) {
      let resp = decryptByAES(response.data.response, decryptKey(Config.key))
      response.data = JSON.parse(resp)
      console.log('\n【response:'+response.config.url+'】',response, '\n\n')
      if(response.data.head.status != 200){
        store.commit('TOGGLE_POPUP', {visible: true, text: response.data.head.msg, duration: 3000})
      }
      let token = response.data.head.token
      cache.set('token', token || cache.get('token'))
      return response
    }, function(error) {
      console.log(error)
      return Promise.reject(error)
    })
    
    export default instance
    

    注意上面 request 和 response 两个拦截器,在拦截 request 的时候,以下是对请求进行加密

    config.data = {
      request: encryptByAES(JSON.stringify(data), decryptKey(Config.key))
    }
    

    在拦截 response 的时候,以下是对响应的解密

    let resp = decryptByAES(response.data.response, decryptKey(Config.key))
    response.data = JSON.parse(resp)
    

    这样,前端只要是通过 instance 这个模版发出去的请求,就能自动在请求时加密,响应时解密了。注意,这里的decryptKey(Config.key)是对进行简单混淆后的密钥进行反处理,才能得到最初的AES密钥。

    前端部分好了,后台部分怎么做呢?其实思路都是类似的,后台是用的springcloud里面的zuul进行统一拦截的,当然你如果不是使用的微服务体系,后台通过最原始的过滤器也是可以的。

    public class RequestFilter extends ZuulFilter{
    	@Value("#{'${filterUrls.services}'.split(',')}")
    	private String[] services;
    
    	@Value("${filterUrls.apis}")
    	private String apis;
    
    	@Value("#{'${filterUrls.excludes}'.split(',')}")
    	private String[] excludes;
    
    	@Override
    	public Object run() throws ZuulException{
    		RequestContext ctx = RequestContext.getCurrentContext();
    		HttpServletRequest request = ctx.getRequest();
    		
    		System.out.println("【contextPath】"+request.getContextPath());
    		System.out.println("【requestURI】"+request.getRequestURI());
    		
    		String contextPath = request.getContextPath();
    		String uri = request.getRequestURI().replaceAll(contextPath, "");
    
    		String encryptedText = request.getParameter("request");
    		Packages pkg = new Packages();
    		String decryptedText = null;
    		try {
    			decryptedText = SecurityUtils.decrypt(encryptedText);
    			pkg = JSONUtils.json2Obj(decryptedText, Packages.class);
    		} catch (Exception e) {
    			e.printStackTrace();
    			pkg.getHead().setStatus(500);
    			pkg.getHead().setMsg("报文解密异常!");
    		}
    
    		if (pkg.getHead().getStatus() == 200 && apis.indexOf(uri) == -1) {
    			String token = pkg.getHead().getToken();
    			String userId = pkg.getHead().getUserId();
    
    			if (StringUtils.isNotEmpty(userId)) {
    				try {
    					Map<String, Object> map = JWTUtils.parse(token);
    					if(userId.equals(map.get("userId"))){
    						Set<Object> resourceSet = CacheUtils.sGet("RESOURCE_"+userId);
    						if(resourceSet == null || !resourceSet.contains(uri)){
    							System.out.println("forbidden:"+uri);
    						}
    //						if(resourceSet == null || !resourceSet.contains(uri)){
    //							pkg.getHead().setStatus(500);
    //							pkg.getHead().setMsg("未授权的访问,请联系管理员!");
    //						}
    					}else {
    						pkg.getHead().setStatus(500);
    						pkg.getHead().setMsg("token验证失败!");
    					}
    				} catch (Exception e) {
    					e.printStackTrace();
    					pkg.getHead().setStatus(500);
    					pkg.getHead().setMsg("token转换失败!");
    				}
    			}
    		}
    
    		InputStream in = (InputStream) ctx.get("requestEntity");
    		if (in == null) {
    			try {
    				in = ctx.getRequest().getInputStream();
    				String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
    				body = "request=" + JSONUtils.obj2Json(pkg);
    
    				final byte[] reqBodyBytes = body.getBytes();
    				ctx.setRequest(new HttpServletRequestWrapper(ctx.getRequest()) {
    					@Override
    					public ServletInputStream getInputStream() throws IOException {
    						return new ServletInputStreamWrapper(reqBodyBytes);
    					}
    
    					@Override
    					public int getContentLength() {
    						return reqBodyBytes.length;
    					}
    
    					@Override
    					public long getContentLengthLong() {
    						return reqBodyBytes.length;
    					}
    				});
    			} catch (IOException e) {
    				e.printStackTrace();
    				throw new ZuulException(e, 500, "获取输入流失败");
    			}
    		}
    
    		return null;
    	}
    
    	@Override
    	public boolean shouldFilter() {
    		boolean shouldFilter = false;
    		
    		HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
    		String uri = request.getRequestURI();
    		for(String url : services){
    			if(uri.startsWith(url)){
    				shouldFilter = true;
    				break;
    			}
    		}
    
    		for(String exclude : excludes){
    			if(uri.startsWith(exclude)){
    				shouldFilter = false;
    				break;
    			}
    		}
    		
    		return shouldFilter;
    	}
    
    	@Override
    	public int filterOrder() {
    		return FilterConstants.PRE_DECORATION_FILTER_ORDER;
    	}
    
    	@Override
    	public String filterType() {
    		return "pre";
    	}
    }
    
    public class ResponseFilter extends ZuulFilter {
    	@Value("#{'${filterUrls.services}'.split(',')}")
    	private String[] services;
    
    	@Value("#{'${filterUrls.origins}'.split(',')}")
    	private Set<String> origins;
    
    	@Value("#{'${filterUrls.excludes}'.split(',')}")
        private String[] excludes;
    
    	@Override
    	public Object run() throws ZuulException {
    		RequestContext ctx = RequestContext.getCurrentContext();
    		HttpServletRequest request = ctx.getRequest();
    		HttpServletResponse response = ctx.getResponse();
    
    		response.setCharacterEncoding("UTF-8");
    		response.setContentType("application/json;charset=utf-8");
    
    		String origin = request.getHeader("Origin");
    		if (origins.contains(origin)) {
    			response.setHeader("Access-Control-Allow-Origin", origin);
    			response.setHeader("Access-Control-Allow-Methods",
    					"POST,GET,OPTIONS");
    			response.setHeader("Access-Control-Allow-Headers",
    					"Origin,X-Requested-With,Content-Type,Accept,token");
    			response.setHeader("Access-Control-Allow-Credentials", "true");
    		}else {
    			System.out.println("【origin】"+origin);
    		}
    		
    		try {
                InputStream stream = ctx.getResponseDataStream();
                String body = StreamUtils.copyToString(stream, Charset.forName("UTF-8"));
                String encryptedText = SecurityUtils.encrypt(body);
                ctx.setResponseBody("{\"response\":\""+ encryptedText.replaceAll("\r\n|\n", "") +"\"}");
            } catch (Exception e) {
                throw new ZuulException(e, 500, "报文加密异常");
            }
    		return null;
    	}
    
    	@Override
    	public boolean shouldFilter() {
    		boolean shouldFilter = false;
    		
    		HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
    		String uri = request.getRequestURI();
    		for(String url : services){
    			if(uri.startsWith(url)){
    				shouldFilter = true;
    				break;
    			}
    		}
    
    		for(String exclude : excludes){
    			if(uri.startsWith(exclude)){
    				shouldFilter = false;
    				break;
    			}
    		}
    
    		return shouldFilter;
    	}
    
    	@Override
    	public int filterOrder() {
    		return FilterConstants.SEND_RESPONSE_FILTER_ORDER;
    	}
    
    	@Override
    	public String filterType() {
    		return "post";
    	}
    }
    
    @EnableZuulProxy
    @SpringBootApplication
    public class GatewayApplication {
    	public static void main(String[] args) {
    		SpringApplication.run(GatewayApplication.class, args);
    	}
    	
    	@Bean
        public RequestFilter requestFilter() {
            return new RequestFilter();
        }
    
    	@Bean
        public ResponseFilter responseFilter() {
            return new ResponseFilter();
        }
    }
    

    记得在启动类里面注册这两个过滤器(拦截器)。
    作者的实现里面在拦截器里面加了大量的逻辑,可以根据自己的需要酌情删减。
    比如:控制权限、控制需要拦截的接口前缀、控制拦截的例外。
    再加上一个统一的熔断,可以更加友好的提醒前端。

    @Component
    public class FallbackConfig implements FallbackProvider {
    	Logger logger = LoggerFactory.getLogger(FallbackConfig.class);
    
    	@Override
    	public String getRoute() {
    		return "*";
    	}
    
    	@Override
    	public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
    //		if (cause != null && cause.getCause() != null) {
    //			System.out.println(cause.getMessage());
    //			String reason = cause.getCause().getMessage();
    //			System.out.println("\n[fallback]"+reason+"\n");
    //		}
    
    		if(cause != null){
    			System.out.println("【fallback msg】"+cause.getMessage());
    		}
    
    		if (cause.getCause() != null) {
    			System.out.println("【fallback cause】"+cause.getCause().getMessage());
    		}
    
    		return new ClientHttpResponse() {
    			
    			@Override
    			public HttpHeaders getHeaders() {
    				HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_JSON);
                    return headers;
    			}
    			
    			@Override
    			public InputStream getBody() throws IOException {
    				Packages pkg = new Packages();
    				pkg.getHead().setStatus(500);
    				pkg.getHead().setMsg("服务器正在开小差");
    				return new ByteArrayInputStream(JSONUtils.obj2Json(pkg).replace("\r\n", "").replace("\n", "").getBytes());
    			}
    			
    			@Override
    			public String getStatusText() throws IOException {
    				return "OK";
    			}
    			
    			@Override
    			public HttpStatus getStatusCode() throws IOException {
    				return HttpStatus.OK;
    			}
    			
    			@Override
    			public int getRawStatusCode() throws IOException {
    				return 200;
    			}
    			
    			@Override
    			public void close() {
    				
    			}
    		};
    	}
    
    }
    

    当前端某个接口调用异常的时候,后台统一返回提醒内容:服务器正在开小差,这样即使你的后台挂了,或者是在重启中(springcloud微服务重启单个服务很正常),前端都不会受影响。

    最后提醒一句,任何前端加密都不能做到绝对的安全,毕竟代码都是暴露在浏览器的,特别是你的加密解密密钥,建议密钥也不要直明文暴露出来,而是对密钥进行简单的混淆处理后使用,再加上现在前后端都是分离的,前端一般都是es6或typescript使用webpack打包进行ugly处理,这样安全性也能提高不少。

    好了,最后附上效果图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 使用ebpf分析网络报文传输时延

    千次阅读 2019-08-31 23:13:06
    获取延迟的方式是在发送端在发送报文的尾部添加一个发送时间戳,在接收端获取报文后将接收端时间戳与发送时间戳进行对比,从而获得中间的延迟时间。由于发送端和接收端位于同一设备(使用不同的网卡),又分别处于...

    最近需要分析某个链路上的单向网络延迟,数据的发送端使用一个用户态协议栈,而接收端则使用linux内核协议栈。获取延迟的方式是在发送端在发送报文的尾部添加一个发送时间戳,在接收端获取报文后将接收端时间戳与发送时间戳进行对比,从而获得中间的延迟时间。由于发送端和接收端位于同一设备(使用不同的网卡),又分别处于用户态和内核态,因此使用了两端都能高性能访问的tsc时钟作为时间戳来源。

    为了在接收端尽可能早的处理报文,减少软件协议栈的执行时间影响,需要在内核处理报文时就进行报文时间戳的解析和延迟计算。要做到这一点有几种方法:

    1. 修改驱动或协议栈代码,直接在其中加入处理逻辑;
    2. 使用netfilter框架,在hook点加入自己的处理函数;
    3. 使用kprobe,插入自己的处理函数;
    4. 使用ebpf,插入自己的处理函数。

    上面几种方式中,其他几种方式都需要修改或开发内核代码,在线上服务器上操作风险较大。而ebpf可以将用户态程序直接插入内核中运行,内核会对这些代码进行严格检查,便捷性和安全性上都更好一些。

    ebpf的使用过程和原理比其他几种方式略复杂一些,简单介绍如下:

    1. 编写ebpf处理逻辑。这个处理逻辑一般是用C(restricted C)写的一个函数,函数参数与函数的插入位置有关。
    2. 使用clang+llvm编译成BPF指令。插入内核的函数并不是能直接运行的x86指令,而是一种中间码——BPF指令。
    3. 使用bpf系统调用将处理函数的中间码插入到内核指定位置。这里的位置有很多种类型:kprobe、xdp、socket_filter等等,但大体上可以分为两类:一类是xdp这种在特定位置专门提供了hook接口的,另一类是kprobe这种通过特定机制附加到内核函数指定位置的。使用时不同类型的位置能使用的函数接口也会有所不同。
    4. 内核在执行到插入函数的位置时,通过JIT虚拟机执行BPF指令。
    5. 内核态插入的BPF代码可以和用户态代码通过专门的数据结构BPF_MAP进行通信。用户态代码可以将新的工作和配置传递给内核代码,内核代码也可以将处理的结果和状态传递给用户态做进一步处理和展示。

    ebpf最大的优点在于能够通过BPF_MAP在用户态程序和插入内核的bpf函数间进行异步通信,这使得ebpf程序能够完成许多使用内核模块很难完成的功能。

    具体开发ebpf程序时,由于自己编译和插入ebpf代码较为复杂,可以通过bcc工具来简化开发工作。bcc是一个python框架,可以在其中编写ebpf函数后通过python封装的接口完成编译、插入和MAP交互等操作。

    更详细的ebpf/bcc介绍可以参考如下文献:

    [1] https://blog.cyru1s.com/posts/ebpf-bcc.html,最好的(可能也是唯一的)中文ebpf/bcc教程,其中详细介绍了使用bcc开发ebpf程序的方法,并且介绍了不少基础但却关键的(也是别的文献很少提及的)注意点。非常实用。
    [2] https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf/,ebpf文献大全,从ebpf的历史发展到未来的发展方向,都能在其中找到。
    [3] https://www.linuxplumbersconf.org/event/2/contributions/71/attachments/17/9/presentation-lpc2018-xdp-tutorial.pdf,面向XDP开发的介绍,其中也介绍了ebpf的原理和xdp的原理与开发方法。

    解析报文、获取时间戳并比较时间差值的逻辑非常简单,但让这不到100行代码正确运行却没有想象中能够简单。ebpf代码虽然可以使用C语言编写,并运行于内核态,但是开发时与开发内核代码有非常大的差异。笔者遇到的问题和建议总结如下:

    1. ebpf代码中不能调用内核导出函数接口,只能调用ebpf子系统提供的指定bpf_helper系列函数。因此在ebpf代码中无法通过rdtsc()获取TSC时钟,也就无法和报文中携带的tsc时间戳直接比较。只能通过bpf_ktime_get_ns()接口获取内核ktime,再通过tsc时钟频率换算成tsc时间。
    同样的,在ebpf代码中很难去访问skb数据结构的非线性数据段。笔者尝试通过kprobe方式将ebpf函数插入协议栈入口位置,但是通过skb无法访问128字节之后的非线性存储的数据,因为页地址转换等函数都是无法使用的。之后只能使用socket_filter或xdp模式。在这些专门处理网络数据的模式中,系统提供了将数据线性化的功能,从而支持对整个数据包内容的访问。例如在socket_filter模式中可以通过load_byte()接口来读取指定偏移位置的报文内容。
    2. 内核加载ebpf代码时会进行严格的校验,对于不符合要求的代码会拒绝载入。最常遇到的例子是通过指针访问数据前没有检查指针合法性。在访问BPF_MAP中的数据元素时,即使我们确定查询的元素一定存在,也必须加入检查指针是否为空的代码,否则无法载入。
    3. ebpf代码使用的编译器和编译生成的目标都和普通的x86代码大不相同。因此同样的C语法,在ebpf中的表现可能就和在普通的x86代码中不一样。例如:skb->head[skb->transport_header+1]和*(skb->head+skb->transport_header+1)在普通的内核代码中是等价的,但在ebpf中只有后一种写法是有效的。在ebpf的C代码中写一些比较复杂的代码往往会产生和预期不同的结果。因此编写ebpf的C代码时语法要尽可能简单,不要在一句语句中写太多的类型转换和指针引用之类的操作,复杂逻辑尽量拆成多行简单代码实现。

    展开全文
  • tcp头分析 偏移:头部长度(包含选项和填充) 保留区:6个0 URG-紧急指针有效 ACK-确认序号有效 ...PSH-接收方应尽快将这个报文交给应用层 ...紧急指针:是一个正偏移...TCP的紧急方式是发送端向另一端发送紧急数据...

    tcp头分析

    在这里插入图片描述

    偏移:头部长度(包含选项和填充
    保留区:6个0
    URG-紧急指针有效
    ACK-确认序号有效
    PSH-接收方应尽快将这个报文交给应用层
    RST-连接重置
    SYN-同步序号用来发起一个连接
    FIN-终止一个连接
    窗口字段:窗口的字节容量
    校验和:看数据有没有被改
    紧急指针:是一个正偏移量,与序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式
    选项与填充(必须为4字节整数倍,不够补0):
    最常见的可选字段的最长报文大小MSS(Maximum Segment Size),每个连接方通常都在一个报文段中指明这个选项。它指明本端所能接收的最大长度的报文段。该选项如果不设置,默认为536(20+20+536=576字节的IP数据报)

    MMS确认

    最大消息长度在第三次握手时确定

    窗口控制

    收到一个再发一个太慢了,
    一次发多个,用窗口报存

    高速重发控制

    一次没收到再等一等,说不定下一秒就到了,连续三次催了再重发

    流控制

    没处理的多了就把窗口调小,另一端就会发少一点。

    拥塞控制

    选min(接受端允许窗口大小,拥塞窗口大小)作为发包多少的标准。
    拥塞窗口:建立连接初始为0,每收到ACK应答就快速变大。
    一开始不允许发太多,防止一开始就卡死;
    重复确认应答:窗口减半;(高速重发控制,遇到连续3次催)
    超时:窗口设0;

    nagle算法(内容及优缺点)

    发的都有ack回 || 当前要发的积累到MSS了 才发
    减少发包数,可能会产生延迟;

    延迟确认应答机制

    等窗口里的包处理一些了再回(应答包的窗口大小会大一点)

    捎带应答机制

    延迟确认应答机制的基础上,把确认应答塞到要回的数据包里;
    减少发包数量。
    缺:长时间没准备好数据包,会连应答也没发回去。

    展开全文
  • 报文

    2019-09-27 22:47:12
    报文也是网络传输的单位,传输过程中会不断的封装称分组、包、桢来传输,封装的方式就是添加一些信息段,那些就是报文头以一定格式组织起来的数据。 报文方式:不断的封装称分组、包、桢来传输 认证方式:有...
  • IPsec 对数据进行加密的方式有两种:传输模式和隧道模式。 传输模式只是对 IP 协议报文的有效数据载荷 (payload) 进行了加 密,因此需要对原始 IP 报文进行拆装。 隧道模式则是对整个 IP 报文进行加密,就好像...
  • 前面有一篇文章是关于使用zlib库函数解压以gzip压缩方式传输的http报文。里面提到了chunked分块传输格式,现在由于项目需要,做了这部分的研究,现在把成果记录下来。 首先介绍一下chunked分块传输格式。对于一般的...
  • spring-boot-starter-transfer-encrypt 将之前项目中使用的报文加密方式...使用aes对称加密方式对于传输报文data进行整体加解密 使用rsa对aes 的加密key进行加密 ras公私钥动态获取:在客户端每次启动重新获取公私钥
  • 125.数据传输方式

    2020-05-06 18:46:33
    若按数据传输的同步方式可分为同步传输和异步传输; 若按数据传输的流向和时间关系可以分为单工、半双工和全双工数据传输。 数据传输的三种交换方式: 1.电路交换 优点:①时延小实时性强,②既可以传输模拟信号也...
  • http协议数据传输方式

    千次阅读 2017-10-16 15:27:31
    互联网的协议统称为TCP/IP协议。TCP/IP协议分为四层:应用层、传输层、网络层...http常用的几种数据传输方式: 1)GET:获取资源 2)POST:传输实体主体 3)PUT:传输文件 4)HEAD:获取报文手部 5)DELETE:删除文
  • 报文传输方式为2时,报文通信内容的首字母固定为“A4”,按先后顺序每4bit截取一次,转换成16进制数,每个16进制数以ASCII的形式表示。如数据长度不是4bit的整数倍,高位捕0,凑成整数倍。
  • 报文交换

    2021-01-08 17:57:55
    报文交换与分组交换均采用存储-转发交换方式 区别: 报文交换以完整报文进行"存储-转发" 分组交换以较小的分组进行"存储-转发" 分组交换:传输延迟 接收应用报文(消息) 拆分为较小长度为Lbits的分组 在传输速率为R的...
  • 关于报文

    2019-09-28 06:49:40
    报文也是网络传输的单位,传输过程中会不断的封装成分组、包、帧来传输,封装的方式就是添加一些信息段,那些就是报文头以一定格式组织起来的数据。 比如里面有报文类型,报文版本,报文长度,报文实体等等信息。...
  • 报文格式

    2014-07-23 15:40:36
    报文也是网络传输的单位,传输过程中会不断的封装成分组、包、帧来传输,封装的方式就是添加一些信息段,那些就是报文头以一定格式组织起来的数据。 比如里面有报文类型,报文版本,报文长度,报文实体等等信息。...
  • 数据的传输方式分类

    2021-01-19 10:46:05
    为了从源目的地向端系统发送报文,需要把长报文切分为一个个小的数据块,这种数据块称为分组(Packets),也就是说,报文是由一个个小块的分组组成。在端系统和目的地之间,每个分组都要经过通信链路(communication ...
  • /** * <p>描述: 使用HttpPost传输 有用户名密码验证的xml格式的WebService调用</p> * * @param url webservice的URL * @param xmlStr xml格式... * @return 返回传输响应的结果字符串 */ public s...
  • TCP适合传输自定义原始的字节流,比如传输一个序列化为字节流后的对象或者结构体,发送方按约定的自定义报文结构发送,接收方按约定的自定义报文解码。一种传输字流节,适合传输结构体和对象,一种传输出字符串。...
  • 报文:  简单来说,报文就是也就是HTTP报文,作用是在各个系统之间进行和... 另外,报文也是网络传输的单位,传输过程中会不断的封装成分组、包、帧来传输,封装的方式就是添加一些信息段,那就是报文头以一定格...
  • IPSec协议定义 IPsec将IP数据包的内容在装包过程在...IPsec 对数据进行加密的方式 加密模式 加密特点 传输模式 只是对 IP 协议的数据部分 (payload) 进行了加密 隧道模式 对整个 IP 分组进行加密 ESP...
  • 过滤报文的几种方式

    千次阅读 2014-06-16 14:33:39
    可以把windows操作系统的网络构架粗略划分为6层,其中逻辑链路层、网络层和传输层都是通过传输驱动程序实现的,也叫做协议驱动程序。网络驱动接口标准(NDIS)用于windows环境下网络驱动程序的开发,NDIS提供了很多...
  • ** HTTP 请求报文结构分析 ** 第一行 为 请求行 里面包含 请求方式 路由地址 版本...get 是明文传输 不安全 post 相对比较安全 ** HTTP 请求方式 ** HTTP/1.1 协议中定义了8种 方法 其中我们常用的五种 POST , GET

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,932
精华内容 1,172
关键字:

报文传输方式