免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 6623 | 回复: 11
打印 上一主题 下一主题

[原创] 300 行代码 "打磨" 自己的搜索引擎 myso (示例源码下载) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-01-24 15:44 |只看该作者 |倒序浏览
300 行代码 打磨自己的搜索引擎 myso (示例源码下载)

关键字:
   myso 搜索引擎 C语言 socket proxy CSP eybuild

功能概述:
   A. 本示例以 "五笔编码&汉语拼音查询" 和 "IP地址所在地和域名" 为例.
   B. 适合 OS 平台包含: Linux/Unix, Windows
   C. Windows 平台支持自动 IE 代理搜索, 以适应连接 intenet.
   D. 直接调用系统的 socket 相关 C 接口API.
   E. 支持自动域名解析.

示例抓图:
   http://www.eybuild.com/develop/images/myso.jpg


1. 问题提出:
   我们要打磨一个自己的搜索引擎,
   (1) 需要有数据源. 数据源从哪里来?
   (2) 需要用动态页面处理用户请求, 采用什么动态网页技术?
   (3) 如何提高搜索引擎的并发性能, 减小代码量和运行时的内存尺寸?
   (3) 我的服务器放在哪里, 如何使我的引擎适应更广泛的连接方式和应用平台?
       即: 如何支持直接连接/代理连接连接方式, 同时支持 Windows/Unix 等多种 OS 平台.
   
2. 分析与方案:
   1. 数据源我可以直接动态从 intenet 上获取. 这样既能减小我们对数据源的维护量,
      也能保证数据是最新. 如本例采用的来自 http://www.ip138.com/ 的数据源.
   2. 支持动态网页的技术很多, 如 CGI(CSP/eybuild), PHP, ASP, JSP, ...
   3. 考虑到并发性能, 代码尺寸, 我们采用能用 C 语言编写 CGI 的 CSP 技术.
      CSP/eybuild 不需要安装任何脚本解释器, 运行最低只要 64K 的动态内存即可,
      具有更泛的 C 语言平台支撑, 易学易用.
   4. CSP 直接以 C 为基础, 可以在 Unix/Windows 上移植使用, 可以直接调用系统中的
      任何系统 API, 集成功能强大. 编写代理连接方式等功能十分容易(见示例源码).

3. 打磨原理:

   如下图所示, 展示了一个整个处理流程的意示图:

  1.          |       +---------+           +----------+           +---------+
  2.      O   |       |         |---HTTP--->|   MySo   |---HTTP--->|   DATA  |
  3.     ---  |------>| Browser |           |WEB Server|           | Server  |
  4.     / \  |       |   PC    |<--HTTP--- |  (CGI)   |<--HTTP----|         |
  5.    (man)         +---------+           +----------+           +---------+   

  6.   -- 1 ------------- 2 --------------------- 3 ------------------- 4 ----
复制代码


   (1) 最左是一个用户, 向浏览器的表单中输入数据.
   (2) 中间偏左是浏览器, 它把用户请求通过 http 数据提交给 myso.cgi 所在 webserver.
   (3) Web服务器执行 myso.cgi. 然后 myso.cgi 会连接远程的数据服务器(能提供搜索功能),
       myso.cgi 收到查询结果后, 分析查询结果. 重新构造自己的查询结果.
   (4) Data Server 其实就是个能提供搜索功能的服务器, 它本身就是个搜索引擎.
       我们这里是借用它的数据源, "打磨" 自己的搜索引擎.

   好了, 经过上面的分析可以已经能看出整个处理过程了.

   可能有人会说, 这是一种 "盗链". 是的, 必须承认这是一种盗链, 不过本示例还解决了
   防 "盗链" 的相关技术(见下文). 这里就是为什么标题中称为"打磨"的本意.

