用BirdNest在Google App Engine上搭建一个twitter API

想在手机上看看twitter,下了个程序,但登录不上去,网上找来找去也找不到一个可以放心使用的api,看到一篇教程,用birdnest在google app engine上搭建一个api就好了,幸好gae还没有被封。

环境准备:Google App Engine

先要注册Google App Engine,注册地址 http://appengine.google.com/ ,然后建立一个application,目前第一次使用需要验证用户手机,输入手机号码就收验证码即可,之后,就可以用 yourid.appspot.com来访问你的app应用。

此外,还需要下载安装Google APP Engine的开发环境,注意Python的版本,需要是2.6系列的,不能使用3.1系列的,否则运行会出错。

Google App Engine SDK 下载地址 http://code.google.com/intl/zh-CN/appengine/downloads.html

Python 2.6.5 下载地址 http://www.python.org/download/releases/2.6.5/

环境准备:BirdNest

下载birdnest要注意是下载分支branches/gae,别下载主干trunk,否则更新到GAE上也不能用,会报错。可以使用一个SVN工具下载。例如TortoiseSVN等。 将其放到一个目录中,进入目录,编辑app.yaml文件,将第一行的application里的参数修改为自己的应用名。

发布应用到GAE

准备好了上面的一切后,就可以发布这个应用到自己的Appspot上了,打开Google App Engine Launcher。点击菜单创建一个新的application(这里创建的application名称要和上面修改的app.yaml应用名一样),然后把下载的birdnest文件复制到这个application的目录里面,(ps:在复制文件到这个application目录下面的之后,在我的电脑上发现Google App Engine Launcher里面的这个应用无效,最后把app.yaml用记事本打开重新保存一遍就好了,可能是文件编码的问题,如果你也出现了这个错误,可以试试把文件重新在记事本里面打开再保存一下),最好在Google App Engine Launcher里面选中这个应用后,点击Deploy按钮把代码上传到网上去,等待上传完毕后,你创建的API地址应该是:应用名.appspot.com/api/

打开api可能出现的错误提示和解决办法

出现“internal server error”错误,这个看别个说是代码里面有问题,你可以尝试删除code.py里面的下面一段代码:

import socket
import re
ua = web.ctx.environ.get("HTTP_USER_AGENT", 'None')
if ua.find('jibjib') >= 0:
socket.setdefaulttimeout(60)
elif ua.find('zh-CN') >= 0:
#raise Exception('unknown error')
socket.setdefaulttimeout(2)
else:
socket.setdefaulttimeout(2)

如果不想去下载birdnest或者运行时程序出现错误,可以试试本站提供的压缩包:birdnest.rar
注意把app.yaml文件里面的你的应用名字改成你自己的。

支付宝导入数字证书出现Microsoft Visual C++ Runtime Library

windows 7 x64操作系统,用IE8登录支付宝导入证书的时候出现Microsoft Visual C++ Runtime Library错误,貌似是因为IE8的安全选项默认设置的问题,导致了这个错误。进入internet选项,修改一下安全设置就可以了。

IE8浏览器的菜单:工具 -> Internet选项 -> 安全 -> 自定义级别 -> 将本地文件上载至服务器时包含本地目录路径,选中启用即可。

Firefox里实现自动换行的方法

为了是文字自动换行,IE里面可以加入这个样式属性text-justify:inter-ideograph;但firefox里面就不起作用了,文章不换行, 会突破div边框的限制, 在外部显示, 严重影响了页面显示的美观和整洁。

现提供一种解决方法, 首先创建wrap.xml, 内容如下

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml">
	<binding id="wordwrap" applyauthorstyles="false">
		<implementation>
			<constructor>
			//<![CDATA[
			var elem = this;
			doWrap();
			elem.addEventListener('overflow', doWrap, false);

			function doWrap()
			{
				var walker = document.createTreeWalker(elem, NodeFilter.SHOW_TEXT, null, false);
			 	while (walker.nextNode())
				{
					var node = walker.currentNode;
					node.nodeValue = node.nodeValue.split('').join(String.fromCharCode('8203'));
				}
			}
			//]]>
			</constructor>
		</implementation>
	</binding>
</bindings>

