解析中文域名并配置 Nginx 服务器

部署中文域名站点,下面讲诉设置中文域名站点——“郝越.我爱你”。

设置域名解析

第一步是在设置域名 A 记录,使主机记录中的 @ 和 www 都指向一个 ip。

域名解析

上图为万网域名解析后台解析设置

设置 Nginx server_name

第二步是修改 nginx/conf/nginx.conf 或者其他 vhost 文件,在 server_name 这一行调整为经过 idn_to_ascii 函数编码的中文域名,而不是原始的中文域名。

server {
		listen       80;
		server_name  xn--vq3al9d.xn--6qq986b3xl;
		index index.php;
		root /alidata/www/love;
}

“郝越.我爱你”经过编码之后是“xn--vq3al9d.xn--6qq986b3xl”。

在百度搜索“中文域名编码”,进入在线编码的网站,提交中文域名,即可将中文域名转为 punycode 或者是 GBK 编码。在 nginx 配置文件中使用 punycode 即可。

设置完成之后 reload nginx,即可访问中文域名。

访问中文域名

在浏览器地址栏看到的虽然是中文域名,但实际的链接是 http://xn--vq3al9d.xn--6qq986b3xl 。通过 JavaScript document.domain 获取到的域名也是 “xn--vq3al9d.xn--6qq986b3xl”,两者其实是同一个,通过任何一个地址都能访问到服务器。

扩展:使用 PHP 进行中文域名编码

使用 PHP 进行中文域名编码

国际化域名。

比如中文域名,在浏览器地址栏以中文显示,但实际是以 punycode 进行解析和访问。服务器设置 server_name 时不能直接设置中文域名,而是设置中文域名对应的 punycode。

PHP 提供的 IDN 函数可以把非英文域名转换为 punycode,详见 PHP IDN Functions

idn_to_ascii 将域名转换为 IDNA ASCII 编码
idn_to_unicode idn_to_utf8 别名
idn_to_utf8 将域名 从 ASCII 编码转换为 Unicode 编码

代码示例

$xn_idn = idn_to_ascii('郝越.我爱你');
echo $xn_idn; // xn--vq3al9d.xn--6qq986b3xl

$idn = idn_to_utf8($xn_idn);
echo $idn; // 郝越.我爱你

使用 IDN 函数之前需要先安装 php5-intl 扩展:

sudo apt-get install php5-intl

PHP Socket 实现 TCP、UDP 报文的发送与接收

利用 PHP Socket 相关函数实现 TCP、UDP 端口监听。

需要注意,下面的示例代码中没有处理 Socket 错误。实际应用场景中每一步 Socket 的连接、写入、读取都需要进行错误判断和处理,相应的函数 socket_connect、socket_write、socket_read 以及 socket_bind、socket_listen 返回 false 时,需要调用 socket_last_error() 获取最新的 socket 错误号 $errno,并通过 socket_strerror($errno) 获取错误号对应的能够阅读的错误描述信息。

PHP Socket TCP 发送数据示例

$host = '127.0.0.1';
$port = '81';
$message = 'Hello TCP Server';

function send_tcp_message($host, $port, $message)
{
	$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
	@socket_connect($socket, $host, $port);

	$num = 0;
	$length = strlen($message);
	do
	{
		$buffer = substr($message, $num);
		$ret = @socket_write($socket, $buffer);
		$num += $ret;
	} while ($num < $length);

	$ret = '';
	do
	{
		$buffer = @socket_read($socket, 1024, PHP_BINARY_READ);
		$ret .= $buffer;
	} while (strlen($buffer) == 1024);

	socket_close($socket);

	return $ret;
}

$ret = send_tcp_message($host, $port, $message);

PHP Socket TCP 接收数据示例

创建一个 Server 接收 TCP 连接,需要先监听一个端口。

$host = '127.0.0.1';
$port = '81';
$callback = 'echo';

function receive_tcp_message($host, $port, $callback)
{
	$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

	// socket_bind() 的参数 $host 必传, 由于是监听本机, 此处可以固定写本机地址
	// 注意: 监听本机地址和内网地址效果不一样
	@socket_bind($socket, $host, $port);
	@set_time_limit(0);

	// 绑定端口之后调用监听函数, 实现端口监听
	@socket_listen($socket, 5);

	// 接下来只需要一直读取, 检查是否有来源连接即可, 如果有, 则会得到一个新的 socket 资源
	while ($child = @socket_accept($socket))
	{
		// 休息 1 ms, 也可以不用休息
		usleep(1000);

		if (false === socket_getpeername($child, $remote_host, $remote_port))
		{
			@socket_close($child);
			continue;
		}

		// 读取请求数据
		// 例如是 http 报文, 则解析 http 报文
		$request = '';
		do
		{
			$buffer = @socket_read($child, 1024, PHP_BINARY_READ);
			if (false === $buffer)
			{
				@socket_close($child);
				continue 2;
			}
			$request .= $buffer;
		} while (strlen($buffer) == 1024);

		// 此处省略如何调用 $callback
		$response = $callback($remote_host, $remote_port, $request);

		if (!strlen($response))
		{
			// 至少返回含有一个空格的字符串
			$response = ' ';
		}

		// 因为是 TCP 链接, 需要返回给客户端处理数据
		$num = 0;
		$length = strlen($response);
		do
		{
			$buffer = substr($response, $num);
			$ret = @socket_write($child, $buffer);
			$num += $ret;
		} while ($num < $length);

		// 关闭 socket 资源, 继续循环
		@socket_close($child);
	}
}