4. 原码分析:
   (1) 源码中设及 API 说明等的相关说明:
       1) <% %> - 用于在 HTML 等模板文件中嵌入 C 程序 (即 CSP 语句)
       2) @g    - 标记块, 用于在 HTML 等模板文件中 嵌入 C 函数等全局性信息.
       3) @b    - 将被 CSP 页开始处执行.
       4) G()   - 函数, 从用户提交的表单中获取输入的值, 相关于 getParameter() 函数.
       5) 其它 API 请参阅相关手册.

   (2) 源码可以分为三部分理解:
       1) html 部分 (1-71 行), 这部分主要是 html 模板
       2) @b 部分 (73-165 行), 这部分是当请求提交给该页面时, 被首先执行. 即: begin
       3) @g 部分 (167-328行), 这部分定义了与页面相关的全局的函数,
          可以在程序任意处调用. 即: global.
  
   (3) @b 部分(73-165 行):
       1) 74 - 87 行, 说明了该函数需要包含的 C 程序的头文件, 注意用 @include 不是 #include
       2) 88 - 94 定义了,  C 的局变量
       3) 96 - 123 用于处理五笔编码和汉语拼音的查询.
       4) 128- 164 用于处理五 IP地址所在地和域名 的查询, 与(3) 的流程类似.
       5) 97 - 98 确认是用提交的数据不为空, 并且成功连接远程序数据服务器
       6) 103-110 手工构造 HTTP 的 POST 请求, 以向远程服务器提交查询.
       7) 112-116 发送 http 请求并接收应答到一个 buffer 中.
       8) 117-121 从应答中分离出来我们需要的数据, 应答的是一个 html 文件.
       9) 160-164 如果连接错误, 则根据错误类型构造错误信息并存入 errmsg 中.

   (4) html 部分(1-71 行):
       1) 56 - 60 行, 通过 <% %> 嵌入一断 C 程序的判断语句,
          如果是错误则输出错误到当前处, 否则把查询结果输出到当前处.
       2) 69 行的 <% //=buff %> 是调试用的, 已被 // 注释掉了.
          可以把 // 去掉以把来自数据服务器的全部应答打印出来.
       3) 其它的就不细说了.

   (5) @g 部分 (167-328行)
       1) 这里定义了 6 个函数
          int SocketInit();
          int get_proxy(char * proxy_ip, short * port);
          char * make_error(char * errmsg);
          int connect_query_host(char * hostname, char * errmsg);
          int send_http_req(int sock, char * http_req);
          int recv_http_req(int sock, char * http_req, int maxlen);
       2) 前两个是 windows 专用.
          SocketInit() 用于初始化 socket
          get_proxy() 从注册表中读取 IE 的代理设置.
       3) connect_query_host() 用于连接远程数据服务器,
          当设置代理时, 先尝试使用代理连接.
       4) send_http_req() 和 recv_http_req() 发送和接收 http 请求.
         
   (6) 附源码