然后页面在页面中这样使用, 内容如下:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title></title>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<style>
			.wordwrap {
				white-space: pre-wrap; /* css-3 */
				white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
				white-space: -pre-wrap; /* Opera 4-6 */
				white-space: -o-pre-wrap; /* Opera 7 */
				word-wrap: break-word; /* Internet Explorer 5.5+ */
				-moz-binding: url('./wordwrap.xml#wordwrap');
			}
		</style>
	</head>
	<body>
		<div style="width:200px;border:1px solid red;" class="wordwrap">
			asdlfkasdfjlaksjdflajsdlfkajsdaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaalskdjflakjsdflajsdlf asdkfjlaksjdf alksjdlfkjasdlfkjalsdjfasdf as dfalskdjflkajsdlfkjalsdkjflasdf askdjfasdf asdfasdfasdfasdf
		</div>
	</body>
</html>

用Yahoo Pipes制作全文输出的RSS:cnbeta全文输出方法

cnbeta的rss全文输出方法。共享一个我制作的cnbeta的全文输出feed:
http://pipes.yahoo.com/pipes/pipe.run?_id=035788f8a9608efa4e74a2f0960c2cd1&_render=rss

很多网站尤其是一些新闻类的网站虽然提供RSS输出,像我天天光顾的cb就不是全文输出,如果要看全文还要点击进去,这样总感觉不是很爽……不过现在有了Yahoo Pipes就好办了!这是Yahoo提供的一款超级强大的RSS处理工具,今天俺就教你如何用它来输出网站的全文!

第一步

先在 Yahoo Pipes 里新建一个 pipe(如图)

Create a pipe

第二步

