局域网内服务器证书申请
为了可以使用https协议访问局域网内的服务器,收集并整理了网上有关局域网证书签发的资料。
生成CA
创建CA使用的密钥
openssl genrsa -out ca.key
生成根CA证书
openssl req -new -x509 -key ca.key -out ca.crt -days 3650
将CA证书放入受信任的根证书颁发机构(windows系统)
签发服务器使用的证书
生成证书使用的密钥
openssl genrsa -out web.key
生成证书请求文件CSR
由于我们的服务器是通过ip地址或域名都可访问的,所以在申请证书时,需要修改默认的配置文件以添加额外信息。可以将默认的openssl.cnf文件复制一份后进行修改,该文件所在位置在openssl的OPENSSLDIR目录中,可以通过openssl version -a命令获取。
# 文件名为 openssl.cnf[ req ]default_bits = 2048distinguished_name = req_distinguished_namereq_extensions ...
Kotlin开发Android的小技巧
本文主要用于记录自己开发过程中觉得好用的编码技巧,主要来自于第三版的《第一行代码Android》以及博客。
启动Activity
常见的启动Activity的方法是
val intent = Intent(this, MyActivity::class.java)intent.putExtra("param1", "data1")intent.putExtra("param2", "data2")startActivity(intent)
使用这种方法每次都要重复编码,且容易忘记需要传递的参数的信息,可以通过在Activity中定义静态方法来简化,如下所示:
class MyActivity : AppCompatActivity(){ ... companion object { fun actionStart(context: Context, data1:String, data2:String) { val intent ...
UNIX环境高级编程(19-伪终端)
概述
伪终端是指,对于一个应用程序,它看上去是一个终端,而实际上却并不是一个真正的终端。
父进程首先打开一个伪终端主设备,随后fork,子进程打开相应的伪终端从设备,并将该文件描述符复制到stdin/out/err,最后调用exec。
对于伪终端从设备上的用户进程来说,其stdin/out/err都是终端设备,因此可以处理上一章介绍的各类终端I/O函数。并且,所有写到伪终端主设备的都会作为从设备的输入,反之亦然。
其典型结构如下图所示:
为说明方面,下面将伪终端简称为PTY。
典型用途
网络登录服务器
如telnetd和rlogind服务器。这方面不太熟悉,故不展开讲。
窗口系统终端模拟
窗口系统通常提供一个终端模拟器,这使得我们可以在命令行环境下通过shell运行程序。
终端模拟器是shell和窗口管理器之间的媒介。shell将自己的标准输入/输出/错误连接到PTY的从设备端,而终端模拟器则管理对应的主设备端。其大致框图如下:
当用户改变模拟器的窗口大小时,窗口管理器会通知模拟器,而模拟器则在主设备端利用TIOCSWINSZ命令设置从设备的窗口大小。前台PTY从设备的进程组会收 ...
UNIX环境高级编程(18-终端I/O)
本章主要介绍终端的相关概念,及一些修改终端操作的函数。
概念
工作模式
主要有以下两种工作模式:
规范模式(Canonical mode)输入处理。在此模式下,对于终端的输入以行为单位进行处理。每次读取最多返回一行。这是默认的模式。
非规范模式(Noncanonical mode)输入处理。输入字符不装配成行,一些特殊字符(如Ctrl+D)以不会进行处理。
这两种模式在后面会有详细解释。
终端特性
在结构termios中定义了终端设备全部特性的标志,在其中又将各种标志进行分类,其结构大体如下:
struct termios { tcflag_t c_iflag; /* input flags */ tcflag_t c_oflag; /* output flags */ tcflag_t c_cflag; /* control flags */ tcflag_t c_lflag; /* local flags */ cc_t c_cc[NCCS]; /* control characters */& ...
UNIX环境高级编程(15-进程间通信)
本章主要介绍一些进程间通信的方式,如管道、消息队列、信号量和共享存储等。
管道
一般来说,管道是半双工的(即数据只能在一个方向上流动),并且只能在具有公共祖先的两个进程之间使用。通常,父进程创建管道后会接着调用fork,从而利用管道在父子进程之间通信。
之后,父子进程可以分别关闭管道的读/写端,以利用管道在父子进程中传递信息。例如,如果想要创建从父进程到子进程的管道,则可以关闭父进程的读端和子进程的写端。
由于管道半双工的特性,想要在父子进程间双向传递信息需要建立2个管道。
#include <unistd.h>// Returns: 0 if OK, −1 on errorint pipe(int fd[2]);
利用pipe函数可以创建管道,fd参数返回两个文件描述符,fd[0]为读而打开,fd[1]为写而打开。fd[1]的输出是fd[0]的输入。
在上面的例子中,父进程关闭fd[0],子进程关闭fd[1],那么最后的示意图如下:
注意:
当读一个写端被关闭的管道,在所有数据被读取后,read返回0
当写一个读端被关闭的管道,会产生SIGPIPE信号。如果忽略该 ...
UNIX环境高级编程(14-高级I/O)
本章主要介绍几种高级I/O功能,主要有非阻塞I/O、记录锁、I/O多路转接、异步I/O、readv/writev函数和存储映射I/O。
非阻塞I/O
某些系统调用可能会使进程永远阻塞,一般称其为低速系统调用。而使用非阻塞I/O,可以使open、read和write这类I/O操作不会阻塞,如果不能完成这些操作时,会立即出错返回。
有两种方法将其指定为非阻塞I/O:
调用open时指定O_NONBLOCK标志。
通过fcntl函数打开O_NONBLOCK文件状态标志。
#include <fcntl.h>// Returns: depends on cmd if OK (see following), −1 on errorint fcntl(int fd, int cmd, ... /* int arg */ );
记录锁
记录锁的主要功能是阻止多个进程同时修改文件的某一文件区。记录锁可以对整个文件加锁,也可以只针对文件的一部分进行加锁。
锁的类型
主要有共享读锁和独占性写锁这两种。
加读/写锁时,文件描述符必须是读/写打开。
任意多个进程在给定的字节上可以有一 ...
UNIX环境高级编程(13-守护进程)
主要特点
一般在系统启动时装入,仅在系统关闭时终止。
大多数守护进程以超级用户特权运行。
所有的守护进程都没有控制终端,其终端名设置为问号。
内核守护进程以无控制终端方式启动。
用户层守护进程可以通过调用setsid实现。
用户层守护进程的父进程是init进程。
消息输出
前面提到,守护进程是没有控制终端的,显然无法将自己的消息输出到标准输出或标准错误上。而且系统中运行着许多守护进程,因此需要一个集中的守护进程记录设施,即syslog。
如上图所示,主要有3中产生日志消息的方式:
内核例程调用log函数
大多数用户进程调用syslog函数
将日志消息发送到UDP的514端口
而syslogd守护进程接收这些日志消息,在其启动前会读取配置文件(/etc/syslog.conf),以决定各类消息的处理方式。
#include <syslog.h>void openlog(const char *ident, int option, int facility);void syslog(int priority, const char *format, ...); ...
UNIX环境高级编程(12-线程控制)
本章详细介绍了线程属性和同步原语属性。最后讨论基于进程的系统调用如何与线程进行交互。
属性
可以通过对每个对象关联的不同属性来细调线程和同步对象的行为。管理这些属性的函数大概有以下几类:
初始化函数,负责给属性设置为默认值
销毁函数,负责释放初始化函数分配的资源
获取属性值的函数
设置属性值的函数
线程属性
初始化和销毁
// Both return: 0 if OK, error number on failureint pthread_attr_init(pthread_attr_t *attr);int pthread_attr_destroy(pthread_attr_t *attr);
destroy函数除了释放资源外,还会用无效的值初始化属性对象,这样当线程创建函数误用该对象时,会返回错误信息。
分离状态属性detachstate
// Both return: 0 if OK, error number on failureint pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, ...
UNIX环境高级编程(11-线程)
本章主要介绍线程的概念,创建和终止线程以及线程同步问题。
使用到的函数默认需要包含pthread.h头文件,且在使用gcc编译时,需要链接pthread库。
代码地址:https://gitee.com/maxiaowei/Linux/tree/master/apue
线程的创建与终止
创建线程
// Returns: 0 if OK, error number on failureint pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
新创建的线程的线程ID被设置成tidp指向的内存单元;attr参数定制线程的不同属性;start_rtn函数是线程开始时执行的函数,其参数可以通过arg进行传递。
注意:
新线程最好不要通过tidp指向的内存空间获取自己的线程ID,因为如果新线程在主线程调用pthread_cre ...
C专家编程(4)
数组与指针
在大部分情况下,数组和指针是可以互换的。书中的第4章主要讲解了数组和指针的不同之处,而在第9章则着重讨论两者可以互换的场景。
不同之处
对于两者的不同之处,主要在于访问两者时有一些不同。
编译器会为每个变量分配一个地址,每个符号的地址在编译时可知(如数组的名字)。那么在访问数组中的数据时,可以通过符号表直接得出数组的起始地址,加上偏移量即可访问对应的数据。
而对于指针,必须先在运行时取得它当前存储的值,再对它解引用来获取(数组的)起始值,最后才能加上偏移量来访问数据。
其他区别
指针
数组
保存数据的地址
保存数据
间接访问数据,首先取得指针的内容,把它作为地址,然后从这个地址提取数据。如果指针有一个下标[i],就把指针的内容加上i作为地址,从中提取数据
直接访问数据,a[i]只是简单的a+i为地址取得数据
通常用于动态数据结构
通常用于存储固定数目且数据类型相同的元素
相关的函数为malloc(),free()
隐式分配和删除
通常指向匿名数据
自身即为数据名
相同之处
在书中,主要从“声明”和“使用”两种情况下进行讨论,直接看 ...