// 客户端来的任何请求都会打印到屏幕上
receive_tcp_message($host, $port, $callback);
// 如果程序没有出现异常,该进程会一直存在

有一个快捷的函数 socket_create_listen($port),创建、绑定、监听一步到位。

PHP Socket UDP 发送数据示例

$host = '127.0.0.1';
$port = '82';
$message = 'Hello UDP Server';

function send_udp_message($host, $port, $message)
{
	$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
	@socket_connect($socket, $host, $port);

	$num = 0;
	$length = strlen($message);
	do
	{
		$buffer = substr($message, $num);
		$ret = @socket_write($socket, $buffer);
		$num += $ret;
	} while ($num < $length);

	socket_close($socket);

	// UDP 是一种无链接的传输层协议, 不需要也无法获取返回消息
	return true;
}

send_udp_message($host, $port, $message);

PHP Socket UDP 接收数据示例

$host = '127.0.0.1';
$port = '82';
$callback = 'echo';

function receive_udp_message($host, $port, $callback)
{
	$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);

	@socket_bind($socket, $host, $port);
	@set_time_limit(0);

	while (true)
	{
		usleep(1000);

		$ret = @socket_recvfrom($socket, $request, 16384, 0, $remote_host, $remote_port);
		if ($ret)
		{
			$callback($remote_host, $remote_port, $request);
		}

		// 不需要返回给客户端任何消息, 继续循环
	}
}

// 客户端来的任何请求都会打印到屏幕上
receive_udp_message($host, $port, $callback);
// 如果程序没有出现异常,该进程会一直存在

PHP Socket 相关函数

  1. socket_accept — Accepts a connection on a socket
  2. socket_bind — Binds a name to a socket
  3. socket_clear_error — Clears the error on the socket or the last error code
  4. socket_close — Closes a socket resource
  5. socket_cmsg_space — Calculate message buffer size
  6. socket_connect — Initiates a connection on a socket
  7. socket_create_listen — Opens a socket on port to accept connections
  8. socket_create_pair — Creates a pair of indistinguishable sockets and stores them in an array
  9. socket_create — Create a socket (endpoint for communication)
  10. socket_get_option — Gets socket options for the socket
  11. socket_getpeername — Queries the remote side of the given socket which may either result in host/port or in a Unix filesystem path, dependent on its type
  12. socket_getsockname — Queries the local side of the given socket which may either result in host/port or in a Unix filesystem path, dependent on its type
  13. socket_import_stream — Import a stream
  14. socket_last_error — Returns the last error on the socket
  15. socket_listen — Listens for a connection on a socket
  16. socket_read — Reads a maximum of length bytes from a socket
  17. socket_recv — Receives data from a connected socket
  18. socket_recvfrom — Receives data from a socket whether or not it is connection-oriented
  19. socket_recvmsg — Read a message
  20. socket_select — Runs the select() system call on the given arrays of sockets with a specified timeout
  21. socket_send — Sends data to a connected socket
  22. socket_sendmsg — Send a message
  23. socket_sendto — Sends a message to a socket, whether it is connected or not
  24. socket_set_block — Sets blocking mode on a socket resource
  25. socket_set_nonblock — Sets nonblocking mode for file descriptor fd
  26. socket_set_option — Sets socket options for the socket
  27. socket_shutdown — Shuts down a socket for receiving, sending, or both
  28. socket_strerror — Return a string describing a socket error
  29. socket_write — Write to a socket

JavaScript Math

JavaScript Math 是 JavaScript 内置(built-in)的一个对象,包含常用的数学常量及数学方法。

JavaScript Math

Math 对象详细介绍 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math

Math 有8个常量,接近18个数学方法,并有多个新增方法。

Math 常量

Math.E 欧拉常数 2.718281828459045
Math.LN2 2的自然对数 0.6931471805599453
Math.LN10 10的自然对数 2.302585092994046
Math.LOG2E 以10为底E的对数 1.4426950408889634
Math.LOG10E 以2为底E的对数 0.4342944819032518
Math.PI 圆周率 3.141592653589793
Math.SQRT1_2 1/2的平方根 0.7071067811865476
Math.SQRT2 2的平方根 1.4142135623730951

Math 方法

Math.abs(x) 获取 x 的绝对值
Math.acos(x) 获取 x 的反余弦值
Math.acosh(x) 获取 x 的反双曲余弦值 实验方法
Math.asin(x) 获取 x 的反正弦值
Math.asinh(x) 获取 x 的反双曲正弦值 实验方法
Math.atan(x) 以介于 -PI/2 与 PI/2 弧度之间的数值来获取 x 的反正切值
Math.atanh(x) 获取 x 的反双曲正切值 实验方法
Math.atan2(x, y) 获取 y/x 的反正切值
Math.cbrt(x) 获取 x 的立方根 实验方法
Math.ceil(x) 获取大于等于 x 的最小整数
Math.cos(x) 获取 x 的余弦值
Math.cosh(x) 获取 x 的双曲余弦值 实验方法
Math.exp(x) 获取 Ex, Math.E 的 x 次幂
Math.expm1(x) 获取 Math.exp(x)-1 的值 实验方法
Math.floor(x) 获取小于等于 x 的最大整数
Math.fround(x) 获取与 x 最相近的单精度浮点数 实验方法
Math.hypot([x[,y[,…]]]) 获取所有参数的平方和的平方根, 统计 实验方法
Math.imul(x) 获取一个32位整数的乘积 ? 实验方法
Math.log(x) 获取以 Math.E 为底数, x 为指数的对数, 自然对数
Math.log1p(x) 获取 1 + x 的自然对数 实验方法
Math.log10(x) 获取以 10 为底数, x 为指数的对数 实验方法
Math.log2(x) 获取以 2 为底数, x 为指数的对数 实验方法
Math.max([x[,y[,…]]]) 获取所有参数中的最大值
Math.min([x[,y[,…]]]) 获取所有参数中的最小值
Math.pow(x,y) 获取 x 的 y 次幂
Math.random() 获取 0 到 1 之间的伪随机数
Math.round(x) 获取 x 四舍五入后的整数
Math.sign(x) 获取 x 的标记, 判定 x 是正数, 负数还是 0 -1, 0, 1
Math.sin(x) 获取 x 的正弦值
Math.sinh(x) 获取 x 的双曲正弦值 实验方法
Math.sqrt(x) 获取 x 的平方根
Math.tan(x) 获取 x 的正切值
Math.trunc(x) 获取 x 的整数部分,去除小数 实验方法

从 Math 常量可以看出 JavaScript 浮点数最多表示多少位。下面这段代码比较有趣:

1.1 + 2.2 // == 3.3 ?
// 得到结果 3.3000000000000003
Math.fround(1.1 + 2.2) == 3.299999952316284
// ? 得到最接近的浮点数

Linux安装SVN

使用 SVN 进行网站的发布工具。

在 Linux 上安装 SVN 步骤:

安装 SVN

# yum install subversion

创建 SVN 仓库

# cd /opt
# mkdir svn
# svnadmin create /opt/svn/web

通过上述命令在服务器 /opt/svn 目录下创建了一个仓库 web。接下来需要修改仓库的配置文件。

配置 SVN 仓库

# cd /opt/svn/web
# ls
conf db format hooks locks README.txt
# ls
authz passwd svnserve.conf

conf 目录下存放 SVN 仓库相关配置文件。

1、修改 authz 文件
authz 是 SVN 认证文件,vim 打开

[groups]
admin = svn_admin
[/]
@admin = rw

在 [groups] 下增加 admin,在文件末尾增加 [/],换行增加 @admin=rw。@ 紧跟着用户组,等号右侧是读写权限。

2、修改 passwd 文件

passwd 是 SVN 账号密码文件,vim 打开

[users]
svn_admin = svn_password

在 [users] 下增加 svn_admin,等号右侧是该账号的密码

3、修改 svnserve.conf 文件

svnserve.conf 是该 SVN 仓库运行配置,vim 打开

[general]
anon-access = read
auth-access = write
authz-db = /opt/svn/web/conf/authz
password-db = /opt/svn/web/conf/passwd

在 [general] 下增加访问控制和 authz-db、password-db 文件路径。

启动 SVN 仓库

# svnserve -d -r /opt/svn/web/

启动 SVN 仓库之后,SVN server 即创建完成。

检查仓库运行

# netstat -anp | grep svn
tcp 0 0 0.0.0.0:3690 0.0.0.0:* LISTEN 15019/svnserve

检查端口可以发现 SVN serve 已经运行在端口 3690。

通过 telnet 127.0.0.1 3690 连接该端口可以收到类似这样的信息 ( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries commit-revprops depth log-revprops partial-replay ) ) )。

通过 curl http://127.0.0.1:3690 可以收到类似这样的信息 ( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries commit-revprops depth log-revprops partial-replay ) ) ) 。

checkout 仓库

通过 SVN TortoiseSVN 客户端 checkout 仓库 svn://127.0.0.1 验证账户并登录就可以正常使用 SVN 了。进一步可以了解如何使用 https 访问 SVN 服务。

后续在 Linux 上部署 SVN 更新脚本即可实现 SVN 管理站点发布上线。

开机启动 SVN 服务

一般来说,Linux 服务器重启情况比较少。但为了方便,可以 SVN 服务添加到开机启动项中。

在文件 /etc/rc.local 中增加一行 /usr/bin/svnserve -d -r /opt/svn/web/

不使用 /etc/init.d/svnserver 启动 SVN 服务是因为该命令默认启动时没有携带 -r 参数,即默认没有指定仓库路径。