拖入一个 Fetch Feed 模块,输入你想要全文输出的RSS地址(如图我添加的是cnbeta的RSS

第三步

然后到Operators条目下拖入一个 Loop 模块,与 Fetch Feed 相连接

再到Sources条目拖一出个 Fetch Page 模块拖进入 Loop(注意是拖进Loop里面去,如图)

设置 URL 为 item.link

第四步

这是最关键的一步!!!

随便打开你要全文输出的RSS其中一篇文章,然后等网页加载完毕后,查看这篇网页的源代码

然后查找网页源代码中正文部分,把能囊括正文的开始的地方(align="right" id="sign" onload="fixPNG(this)"/></a>)和结尾的地方(<div class="digbox">)找到。

然后分别填到Cut content from和to里面,如图

第五步

把assign项选为first,然后把results to填为item.description,将 Loop 连接到 Pipe Out,保存,大功告成!!!

最后

当然到这里已经可以全文输出cb的feed了,但还有一点不完美的地方,就是那个截取的内容前面都有align="right" id="sign" onload="fixPNG(this)"/></a>,这里我们可以插入一个regex操作,把这段字符串去掉。从左边的operators里面拖一个regex到右边来。然后在in里面选择item.description.content,replace里面填写align="right" id="sign" onload="fixPNG\(this\)"/></a>,with里面不用填(就是空着)。最后我的pipe图是这样的:

最最后

你可以点击下面的按钮和链接就行调试,不做多说了!这样获取的全文RSS肯定没有直接获取的高效,本人刚刚使用还不知道会不会出现很滞后的现象。(注:文章中有几个图片用的是别个的,懒得截图了)

一个比较全的C#写的ftp类

using System;
using System.Net;
using System.IO;
using System.Text;
using System.Net.Sockets;

/// <summary>
/// FTPClient 的摘要说明。
/// </summary>
public class FTPClient
{
	#region 构造函数
	/// <summary>
	/// 缺省构造函数
	/// </summary>
	public FTPClient()
	{
		strRemoteHost = "";
		strRemotePath = "";
		strRemoteUser = "";
		strRemotePass = "";
		strRemotePort = 21;
		bConnected = false;
	} /// <summary>
	/// 构造函数
	/// </summary>
	/// <param name="remoteHost"></param>
	/// <param name="remotePath"></param>
	/// <param name="remoteUser"></param>
	/// <param name="remotePass"></param>
	/// <param name="remotePort"></param>
	public FTPClient( string remoteHost, string remotePath, string remoteUser, string remotePass, int remotePort )
	{
		strRemoteHost = remoteHost;
		strRemotePath = remotePath;
		strRemoteUser = remoteUser;
		strRemotePass = remotePass;
		strRemotePort = remotePort;
		Connect();
	}
	#endregion

	#region 登陆
	/// <summary>
	/// FTP服务器IP地址
	/// </summary>
	private string strRemoteHost;
	public string RemoteHost
	{
		get
		{
			return strRemoteHost;
		}
		set
		{
			strRemoteHost = value;
		}
	}
	/// <summary>
	/// FTP服务器端口
	/// </summary>
	private int strRemotePort;
	public int RemotePort
	{
		get
		{
			return strRemotePort;
		}
		set
		{
			strRemotePort = value;
		}
	}
	/// <summary>
	/// 当前服务器目录
	/// </summary>
	private string strRemotePath;
	public string RemotePath
	{
		get
		{
			return strRemotePath;
		}
		set
		{
			strRemotePath = value;
		}
	}
	/// <summary>
	/// 登录用户账号
	/// </summary>
	private string strRemoteUser;
	public string RemoteUser
	{
		set
		{
			strRemoteUser = value;
		}
	}
	/// <summary>
	/// 用户登录密码
	/// </summary>
	private string strRemotePass;
	public string RemotePass
	{
		set
		{
			strRemotePass = value;
		}
	} /// <summary>
	/// 是否登录
	/// </summary>
	private Boolean bConnected;
	public bool Connected
	{
		get
		{
			return bConnected;
		}
	}
	#endregion

	#region 链接
	/// <summary>
	/// 建立连接
	/// </summary>
	public void Connect()
	{
		socketControl = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
		IPEndPoint ep = new IPEndPoint( IPAddress.Parse( RemoteHost ), strRemotePort );
		// 链接
		try
		{
			socketControl.Connect( ep );
		}
		catch( Exception )
		{
			throw new IOException( "Couldn't connect to remote server" );
		}   // 获取应答码
		ReadReply();
		if( iReplyCode != 220 )
		{
			DisConnect();
			throw new IOException( strReply.Substring( 4 ) );
		}   // 登陆
		SendCommand( "USER " + strRemoteUser );
		if( !( iReplyCode == 331 || iReplyCode == 230 ) )
		{
			CloseSocketConnect();//关闭连接
			throw new IOException( strReply.Substring( 4 ) );
		}
		if( iReplyCode != 230 )
		{
			SendCommand( "PASS " + strRemotePass );
			if( !( iReplyCode == 230 || iReplyCode == 202 ) )
			{
				CloseSocketConnect();//关闭连接
				throw new IOException( strReply.Substring( 4 ) );
			}
		}
		bConnected = true;   // 切换到目录
		ChDir( strRemotePath );
	}
	/// <summary>
	/// 关闭连接
	/// </summary>
	public void DisConnect()
	{
		if( socketControl != null )
		{
			SendCommand( "QUIT" );
		}
		CloseSocketConnect();
	}
	#endregion

	#region 传输模式
	/// <summary>
	/// 传输模式:二进制类型、ASCII类型
	/// </summary>
	public enum TransferType { Binary, ASCII }; /// <summary>
	/// 设置传输模式
	/// </summary>
	/// <param name="ttType">传输模式</param>
	public void SetTransferType( TransferType ttType )
	{
		if( ttType == TransferType.Binary )
		{
			SendCommand( "TYPE I" );//binary类型传输
		}
		else
		{
			SendCommand( "TYPE A" );//ASCII类型传输
		}
		if( iReplyCode != 200 )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
		else
		{
			trType = ttType;
		}
	}
	/// <summary>
	/// 获得传输模式
	/// </summary>
	/// <returns>传输模式</returns>
	public TransferType GetTransferType()
	{
		return trType;
	}

	#endregion

	#region 文件操作
	/// <summary>
	/// 获得文件列表
	/// </summary>
	/// <param name="strMask">文件名的匹配字符串</param>
	/// <returns></returns>
	public string[] Dir( string strMask )
	{
		// 建立链接
		if( !bConnected )
		{
			Connect();
		}   //建立进行数据连接的socket
		Socket socketData = CreateDataSocket();

		//传送命令
		SendCommand( "NLST " + strMask );   //分析应答代码
		if( !( iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226 ) )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}   //获得结果
		strMsg = "";
		while( true )
		{
			int iBytes = socketData.Receive( buffer, buffer.Length, 0 );
			strMsg += ASCII.GetString( buffer, 0, iBytes );
			if( iBytes < buffer.Length )
			{
				break;
			}
		}
		char[] seperator = { '\n' };
		string[] strsFileList = strMsg.Split( seperator );
		socketData.Close();//数据socket关闭时也会有返回码
		if( iReplyCode != 226 )
		{
			ReadReply();
			if( iReplyCode != 226 )
			{
				throw new IOException( strReply.Substring( 4 ) );
			}
		}
		return strsFileList;
	}
	/// <summary>
	/// 获取文件大小
	/// </summary>
	/// <param name="strFileName">文件名</param>
	/// <returns>文件大小</returns>
	private long GetFileSize( string strFileName )
	{
		if( !bConnected )
		{
			Connect();
		}
		SendCommand( "SIZE " + Path.GetFileName( strFileName ) );
		long lSize = 0;
		if( iReplyCode == 213 )
		{
			lSize = Int64.Parse( strReply.Substring( 4 ) );
		}
		else
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
		return lSize;
	}
	/// <summary>
	/// 删除
	/// </summary>
	/// <param name="strFileName">待删除文件名</param>
	public void Delete( string strFileName )
	{
		if( !bConnected )
		{
			Connect();
		}
		SendCommand( "DELE " + strFileName );
		if( iReplyCode != 250 )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
	}
	/// <summary>
	/// 重命名(如果新文件名与已有文件重名,将覆盖已有文件)
	/// </summary>
	/// <param name="strOldFileName">旧文件名</param>
	/// <param name="strNewFileName">新文件名</param>
	public void Rename( string strOldFileName, string strNewFileName )
	{
		if( !bConnected )
		{
			Connect();
		}
		SendCommand( "RNFR " + strOldFileName );
		if( iReplyCode != 350 )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
		// 如果新文件名与原有文件重名,将覆盖原有文件
		SendCommand( "RNTO " + strNewFileName );
		if( iReplyCode != 250 )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
	}
	#endregion

	#region 目录操作
	/// <summary>
	/// 创建目录
	/// </summary>
	/// <param name="strDirName">目录名</param>
	public void MkDir( string strDirName )
	{
		if( !bConnected )
		{
			Connect();
		}
		SendCommand( "MKD " + strDirName );
		if( iReplyCode != 257 )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
	}

	/// <summary>
	/// 删除目录
	/// </summary>
	/// <param name="strDirName">目录名</param>
	public void RmDir( string strDirName )
	{
		if( !bConnected )
		{
			Connect();
		}
		SendCommand( "RMD " + strDirName );
		if( iReplyCode != 250 )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
	}

	/// <summary>
	/// 改变目录
	/// </summary>
	/// <param name="strDirName">新的工作目录名</param>
	public void ChDir( string strDirName )
	{
		if( strDirName.Equals( "." ) || strDirName.Equals( "" ) )
		{
			return;
		}
		if( !bConnected )
		{
			Connect();
		}
		SendCommand( "CWD " + strDirName );
		if( iReplyCode != 250 )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
		this.strRemotePath = strDirName;
	}

	#endregion

	#region 上传和下载
	/// <summary>
	/// 下载一批文件
	/// </summary>
	/// <param name="strFileNameMask">文件名的匹配字符串</param>
	/// <param name="strFolder">本地目录(不得以\结束)</param>
	public void Get( string strFileNameMask, string strFolder )
	{
		if( !bConnected )
		{
			Connect();
		}
		string[] strFiles = Dir( strFileNameMask );
		foreach( string strFile in strFiles )
		{
			if( !strFile.Equals( "" ) )//一般来说strFiles的最后一个元素可能是空字符串
			{
				Get( strFile, strFolder, strFile );
			}
		}
	}
	/// <summary>
	/// 下载一个文件
	/// </summary>
	/// <param name="strRemoteFileName">要下载的文件名</param>
	/// <param name="strFolder">本地目录(不得以\结束)</param>
	/// <param name="strLocalFileName">保存在本地时的文件名</param>
	public void Get( string strRemoteFileName, string strFolder, string strLocalFileName )
	{
		if( !bConnected )
		{
			Connect();
		}
		SetTransferType( TransferType.Binary );
		if( strLocalFileName.Equals( "" ) )
		{
			strLocalFileName = strRemoteFileName;
		}
		if( !File.Exists( strLocalFileName ) )
		{
			Stream st = File.Create( strLocalFileName );
			st.Close();
		}
		FileStream output = new
		 FileStream( strFolder + "\\" + strLocalFileName, FileMode.Create );
		Socket socketData = CreateDataSocket();
		SendCommand( "RETR " + strRemoteFileName );
		if( !( iReplyCode == 150 || iReplyCode == 125
		 || iReplyCode == 226 || iReplyCode == 250 ) )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
		while( true )
		{
			int iBytes = socketData.Receive( buffer, buffer.Length, 0 );
			output.Write( buffer, 0, iBytes );
			if( iBytes <= 0 )
			{
				break;
			}
		}
		output.Close();
		if( socketData.Connected )
		{
			socketData.Close();
		}
		if( !( iReplyCode == 226 || iReplyCode == 250 ) )
		{
			ReadReply();
			if( !( iReplyCode == 226 || iReplyCode == 250 ) )
			{
				throw new IOException( strReply.Substring( 4 ) );
			}
		}
	}
	/// <summary>
	/// 上传一批文件
	/// </summary>
	/// <param name="strFolder">本地目录(不得以\结束)</param>
	/// <param name="strFileNameMask">文件名匹配字符(可以包含*和?)</param>
	public void Put( string strFolder, string strFileNameMask )
	{
		string[] strFiles = Directory.GetFiles( strFolder, strFileNameMask );
		foreach( string strFile in strFiles )
		{
			//strFile是完整的文件名(包含路径)
			Put( strFile );
		}
	}
	/// <summary>
	/// 上传一个文件
	/// </summary>
	/// <param name="strFileName">本地文件名</param>
	public void Put( string strFileName )
	{
		if( !bConnected )
		{
			Connect();
		}
		Socket socketData = CreateDataSocket();
		SendCommand( "STOR " + Path.GetFileName( strFileName ) );
		if( !( iReplyCode == 125 || iReplyCode == 150 ) )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
		FileStream input = new
		 FileStream( strFileName, FileMode.Open );
		int iBytes = 0;
		while( ( iBytes = input.Read( buffer, 0, buffer.Length ) ) > 0 )
		{
			socketData.Send( buffer, iBytes, 0 );
		}
		input.Close();
		if( socketData.Connected )
		{
			socketData.Close();
		}
		if( !( iReplyCode == 226 || iReplyCode == 250 ) )
		{
			ReadReply();
			if( !( iReplyCode == 226 || iReplyCode == 250 ) )
			{
				throw new IOException( strReply.Substring( 4 ) );
			}
		}
	}

	#endregion

	#region 内部函数
	/// <summary>
	/// 将一行应答字符串记录在strReply和strMsg
	/// 应答码记录在iReplyCode
	/// </summary>
	private void ReadReply()
	{
		strMsg = "";
		strReply = ReadLine();
		iReplyCode = Int32.Parse( strReply.Substring( 0, 3 ) );
	} /// <summary>
	/// 建立进行数据连接的socket
	/// </summary>
	/// <returns>数据连接socket</returns>
	private Socket CreateDataSocket()
	{
		SendCommand( "PASV" );
		if( iReplyCode != 227 )
		{
			throw new IOException( strReply.Substring( 4 ) );
		}
		int index1 = strReply.IndexOf( '(' );
		int index2 = strReply.IndexOf( ')' );
		string ipData =
		 strReply.Substring( index1 + 1, index2 - index1 - 1 );
		int[] parts = new int[6];
		int len = ipData.Length;
		int partCount = 0;
		string buf = "";
		for( int i = 0; i < len && partCount <= 6; i++ )
		{
			char ch = Char.Parse( ipData.Substring( i, 1 ) );
			if( Char.IsDigit( ch ) )
				buf += ch;
			else if( ch != ',' )
			{
				throw new IOException( "Malformed PASV strReply: " +
				 strReply );
			}
			if( ch == ',' || i + 1 == len )
			{
				try
				{
					parts[partCount++] = Int32.Parse( buf );
					buf = "";
				}
				catch( Exception )
				{
					throw new IOException( "Malformed PASV strReply: " +
					 strReply );
				}
			}
		}
		string ipAddress = parts[0] + "." + parts[1] + "." +
		 parts[2] + "." + parts[3];
		int port = ( parts[4] << 8 ) + parts[5];
		Socket s = new
		 Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
		IPEndPoint ep = new
		 IPEndPoint( IPAddress.Parse( ipAddress ), port );
		try
		{
			s.Connect( ep );
		}
		catch( Exception )
		{
			throw new IOException( "Can't connect to remote server" );
		}
		return s;
	}
	/// <summary>
	/// 关闭socket连接(用于登录以前)
	/// </summary>
	private void CloseSocketConnect()
	{
		if( socketControl != null )
		{
			socketControl.Close();
			socketControl = null;
		}
		bConnected = false;
	}

	/// <summary>
	/// 读取Socket返回的所有字符串
	/// </summary>
	/// <returns>包含应答码的字符串行</returns>
	private string ReadLine()
	{
		while( true )
		{
			int iBytes = socketControl.Receive( buffer, buffer.Length, 0 );
			strMsg += ASCII.GetString( buffer, 0, iBytes );
			if( iBytes < buffer.Length )
			{
				break;
			}
		}
		char[] seperator = { '\n' };
		string[] mess = strMsg.Split( seperator );
		if( strMsg.Length > 2 )
		{
			strMsg = mess[mess.Length - 2];
			//seperator[0]是10,换行符是由13和0组成的,分隔后10后面虽没有字符串,
			//但也会分配为空字符串给后面(也是最后一个)字符串数组,
			//所以最后一个mess是没用的空字符串
			//但为什么不直接取mess[0],因为只有最后一行字符串应答码与信息之间有空格
		}
		else
		{
			strMsg = mess[0];
		}
		if( !strMsg.Substring( 3, 1 ).Equals( " " ) )//返回字符串正确的是以应答码(如220开头,后面接一空格,再接问候字符串)
		{
			return ReadLine();
		}
		return strMsg;
	}
	/// <summary>
	/// 发送命令并获取应答码和最后一行应答字符串
	/// </summary>
	/// <param name="strCommand">命令</param>
	private void SendCommand( String strCommand )
	{
		//Byte[] cmdBytes =
		// Encoding.ASCII.GetBytes((strCommand + "\r\n").ToCharArray());
		byte[] cmdBytes = Encoding.GetEncoding( "gb2312" ).GetBytes( ( strCommand + "\r\n" ).ToCharArray() );
		socketControl.Send( cmdBytes, cmdBytes.Length, 0 );
		ReadReply();
	}
	#endregion

	#region 内部变量
	/// <summary>
	/// 服务器返回的应答信息(包含应答码)
	/// </summary>
	private string strMsg;
	/// <summary>
	/// 服务器返回的应答信息(包含应答码)
	/// </summary>
	private string strReply;
	/// <summary>
	/// 服务器返回的应答码
	/// </summary>
	private int iReplyCode;
	/// <summary>
	/// 进行控制连接的socket
	/// </summary>
	private Socket socketControl;
	/// <summary>
	/// 传输模式
	/// </summary>
	private TransferType trType;
	/// <summary>
	/// 接收和发送数据的缓冲区
	/// </summary>
	private static int BLOCK_SIZE = 512;
	Byte[] buffer = new Byte[BLOCK_SIZE];
	/// <summary>
	/// 编码方式
	/// </summary>
	Encoding ASCII = Encoding.ASCII;
	#endregion
}

SQLServer添加用户:错误15023:当前数据库中已存在用户或角色

在使用SQL Server 2000时,我们经常会遇到一个情况:需要把一台服务器上的数据库转移到另外一台服务器上。而转移完成后,需要给一个"登录"关联一个"用户"时,往往会 发生错误:

错误15023:当前数据库中已存在用户或角色

这个问题非常让人头疼,几经排常找到了原因与解决方法,因为这个问题与解决方法均比较复杂,所以把这个过程中的一些经验纪录下来与大家分享,希望能对大家以后的类似操作有所帮助,原因及解决办法如下:

首先介绍一下sql server中“登录”与“用户”的区别,“登录”用于用户身份验证,而数据库“用户”帐户用于数据库访问和权限验证。登录通过安全识别符 (SID) 与用户关联。将数据库恢复到其他服务器时,数据库中包含一组用户和权限,但可能没有相应的登录或者登录所关联的用户可能不是相同的用户。这种情况被称为存 在“孤立用户”。此时是不能通过新建登录或者是对同名登录授予对应数据库的“用户”权限来解决登录问题,因为SQL Server会报出“错误15023:当前数据库中已存在用户或角色”,为了解决这个问题,需要调用系统存储过程 sp_change_users_login,具体用法如下:

Use tablename

go

sp_change_users_login 'update_one', 'junstyle', 'junstyle'

其中tablename为存在孤立用户的数据库,update_one是存储过程的参数,表示只处理一个用户,前一个 junstyle是“用户”,后一个junstyle是“登录”,以上这个SQL表示将服务器登录“junstyle”与 tablename数据库用户“junstyle”重新连接起来。这样就可以正常使用数据库了。

gmail实验室里面的实验性功能

今天进到gmail的实验室看了一下,发现又加入了几个新功能。但也让我发现了一个很无语的功能。上图:

gmail真是贴心,有些人看到logo上的beta消失了,不安心,这个功能可以让他们安心下来。

玩转Google:Google搜索命令大全

使用谷歌搜索的时候有很多高级搜索代码,比如大家都很熟悉的site等命令,今天junstyle整理出目前所有的google高级搜索命令,供大家参考!

以下是目前所有的Google搜索命令语法,它不同于Google的帮助文档,因为这里介绍了几个Google不推荐使用的命令语法。大多数的 Google搜索命令语法有它特有的使用格式,希望大家能正确使用。

allintext: 当我们用allintext提交查询的时候,Google会限制搜索结果仅仅是在网页正文里边包含了我们所有查询关键词的网页。例[allintext: travel packing list],提交这个查询,Google仅仅会返回在一个网页包含了三个关键词”travel” “packing”和”list”的网页。

allinanchor: anchor是一处说明性的文字,它标注说明了这个链接可能跳转到其它的网页或跳转到当前网页的不同地方。当我们用allinanchor提交查询的时候,Google会限制搜索结果必须是那些在anchor文字里包含了我们所有查询关键词的网页。例[ allinanchor: best museums Sydney ] ,提交这个查询,Google仅仅会返回在网页anchor说明文字里边包含了关键词”best” “museums” 和”Sydney”的网面。

allintitle: 当我们用allintitle提交查询的时候,Google会限制搜索结果仅是那些在网页标题里边包含了我们所有查询关键词的网页。例[allintitle: detect plagiarism],提交这个查询,Google仅会返回在网页标题里边包含了”detect”和”plagiarism”这两个关键词的网页。 (全文 ...)

google终于被墙了

昨天google宣布转战香港,cn的域名全部跳转到google.com.hk,今天早上访问google,不能搜索了,只能打开页面,显示不了搜索结果。估计要不了几天google会被彻底的墙掉了!

完全跨域单点登录实现原理

单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

跨域单点登录分为以下两种:

1、跨子域单点登录。如 blog.a.com 和 news.a.com 这2个站点同属一个主域.a.com,实现跨子域单点登录很简单,可以利用cookie,设置Domain为".a.com'即可,这里就不再赘叙。

2、完成跨域单点登录。如 http://www.abc.com/ http://www.xyz.com/ 这2个站点之间实现共享一个身份验证系统,只需在一处地方登录,下面主要谈下这种方式的实现方法。

下面简单说说跨域单点登录实现原理:

当用户第一次访问web 应用系统1的时候,因为还没有登录,会被引导到认证中心进行登录;根据用户提供的登录信息,认证系统进行身份效验,如果通过效验,返回给用户一个认证的凭据;用户再访问别的web应用的时候就会将这个Token带上,作为自己认证的凭据,应用系统接受到请求之后会把Token送到认证中心进行效验,检查Token的合法性。如果通过效验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。所有应用系统共享一个身份认证系统。认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志,返还给用户。另外,认证系统还应该对Token进行效验,判断其有效性。 所有应用系统能够识别和提取Token信息要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对Token进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。比如说,我现在有3个分站点和1个认证中心(总站)。当用户访问分站点的时候,分站点会发Token到验证中心进行验证。验证中心判断用户是否已经登录。如果未登录,则返回到验证中心登录入口进行登录,否之则返回Token验证到分站点,直接进入分站点。