<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop &#187; socket</title>
	<atom:link href="https://zohead.com/archives/tag/socket/feed/" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>基于Chrome Socket的XMLHttpRequest</title>
		<link>https://zohead.com/archives/chrome-xhr/</link>
		<comments>https://zohead.com/archives/chrome-xhr/#comments</comments>
		<pubDate>Sat, 18 Jul 2015 16:49:35 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Chrome]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[chrome.sockets.tcp]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[XMLHttpRequest]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=999</guid>
		<description><![CDATA[之前在开发 Chrome OS 系统下的快盘文件系统时发现使用 Chrome 自带的 XMLHttpRequest 存在一些限制： 使用 jQuery 实现的 XHR 碰到 HTTP 302 redirect 等特殊的请求时浏览器会自动处理（例如：自动重定向），在 Chrome App 模式下会被限制，如果需要得到重定向的地址就不好实现，此时可以考虑用 Chrome Socket 来实现完整的 HTTP 协议请求。 有关 Chrome Socket 即 chrome.sockets.tcp 的说明可以参考这里： https://developer.chrome.com/apps/sockets [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>之前在开发 Chrome OS 系统下的快盘文件系统时发现使用 Chrome 自带的 XMLHttpRequest 存在一些限制：</p>
<p>使用 jQuery 实现的 XHR 碰到 HTTP 302 redirect 等特殊的请求时浏览器会自动处理（例如：自动重定向），在 Chrome App 模式下会被限制，如果需要得到重定向的地址就不好实现，此时可以考虑用 Chrome Socket 来实现完整的 HTTP 协议请求。</p>
<p>有关 Chrome Socket 即 chrome.sockets.tcp 的说明可以参考这里：</p>
<p><a href="https://developer.chrome.com/apps/sockets_tcp" target="_blank">https://developer.chrome.com/apps/sockets_tcp</a></p>
<p>后来发现网上已经有人实现了一个还算完整的基于 Chrome Socket 的 XMLHttpRequest：</p>
<p><a href="https://github.com/ahmadnassri/chrome.sockets.tcp.xhr" target="_blank">https://github.com/ahmadnassri/chrome.sockets.tcp.xhr</a></p>
<p>经过实际使用之后发现这个 XHR 类确实实现了基本的 HTTP 协议，但实际使用时还是有点问题，因此我在此项目基础上做了一些改进：</p>
<ul>
<li>原 chrome.sockets.tcp.xhr 不支持获取完整的 HTTP 响应内容，只能得到第一次接收到的数据，这样对于需要返回大量数据的下载等操作是不能接受的；</li>
<li>增加重定向判断，支持直接返回 HTTP 302 重定向的目标地址；</li>
<li>支持设置第一次接收到数据的超时；</li>
<li>支持所有接收的数据通过 ArrayBuffer 返回。</li>
</ul>
<p>修改过的基于 Chrome Socket 的 XMLHttpRequest 代码我放在这里了：</p>
<p><a href="https://github.com/zohead/chrome.sockets.tcp.xhr" target="_blank">https://github.com/zohead/chrome.sockets.tcp.xhr</a></p>
<p>chrome.sockets.tcp.xhr 的使用文档可以参考这里：</p>
<p><a href="http://chromesocketstcpxhr.readthedocs.org/en/latest/" target="_blank">http://chromesocketstcpxhr.readthedocs.org/en/latest/</a></p>
<p>修改之后的使用方法与这个文档里的基本一致，增加的部分例如可以通过 recvTimeout 属性指定第一次接受到数据的超时：</p>
<pre class="brush: jscript; title: ; notranslate">
var xhr = new chrome.sockets.tcp.xhr();
xhr.recvTimeout = 3500;
xhr.open('GET', 'http://google.com:80');
xhr.setRequestHeader('X-Requested-With', 'chrome.sockets.tcp.xhr');
xhr.send(null);
</pre>
<p>上面的代码就表示连接成功之后如果 3.5 秒之内没有接收到数据就认为超时断开连接。</p>
<p>由于此 chrome.sockets.tcp.xhr 项目功能实现的还不是特别完整，如果此项目使用中发现有什么问题欢迎提出并 fork 修改哦～～～</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/chrome-xhr/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>使用libc封装库修改程序绑定端口</title>
		<link>https://zohead.com/archives/libc-bind-wrapper/</link>
		<comments>https://zohead.com/archives/libc-bind-wrapper/#comments</comments>
		<pubDate>Mon, 11 Aug 2014 14:15:01 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[网络技术]]></category>
		<category><![CDATA[bind.so]]></category>
		<category><![CDATA[libc]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[wrapper]]></category>
		<category><![CDATA[端口]]></category>
		<category><![CDATA[绑定]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[网络]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=764</guid>
		<description><![CDATA[本文同步自（最佳显示效果请点击）：https://zohead.com/archives/libc-bind-wrapper/ 最近在使用一个第三方程序的时候发现程序绑定的 UDP 端口和现有 Linux 系统中的程序有冲突，系统自带的程序又不好修改端口，而第三方程序更没有源码或者配置文件来指定端口。 这种情况下想到可以用 libc 的封装库自己实现 bind 之类的函数来修改端口号，而网上也找到了 Daniel Ryde 类似的实现： http://www.ryde.net/code/bind.c.txt 编译这个 bind 库可以通过环境变量指定绑定的本地 IP 地址，但不支持端口号修改， [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（最佳显示效果请点击）：<a href="https://zohead.com/archives/libc-bind-wrapper/" target="_blank">https://zohead.com/archives/libc-bind-wrapper/</a></p>
<p>最近在使用一个第三方程序的时候发现程序绑定的 UDP 端口和现有 Linux 系统中的程序有冲突，系统自带的程序又不好修改端口，而第三方程序更没有源码或者配置文件来指定端口。</p>
<p>这种情况下想到可以用 libc 的封装库自己实现 bind 之类的函数来修改端口号，而网上也找到了 Daniel Ryde 类似的实现：</p>
<p><a href="http://www.ryde.net/code/bind.c.txt" target="_blank">http://www.ryde.net/code/bind.c.txt</a></p>
<p>编译这个 bind 库可以通过环境变量指定绑定的本地 IP 地址，但不支持端口号修改，而且是直接修改程序所有绑定主机地址不好过滤定制。因此，我对这个 bind 的封装库做了以下改进：</p>
<ul>
<li>支持 socket 类型过滤，可以指定是 TCP、UDP socket 等；</li>
<li>支持指定是本地监听请求还是连接远程的请求；</li>
<li>支持修改本地使用的 IP 地址及端口号；</li>
<li>支持通过环境变量过滤掉某些端口</li>
</ul>
<p>支持上面这些新增的功能之后，我们就可以根据实际情况只修改特定请求的绑定地址或者绑定端口以满足需求。</p>
<p>我修改的 bind 封装库代码如下，也比较简单：</p>
<pre class="brush: cpp; title: bind.c; notranslate">
/*
   Copyright (C) 2000  Daniel Ryde

   Modified by Uranus Zhou (2014)

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
*/
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;dlfcn.h&gt;
#include &lt;errno.h&gt;
#include &lt;string.h&gt;

int (*real_bind)(int, const struct sockaddr *, socklen_t);
int (*real_connect)(int, const struct sockaddr *, socklen_t);

char *bind_addr_env, *connect_bind_addr_env, *bind_port_env, *bind_type_env, *exclude_ports_env, *connect_port_env, *connect_type_env, *connect_bind_port_env;
unsigned long int bind_addr_saddr;
struct sockaddr_in local_sockaddr_in[] = { 0 };
int bind_port_ns = -1, connect_port_ns = -1;
int bind_type = -1, connect_type = -1;

static int get_sock_type(const char *str)
{
	if (strcasecmp(str, &quot;TCP&quot;) == 0)
		return SOCK_STREAM;
	else if (strcasecmp(str, &quot;UDP&quot;) == 0)
		return SOCK_DGRAM;
	else if (strcasecmp(str, &quot;RAW&quot;) == 0)
		return SOCK_RAW;
	else if (strcasecmp(str, &quot;PACKET&quot;) == 0)
		return SOCK_PACKET;
	else
		return -1;
}

void _init (void)
{
	const char *err;

	real_bind = dlsym (RTLD_NEXT, &quot;bind&quot;);
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, &quot;dlsym (bind): %s\n&quot;, err);
	}

	real_connect = dlsym (RTLD_NEXT, &quot;connect&quot;);
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, &quot;dlsym (connect): %s\n&quot;, err);
	}

	if (bind_addr_env = getenv (&quot;BIND_ADDR&quot;))
		bind_addr_saddr = inet_addr (bind_addr_env);

	if (bind_port_env = getenv (&quot;BIND_PORT&quot;))
		bind_port_ns = htons (atoi(bind_port_env));

	if (bind_type_env = getenv (&quot;BIND_TYPE&quot;))
		bind_type = get_sock_type(bind_type_env);

	exclude_ports_env = getenv (&quot;EXCLUDE_PORTS&quot;);

	if (connect_port_env = getenv (&quot;CONNECT_PORT&quot;))
		connect_port_ns = htons (atoi(connect_port_env));

	if (connect_type_env = getenv (&quot;CONNECT_TYPE&quot;))
		connect_type = get_sock_type(connect_type_env);

	if (connect_bind_addr_env = getenv (&quot;CONNECT_BIND_ADDR&quot;)) {
		local_sockaddr_in-&gt;sin_family = AF_INET;
		local_sockaddr_in-&gt;sin_addr.s_addr = inet_addr (connect_bind_addr_env);
		local_sockaddr_in-&gt;sin_port = htons (0);
	}

	if (connect_bind_port_env = getenv (&quot;CONNECT_BIND_PORT&quot;))
		local_sockaddr_in-&gt;sin_port = htons (atoi(connect_bind_port_env));
}

static int check_port(int port)
{
	char *tmp = exclude_ports_env, *str = NULL;
	if (tmp == NULL) return 0;

	while (1) {
		char szPort[50] = {0};
		str = strchr(tmp, ',');
		if (str == NULL)
			strncpy(szPort, tmp, sizeof(szPort));
		else
			strncpy(szPort, tmp, str - tmp);
		if (atoi(szPort) == port) return 1;
		if (str == NULL) break;
		tmp = str + 1;
	}

	return 0;
}

int bind (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *lsk_in;

	lsk_in = (struct sockaddr_in *)sk;
	if (lsk_in-&gt;sin_family == AF_INET || lsk_in-&gt;sin_family == AF_INET6) {
		int type, length = sizeof(int);

		getsockopt(fd, SOL_SOCKET, SO_TYPE, &amp;type, &amp;length);

		if (check_port(ntohs(lsk_in-&gt;sin_port)) == 0 &amp;&amp; (bind_type_env == NULL || bind_type == type)) {
			// change bind address
			if (lsk_in-&gt;sin_addr.s_addr == htonl (INADDR_ANY) &amp;&amp; bind_addr_env)
				lsk_in-&gt;sin_addr.s_addr = bind_addr_saddr;

			// change bind port
			if (bind_port_env)
				lsk_in-&gt;sin_port = bind_port_ns;
		}
	}
	return real_bind (fd, sk, sl);
}

int connect (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *rsk_in;

	rsk_in = (struct sockaddr_in *)sk;
	if (rsk_in-&gt;sin_family == AF_INET || rsk_in-&gt;sin_family == AF_INET6) {
		int type, length = sizeof(int);

		getsockopt(fd, SOL_SOCKET, SO_TYPE, &amp;type, &amp;length);

		if ((connect_port_env == NULL || connect_port_ns == rsk_in-&gt;sin_port) &amp;&amp; (connect_type_env == NULL || connect_type == type)) {
			// change connect bind address or bind port
			if (connect_bind_addr_env)
				real_bind (fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
		}
	}
	return real_connect (fd, sk, sl);
}
</pre>
<p>编译方法：</p>
<pre><strong>gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE</strong></pre>
<p>运行指定程序时需要通过 LD_PRELOAD 预先加载 bind.so 库，并通过环境变量指定如何过滤修改网络参数：</p>
<ul>
<li>BIND_ADDR 指定本地监听的 IP 地址；</li>
<li>BIND_PORT 指定本地监听的端口；</li>
<li>BIND_TYPE 指定本地监听的 socket 类型（TCP、UDP、RAW、PACKET）；</li>
<li>CONNECT_PORT 指定连接远程主机的端口；</li>
<li>CONNECT_TYPE 指定远程主机 socket 连接类型（与 BIND_TYPE 类似）；</li>
<li>CONNECT_BIND_ADDR 指定连接远程主机时本地使用的 IP 地址；</li>
<li>CONNECT_BIND_PORT 指定连接远程主机时本地使用的端口；</li>
<li>EXCLUDE_PORTS 需要排除的端口列表（以逗号隔开）</li>
</ul>
<p>这里举个例子说明如何使用 bind.so：</p>
<p>假设第三方程序为 testapp，testapp 启动时会本地监听 UDP 775 端口，另外还会在本地 eth0 网卡上监听一个随机的 UDP 端口。现在我们需要将后面这个随机的 UDP 端口固定为 666，而且需要指定使用 eth2 网卡，就可以这样运行 testapp 程序：</p>
<pre><strong>BIND_ADDR="eth2-ip" BIND_PORT=666 BIND_TYPE=UDP EXCLUDE_PORTS=775 LD_PRELOAD=bind.so testapp</strong></pre>
<p>上面的命令中指定了本地 IP 地址（eth2-ip）、绑定端口（666）、socket 类型（UDP），并且使用 EXCLUDE_PORTS 环境变量过滤了固定监听的 775 端口，这样 testapp 程序运行使用的随机 UDP 端口就固定在 666 上了。</p>
<p>我修改的 bind.c 源代码可以从我的 Gist 下载：</p>
<p><a href="https://gist.github.com/zohead/9950663ca01952c940eb" target="_blank">https://gist.github.com/zohead/9950663ca01952c940eb</a></p>
<p>实际使用如果有任何问题欢迎提出指正哦，玩的开心 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/libc-bind-wrapper/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>解决socket在execl之后被自动继承的问题</title>
		<link>https://zohead.com/archives/close-socket-after-execl/</link>
		<comments>https://zohead.com/archives/close-socket-after-execl/#comments</comments>
		<pubDate>Thu, 03 May 2012 17:25:12 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[execl]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[描述符]]></category>
		<category><![CDATA[继承]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=99</guid>
		<description><![CDATA[本文同步自：https://zohead.com/archives/close-socket-after-execl/ Linux下的进程在创建子进程（fork）后，子进程会自动继承父进程的文件描述符，这种机制在没有 execl 运行时一般都没有问题，而且也比较方便在子进程中直接使用父进程中打开的文件描述符，但如果子进程通过 execl 类函数运行新的程序时，这些继承的文件描述符却没有被关闭，导致 execl 之后的程序中也有父进程中文件描述符的拷贝，有些情况就会出现错误。 由于 socket 也在这些文件描述符范围内，本文简单说下解决 socket 被自动继承的几种方法。 首先是测试程序，头 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自：<a href="https://zohead.com/archives/close-socket-after-execl/" target="_blank">https://zohead.com/archives/close-socket-after-execl/</a></p>
<p>Linux下的进程在创建子进程（fork）后，子进程会自动继承父进程的文件描述符，这种机制在没有 execl 运行时一般都没有问题，而且也比较方便在子进程中直接使用父进程中打开的文件描述符，但如果子进程通过 execl 类函数运行新的程序时，这些继承的文件描述符却没有被关闭，导致 execl 之后的程序中也有父进程中文件描述符的拷贝，有些情况就会出现错误。</p>
<p>由于 socket 也在这些文件描述符范围内，本文简单说下解决 socket 被自动继承的几种方法。</p>
<p>首先是测试程序，头文件就没列出来了。</p>
<pre class="brush: cpp; highlight: [11,35]; title: cat &gt; test.c; notranslate">
# ... 包含的头文件... #

int main(int argc, char ** argv)
{
	int ret = 0, sock = 0;
	struct sockaddr_in in_svr = {0};
	pid_t p_ret = 0;

	signal(SIGCHLD, SIG_IGN);

	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sock &lt; 0) {
		perror(&quot;create socket failed&quot;);
		return 1;
	}

	in_svr.sin_family = AF_INET;
	in_svr.sin_addr.s_addr = htonl(INADDR_ANY);
	in_svr.sin_port = htons(5555);

	ret = bind(sock, (struct sockaddr *)&amp;in_svr, sizeof(in_svr));
	if (ret != 0) {
		perror(&quot;bind failed&quot;);
		close(sock);
		return 2;
	}

	p_ret = fork();

	if (p_ret &lt; 0) {
		perror(&quot;fork failed&quot;);
		close(sock);
		return 3;
	} else if (p_ret == 0) {
		execl(&quot;/bin/sleep&quot;, &quot;sleep&quot;, &quot;10&quot;, NULL);
		exit(0);
	}

	sleep(10);
	close(sock);

	return 0;
}
</pre>
<p>父进程和子进程分别 sleep 10秒钟，方便外部查看打开的文件描述符，编译运行程序，使用 lsof 命令就可以看到子进程在 execl 之后的 sleep 进程中也打开了父进程的 socket 描述符（UDP连接），这不是我们想要的效果滴：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# lsof -c sleep
    COMMAND     PID   USER   FD     TYPE  DEVICE SIZE/OFF   NODE      NAME
    sleep       5750  root   cwd    DIR   8,1    4096       8145      /root
    sleep       5750  root   rtd    DIR   8,1    4096       2         /
    sleep       5750  root   txt    REG   8,1    27880      456104    /bin/sleep
    sleep       5750  root   mem    REG   8,1    155696     301801    /lib64/ld-2.12.so
    sleep       5750  root   mem    REG   8,1    1912928    301802    /lib64/libc-2.12.so
    sleep       5750  root   mem    REG   8,1    99158752   391655    /usr/lib/locale/locale-archive
    sleep       5750  root   0u     CHR   136,1  0t0        4         /dev/pts/1
    sleep       5750  root   1u     CHR   136,1  0t0        4         /dev/pts/1
    sleep       5750  root   2u     CHR   136,1  0t0        4         /dev/pts/1
    sleep       5750  root   3u     IPv4  15022  0t0        UDP       *:5555
</pre>
<p>解决办法有如下几种：</p>
<ol>
<li>
<p>比较死板的方法，在子进程 fork 之后关闭所有的 socket 描述符，在 execl 之前加上：</p>
<pre class="brush: cpp; title: ; notranslate">
int fd = 0, fdtablesize = 0;

for (fd = 0, fdtablesize = getdtablesize(); fd &amp;lt; fdtablesize; fd++) {
  // close all socket handle inherited from parent process
  if (isfdtype(fd, S_IFSOCK)) {
      close(fd);
  }
}
</pre>
<p>这种处理比较彻底，即使子进程没有 execl 也会关闭继承的 socket 句柄，如果要关闭从父进程继承的所有句柄，去掉 isfdtype 判断即可。</p>
</li>
<li>
<p>使用 fcntl 为父进程 socket 设置 O_CLOEXEC 标志，表示此 socket 在 execl 之前自动关闭（注意并非在 fork 之后），在 socket 创建完成之后加上：</p>
<pre class="brush: cpp; title: ; notranslate">
ret = fcntl(sock, F_GETFD);
ret = fcntl(sock, F_SETFD, ret | FD_CLOEXEC);
</pre>
</li>
<li>
<p>Linux 2.6.27 之后创建 socket 时开始支持 SOCK_CLOEXEC 参数，与上面的效果相似，只要改创建 socket 的地方：</p>
<pre class="brush: cpp; title: ; notranslate">
sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
</pre>
<p>另外 Linux 2.6.27 之后创建 socket 另外支持 SOCK_NONBLOCK 参数，如果设置此参数，socket 自动创建为非阻塞式。</p>
</li>
</ol>
<p>以上三种办法均在 RHEL 6.1 64位系统上编译测试通过，有任何问题欢迎交流，玩的开心 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/close-socket-after-execl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