001  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
002  "http://www.w3.org/TR/html4/loose.dtd">
003  <!--
004  源码来自: http://www.eybuild.com
005  支持平台: Unix/Windows
006  Unix编译: make clean all
007  变更请通知作者: eybuild@hotmail.com
008  -->
009  <html>
010  <head>
011  <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
012  <title>五笔编码查询</title>
013  <link href="/css/tq.css" rel="stylesheet" type="text/css">
014  <script language="javascript" src="<% =romPrefix(NULL) %>/js/myso.js"></script>
015  </head>
016  <style><!--
017  body,td,a,p,{font-family:arial,sans-serif;font-size:14px}
018  --></style>
019  <body>
020  <p>
021  <center><img src="<% =romPrefix(NULL) %>/img/myso.jpg"></center>
022  <table width="430" border="0" align="center" cellspacing="0";">
023      <tr>
024          <td colspan="3"> </td>
025      </tr>
026      <tr>
027          <td colspan="3"><strong>五笔编码&汉语拼音查询</strong></td>
028      </tr>
029      <form name="form1" method="post" action="<% =thisCgiPrefix() %>" onsubmit="return check()">
030      <tr>
031          <td width=135>请输入汉字:</td>
032          <td><input name="querykey" type="text" size="20" value=""></td>
033          <td><input type="submit" name="wubiquery" value="开始查询""></td>
034      </tr>
035      </form>
036      <tr>
037          <td colspan="3"> </td>
038      </tr>
039      <tr>
040          <td colspan="3"><strong>IP地址所在地/域名查询</strong></td>
041      </tr>
042      <form name="ipform" method=post action="<% =thisCgiPrefix() %>" onsubmit="return checkIP();">
043      <tr>
044          <td>请输入IP或域名:</td>
045          <td><input type="text" name="ip" size="20">
046          <td><input type="submit" name="ipquery" value="开始查询">
047          <input TYPE="hidden" name="action" value="2">
048      </tr>
049      </form>
050      <tr>
051          <td colspan="3"> </td>
052      </tr>
053  </table>
054  <table width="430"  border="0" align="center" cellspacing="0">
055      <tr>
056          <td><font color=red><% if (!isblankstr(errmsg))    /* report error */
057                     print("<b>查询失败:</b> %s", errmsg);
058                 else
059                     print("%.*s", qlen, qstart);    /* output query result */
060              %></font>
061          </td>
062      </tr>
063      <tr><td> </td></tr>
064      <tr><td><center><a href="http://www.eybuild.com" target=_blank>源码下载<A> |
065              <a href="maito=eybuild@hotmail.com" target=_blank>联系作者<A> |
066              <a href="http://www.eybuild.com" target=_blank>更多信息<A> </center></td></tr>
067      <tr><td><center>(来自: http://www.eybuild.com)</center></td></tr>
068  </table>
069  <xmp><% //=buff %></xmp>
070  </body>
071  </html>
072  
073  <% @b
074  @include <undef.h>
075  @ifdef WIN32
076  @include <winsock2.h>
077  @define close   closesocket
078  @else
079  @include <unistd.h>
080  @include <errno.h>
081  @include <sys/types.h>
082  @include <sys/socket.h>
083  @include <netinet/in.h>
084  @include <arpa/inet.h>
085  @include <netdb.h>
086  @endif /* WIN32 */
087  @include <ebdef.h>
088      int                 sock = 0;
089      char                buff[4096] = "";
090      int                 maxlen = sizeof(buff);
091      char *              qstart = "";    /* query result start address */
092      int                 qlen = 0;       /* query result length */
093      char                errmsg[256] = "";
094      int                 ret = OK;
095      
096      /* wubi query */
097      if (!isblankstr(G("querykey")) &&
098              (sock = connect_query_host("qq.ip138.com", errmsg)) > 0)
099      {
100          char            req_buf[1024] = "";
101          char            query[256] = "";
102         
103          /* make query and http header */
104          sprintf(query, "querykey=%s", urlEncode(G("querykey")));
105          sprintf(req_buf, "POST http://qq.ip138.com/wb/wb.asp? HTTP/1.0\r\n"
106                           "Content-Type: application/x-www-form-urlencoded\r\n"
107                           "Content-Length: %d\r\n"
108                           "Host: qq.ip138.com\r\n"
109                           "\r\n"
110                           "%s", strlen(query), query);
111  
112          /* send ==> receive ==> parse result */
113          if ((ret=send_http_req(sock, req_buf)) > 0)
114          {
115              if ((ret=recv_http_req(sock, buff, sizeof(buff))) > 0)
116              {
117                  /* separate result */
118                  if (NULL != (qstart=strstr(buff, "<p align=\"center\">\r\n")))
119                  {
120                      qlen = strstr(qstart, "</p>") - qstart;
121                  }
122              }
123          }
124         
125          close(sock);
126      }
127      
128      /* ip query */
129      if (!isblankstr(G("ip")) &&
130              (sock = connect_query_host("www.ip138.com", errmsg)) > 0)
131      {
132          char            req_buf[1024] = "";
133          char            query[256] = "";
134         
135          /* make query and http header */
136          sprintf(query, "ip=%s&action=2", G("ip"));
137          sprintf(req_buf, "POST http://www.ip138.com/ips8.asp HTTP/1.0\r\n"
138                           "Referer: http://www.ip138.com/\r\n"
139                           "Content-Type: application/x-www-form-urlencoded\r\n"
140                           "Content-Length: %d\r\n"
141                           "Host: www.ip138.com\r\n"
142                           "\r\n"
143                           "%s", strlen(query), query);
144                           
145          /* send ==> receive ==> parse result */
146          if ((ret=send_http_req(sock, req_buf)) > 0)
147          {
148              if ((ret=recv_http_req(sock, buff, sizeof(buff))) > 0)
149              {
150                  if (NULL != (qstart=strstr(buff, "<ul class=\"ul1\">")))
151                  {
152                      qlen = strstr(qstart, "</td>") - qstart;
153                  }
154              }
155          }
156         
157          close(sock);
158      }
159      
160      /* make error message */
161      if (sock < 0 || ret < 0)
162      {
163          make_error(errmsg);
164      }
165  %>  
166  
167  <% @g
168  
169  #ifdef WIN32
170  char * make_error(char * errmsg)
171  {
172      int         errcode;
173      LPVOID      lpMsgBuf;
174      
175      if (OK == (errcode=GetLastError()))
176          return "Ready";
177         
178      FormatMessage(
179          FORMAT_MESSAGE_ALLOCATE_BUFFER |
180          FORMAT_MESSAGE_FROM_SYSTEM |
181          FORMAT_MESSAGE_IGNORE_INSERTS,
182          NULL,
183          errcode,
184          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
185          (LPTSTR) &lpMsgBuf,
186          0,
187          NULL
188      );
189      
190      // Process any inserts in lpMsgBuf.
191      sprintf(errmsg, "%d: %s", errcode, lpMsgBuf);
192      
193      // Free the buffer.
194      LocalFree( lpMsgBuf );
195      
196      return errmsg;
197  }
198  
199  int SocketInit()
200  {
201  
202      WSADATA wsaData;
203  
204      if ( WSAStartup( 0x101, &wsaData ) ) {
205          fprintf( stderr, "Could not initialize WinSock\n" );
206          return ERROR;
207      }
208      
209      if ( 0x101 != wsaData.wVersion ) {
210          fprintf( stderr, "Version %x not supported\n", wsaData.wVersion );
211          return ERROR;
212      }
213      
214      return OK;
215  }
216  
217  /* try to get proxy from windows register */
218  int get_proxy(char * proxy_ip, short * port)
219  {
220      HKEY        hkey;
221      DWORD       d=sizeof(char);
222      char *      path = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
223      unsigned long   type;
224      unsigned char   data[256] = "";
225      unsigned long   dlen = sizeof(data);
226      char *          pstr = NULL;
227  
228      proxy_ip[0] = '\0';
229      if (ERROR_SUCCESS==RegOpenKeyEx(HKEY_CURRENT_USER, path, 0, KEY_READ, &hkey))   
230      {
231          if (ERROR_SUCCESS!=RegQueryValueEx(
232                  hkey, "ProxyServer", 0, &type, (unsigned char*)data, &dlen))
233          {
234              RegCloseKey(hkey);
235              return -1;
236          }
237      
238          if (NULL != (pstr=strchr(data, ':')))
239          {
240              sprintf(proxy_ip, "%.*s", pstr-data, data);
241              *port = (short)atoi(pstr+1);
242          }
243  
244          RegCloseKey(hkey);
245          return 0;
246      }
247      
248      return -1;
249  }
250  
251  #else /* WIN32 */
252  /* for unix */
253  char * make_error(char * errmsg)
254  {
255      strcpy(errmsg, strerror(errno));
256      
257      return errmsg;
258  }
259  #endif /* WIN32 */
260  
261  
262  int connect_query_host(char * hostname, char * errmsg)
263  {
264      int         sock = 0;
265      struct sockaddr_in  sa;
266      char        dstip[20] = "";
267      short       port = 0;
268      
269  #ifdef WIN32
270      if (SocketInit() < 0)
271          return ERROR;
272      
273      memset(&sa, 0, sizeof(sa));
274      sa.sin_family=AF_INET;
275      
276      /* by proxy */
277      if (!get_proxy(dstip, &port) && !isblankstr(dstip))
278      {
279          sa.sin_port = htons(port);
280          sa.sin_addr.s_addr = inet_addr(dstip);
281      }
282      /* by domain name */
283      else
284  #endif /* WIN32 */
285      {
286          struct hostent *    ht = gethostbyname(hostname);
287         
288          /* parse domain error */
289          if (NULL == ht)
290              return ERROR;
291  
292          sa.sin_port = htons(80);
293          sa.sin_addr.s_addr = (*(struct in_addr *)ht->h_addr_list[0]).s_addr;
294      }
295      
296      if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
297          return ERROR;
298  
299      if (connect(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0)
300          return ERROR;
301      
302      return sock;
303  }  
304  
305  
306  int send_http_req(int sock, char * http_req)
307  {
308      int             len;
309      
310      len = send(sock, http_req, strlen(http_req), 0);
311      
312      return len;
313  }
314  
315  
316  int recv_http_req(int sock, char * http_req, int maxlen)
317  {
318      int             len;
319      int             dlen = 0;
320      
321      while(dlen < maxlen-1 && (len=recv(sock, http_req+dlen, maxlen-dlen-1, 0)) > 0)
322      {
323          dlen += len;
324      }
325      
326      return dlen;
327  }
328  %>


附录:相关参考
1. CSP/eybuild 的主站点:http://www.eybuild.com
2.《eyBuild 中文手册》 下载地址:http://www.eybuild.com/develop/doc/manual/eybuild_manual_ch.pdf
3.《CSP/eybuild APIs参考》, 在线阅读:http://www.eybuild.com/develop/doc/API/libIndex.htm
4. CSP/eybuild API 函数列表, 在线阅读:http://www.eybuild.com/develop/doc/API/rtnIndex.htm
5.《CSP/eybuild FAQ(常见问题)》 下载地址:http://www.eybuild.com/develop/doc/manual/eybuild_faq_ch.pdf

~ 完 ~
2007.1.24

论坛徽章:
0
2 [报告]
发表于 2007-01-24 23:12 |只看该作者
好贴 先顶 再读

论坛徽章:
0
3 [报告]
发表于 2007-01-24 23:27 |只看该作者
good job

论坛徽章:
0
4 [报告]
发表于 2007-01-25 09:21 |只看该作者
非常好,谢谢

论坛徽章:
0
5 [报告]
发表于 2007-01-25 09:25 |只看该作者
经典~~~~~!  

   谢谢 newzy 这段时间对我不厌其烦的帮助,小弟支持你!

论坛徽章:
0
6 [报告]
发表于 2007-01-25 10:56 |只看该作者
厉害,
我也写过http编程,可惜只是利用一个webserver现有的封装好的函数,对CGI和HTTP不是很熟

论坛徽章:
0
7 [报告]
发表于 2007-01-25 14:58 |只看该作者
好帖!

论坛徽章:
0
8 [报告]
发表于 2007-01-25 15:23 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
9 [报告]
发表于 2007-01-25 16:35 |只看该作者
拿来干什么?

把baidu google 的结果再整合一次?

论坛徽章:
0
10 [报告]
发表于 2007-01-26 11:06 |只看该作者
真强!这么快就出来了!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP