自打老婆怀孕的时候,我就琢磨着给未曾谋面的宝宝建一个网站,记录他的成长,无奈乱七八糟的事情太多,网站的事情一拖再拖。7月30日宝宝出生了,亲朋好友都希望看到他的近照,结果发邮件发到手疼,这才想起建站的事情不能在拖下去了,把宝宝的照片、我们的心情都记录下来,大家访问网站就行了,也算是对宝宝有个交代。说到网站,免费的空间限制太多,我个人不太喜欢,估计宝宝也不喜欢(随我,呵呵),咬咬牙,租了个空间,申请了一个域名,以后这里就是宝宝在网上的家了。

最后这句话是对俺家宝宝说的:糖糖,人生的路还很长,爸爸妈妈祝愿你健康快乐的成长!!

posted @ 2008-08-29 09:30 BAsil 阅读(108) | 评论 (0)编辑

上一节我们讲到了客户端发送Login命令后,服务器返回欢迎信息,完成了一个简单的数据传输。这一节我们来完成游戏大厅的基本功能,我们首先思考一下游戏大厅的基本功能:

1 提供可供对弈的游戏桌,游戏大厅可供多桌玩家同时游戏,为了考虑游戏大厅服务器的负载能力,应该设置一个人数的上限和桌数的上限。实际上前面提到的功能抽象出来就是一些数据的状态集合。

2 当玩家登入大厅,应该直观的显示当前大厅的就座情况,方便玩家选择。此处应该考虑大厅的直观显示。

3 当玩家选择某一位置就坐,游戏大厅的相应状态数据应发生更改,任何玩家都能看到大厅的就座情况的变化,方便做出选择。比如a选择坐在第一桌的黑方位置,则b应该看到该位置不可落坐,只能选择其他位置就坐。

尽量从面向对象的角度考虑,我们应当把游戏大厅,游戏桌,玩家看做对象。建议大家使用面向对象的方法去思考,个人感觉服务器客户端通信的网络程序主要涉及通信协议(就是我们前面提到的命令,参数1,参数2等等)的分析,设计不好的话到最后你会发现逻辑复杂到难以控制的程度。

下面我们分别看一下这几个对象(有删减,具体请看源代码)

 

 1 //游戏大厅
 2 public FormServer
 3 {
 4     //basilwang 2008-09-06 
 5     //new to this version myGame2
 6     //游戏桌集合
 7     private GameTable[] gameTable;
 8     //玩家集合
 9     List<User> userList = new List<User>();
10     //游戏桌上限
11     private int maxTables;
12     //玩家上限
13     private int maxUsers;
14     //上一节列出的帮助类
15     private Service service;
16 }
17 //游戏桌
18 class GameTable
19 {
20         private const int None = -1//无棋子
21         private const int Black = 0//黑白棋子
22         private const int White = 1//白色棋子
23         private int[,] grid = new int[88];   //8*8的方格
24         public Player[] gamePlayer;
25         public GameTable()
26         {
27             gamePlayer = new Player[2];
28             gamePlayer[0= new Player();
29             gamePlayer[1= new Player();
30         }
31         
32  }
33 class Player
34 {
35         //是否开始
36     public bool started;
37     //己方棋子个数
38     public int grade;
39     //是否落座
40     public bool someone;
41     public Player()
42     {
43         someone = false;
44         started = false;
45         grade = 0;
46     }
47 }

 

当服务器程序启动的时候,需要初始化游戏大厅的数据;而当客户端登录到游戏大厅后,按照前面列出的逻辑,我们需要把游戏大厅的状态在客户端直观的显示出来。那怎么才能得到游戏大厅的状态数据呢?没错,也是利用的客户端服务器之间的通讯。

当客户端向服务器发送Login命令后,服务器处理完毕后,返回Tables命令返回状态数据

 

 1 private void ReceiveData(object obj)
 2 {
 3     //省略接受客户端协议代码
 4     
 5     //拆分接受到的协议      格式:  命令,参数1,参数2 .
 6     string[] splitString = receiveString.Split(',');
 7     string sendString = "";
 8     switch (splitString[0])
 9     {
10     case "Login":
11         user.userName = string.Format("[{0}--{1}]", splitString[1], client.Client.RemoteEndPoint);
12         //向客户端发送协议   
13         //格式 : Tables,参数1
14         //参数1为游戏大厅的游戏桌就座情况
15         sendString = "Tables," + this.GetOnlineString();        
16         service.SendToOne(user, sendString);        
17         break;
18     default:
19         break;
20     }
21  }
22  //返回如0100010101的字符串  奇数位表示游戏桌黑方的就座情况,偶数位相反,游戏桌按序号排列连接
23  private string GetOnlineString()
24 {
25     string str = "";
26     for (int i = 0; i < gameTable.Length; i++)
27     {
28     for (int j = 0; j < 2; j++)
29     {
30         str += gameTable[i].gamePlayer[j].someone == true ? "1" : "0";
31     }
32     }
33     return str;
34 }

 

客户端接受到Tables命令协议进行分析,直观显示游戏大厅的就座情况,这里为了简单,采用了动态生成若干组checkbox控件添加到Panel的方法,比较简单但能够说明问题。checkbox选中表明已有玩家就座,如果未选中表明可以在此处落座。

这部分代码就不列出来了,可以看一下原程序。

如果玩家选择在某一位置落座,将出发CheckBox的CheckedChanged事件,并向服务器发送SitDown命令

 

 1 void checkBox_CheckedChanged(object sender, EventArgs e)
 2 {
 3             CheckBox checkbox = (CheckBox)sender;
 4             if (checkbox.Checked)
 5             {
 6             //动态生成的CheckBox命名规则为checkXXXXYYYY, 第5-8位为桌号,不足0补齐;第9-12位为黑方或白方,不足0补齐
 7                 int i = int.Parse(checkbox.Name.Substring(54));
 8                 int j = int.Parse(checkbox.Name.Substring(94));
 9                 side = j;
10         //格式 SitDown,参数1,参数2
11         //参数1 桌号
12         //参数2 黑方或白方
13                 service.SendToServer(string.Format("SitDown,{0},{1}", i, j));
14             }
15  }

 

服务器分析SitDown命令

 1 private void ReceiveData(object obj)
 2 {
 3         //省略接受客户端协议代码
 4     
 5         //拆分接受到的协议      格式:  命令,参数1,参数2 .
 6         string[] splitString = receiveString.Split(',');
 7     string sendString = "";
 8     int tableIndex = -1;  //桌号
 9     int side = -1;        //座位号
10     int anotherSide = -1//对方座位号
11 
12     switch (splitString[0])
13     {
14         case "Login":
15         //省略部分
16         break;
17         case "SitDown":
18         tableIndex = int.Parse(splitString[1]);
19         side = int.Parse(splitString[2]);
20         gameTable[tableIndex].gamePlayer[side].user = user;
21         gameTable[tableIndex].gamePlayer[side].someone = true;
22         service.SetListBox(string.Format(
23             "{0}在第{1}桌第{2}座入座", user.userName, tableIndex + 1, side + 1));
24         //得到对家座位号
25         anotherSide = (side + 1% 2;
26         //判断对方是否有人
27         if (gameTable[tableIndex].gamePlayer[anotherSide].someone)
28         {
29             //先告诉该用户对家已经入座
30             //发送格式:SitDown,座位号,用户名
31             sendString = string.Format("SitDown,{0},{1}", anotherSide,
32             gameTable[tableIndex].gamePlayer[anotherSide].user.userName);
33             service.SendToOne(user, sendString);
34         }
35         //同时告诉两个用户该用户入座(也可能对方无人)
36         //发送格式:SitDown,座位号,用户名
37         sendString = string.Format("SitDown,{0},{1}", side, user.userName);
38         service.SendToBoth(gameTable[tableIndex], sendString);
39         //重新将游戏室各桌情况发送给所有用户
40         service.SendToAll(userList, "Tables," + this.GetOnlineString());
41         break;
42         default:
43         break;
44     }
45  }
46 
到这里,我们把游戏大厅的简单逻辑都处理了,下一节将介绍客户端棋盘的呈现。
源代码下载
posted @ 2008-10-11 15:29 BAsil 阅读(661) | 评论 (4)编辑

上一节给大家演示了建立连接的关键代码,连接建立好后,就可以进行数据传输了。数据传输包含从服务器端到客户端和从客户端到服务器端,两者差别不大。

数据的传输,TcpClient的GetNetworkStream是关键,通过它我们可以得到NetworkStream网络流,客户端和服务器主要的工作就是对其读出和写入。关于如何构造稳定且性能好的网络应用,如何进行复杂的封包和解包,这里我们不考虑,我们使用StreamReader和StreamWriter来封装NetStream,读取和写入的代码如下

 1 TcpClient client;
 2 StreamReader sr;
 3 StreamWriter sw;
 4 this.userName = "";
 5 NetworkStream netStream = client.GetStream();
 6 sr = new StreamReader(netStream, System.Text.Encoding.UTF8);
 7 sw = new StreamWriter(netStream, System.Text.Encoding.UTF8);
 8 //读取数据
 9 string output=sr.ReadLine();
10 //写入数据
11 string input="";
12 sw.WriteLine();
13 sw.Flush();

客户端和服务器的通信一般是由客户端向服务器端发请求,服务器端接受请求并处理,再将结果重新发回客户端

发送请求和应答的协议格式可以自己定义,游戏中使用的协议格式为  命令,参数1,参数2......

先看一下客户端请求登录

 1 service.SendToServer("Login," + textBoxName.Text.Trim());
 2 class Service
 3 {
 4 ListBox listbox;
 5 StreamWriter sw;
 6 public Service(ListBox listbox, StreamWriter sw)
 7 {
 8     this.listbox = listbox;
 9     this.sw = sw;
10 }
11 public void SendToServer(string str)
12 {
13     sw.WriteLine(str);
14     sw.Flush();
15 }
16 }


服务器接受请求并处理,一般来说是根据不同的请求创建不同的线程进行处理,见本节后半部分
 1 private void ReceiveData(object obj)
 2 {
 3     //User类封装了TcpClient,StreamReader和StreamWriter
 4     User user = (User)obj;
 5     TcpClient client = user.client;
 6     string receiveString = null;
 7     receiveString = sr.ReadLine();
 8     service.SetListBox(string.Format("来自{0}:{1}", user.userName, receiveString));
 9     string[] splitString = receiveString.Split(',');
10     switch (splitString[0])
11     {
12         case "Login":
13             user.userName = string.Format("[{0}--{1}]", splitString[1], client.Client.RemoteEndPoint);
14             service.SendToOne(user, "Welcome");
15             break;
16         default:
17             break;
18     }
19 }
20 


 1 class Service
 2 {
 3         private ListBox listbox;
 4         private delegate void SetListBoxCallback(string str);
 5         private SetListBoxCallback setListBoxCallback;
 6         public Service(ListBox listbox)
 7         {
 8             this.listbox = listbox;
 9             setListBoxCallback = new SetListBoxCallback(SetListBox);
10         }
11         public void SetListBox(string str)
12         {
13             if (listbox.InvokeRequired)
14             {
15                 listbox.Invoke(setListBoxCallback, str);
16             }
17             else
18             {
19                 listbox.Items.Add(str);
20                 listbox.SelectedIndex = listbox.Items.Count - 1;
21                 listbox.ClearSelected();
22             }
23         }
24         public void SendToOne(User user, string str)
25         {
26             try
27             {
28                 user.sw.WriteLine(str);
29                 user.sw.Flush();
30                 SetListBox(string.Format("向{0}发送{1}", user.userName, str));
31             }
32             catch
33             {
34                 SetListBox(string.Format("向{0}发送信息失败", user.userName));
35             }
36         }
37  }
服务器负责监听并根据不同的用户启动不同的线程
 1 IPAddress localAddress;
 2 int port = 51888;
 3 TcpListener myListener;
 4 Service service = new Service(listbox);
 5 IPAddress[] addrIP = Dns.GetHostAddresses(Dns.GetHostName());
 6 localAddress = addrIP[0];
 7 myListener = new TcpListener(localAddress, port);
 8 myListener.Start();
 9 service.SetListBox(string.Format("开始在{0}:{1}监听客户连接", localAddress, port));
10 ThreadStart ts = new ThreadStart(ListenClientConnect);
11 Thread myThread = new Thread(ts);
12 myThread.Start();


ListenClientConnect方法
 1 while (true)
 2 {
 3     TcpClient newClient = null;
 4     try
 5     {
 6        newClient = myListener.AcceptTcpClient();
 7     }
 8     catch
 9     {
10        break;
11     }
12     User user = new User(newClient);
13     userList.Add(user);
14     service.SetListBox(string.Format("{0}进入", newClient.Client.RemoteEndPoint));
15     service.SetListBox(string.Format("当前连接用户数:{0}",userList.Count));
16     //basilwang 2008-09-06 to receive data from client
17     ParameterizedThreadStart pts = new ParameterizedThreadStart(ReceiveData);
18     Thread threadReceive = new Thread(pts);
19     threadReceive.Start(user);
20 }

此处的启用了ParameterizedThreadStart 线程处理带参数的ReceiveData方法,主要是防止同客户端传输数据的过程出现的异常不能正确处理导致线程崩溃,每一个客户端分配一个线程可以保证客户端各自的独立性;同时ParameterizedThreadStart允许传递参数。

至此我们完成了一个简单的服务器客户端应答,希望能对初学者有所帮助。

 文件下载

posted @ 2008-09-24 14:48 BAsil 阅读(1796) | 评论 (7)编辑

利用TCP开发网络应用程序,可以采用同步或者异步的方式,这个游戏采用的是同步的工作方式,比较简单,系列教程也主要介绍同步的工作方式。

网络通信的前提就是客户端和服务器端的通信,在服务器端,程序需要不断的监听客户端是否有连接请求,已保证多个客户端的连接,服务器通过套接字识别客户端;而客户端只需要指定哪个服务器即可。一旦双方建立连接并创建了对应的套接字,就可以互相传输数据了。客户端和服务器端发送和接受数据的方法都是一样的,区别仅是方向不同。

在同步TCP网络应用程序中,发送、接受和监听语句均采用阻塞方式工作,一般有如下步骤:

(1)创建一个包含所采用的网络类型、数据传输类型和协议类型的本地套接字对象,并将其余服务器的IP地址和端口号绑定。可通过Socket类或者TcpListener类完成。

(2)在指定的端口进行监听,以便接受客户端的连接请求。

(3)一旦接受了客户端的连接请求,就根据客户端发送的连接信息创建与该客户端对应的Socket对象或者TcpClient对象。

(4)根据创建的Socket对象或者TcpClient对象,分别与每个连接的客户进行数据传输。

(5)根据传送信息的情况确定是否关闭与对方的连接。

本文的目的是完成前三步,即创建服务器和客户端的连接,服务器将根据对应客户端建立的TcpClient对象得到客户端的信息

服务器端部分代码


 

IPAddress localAddress;
            
int port = 51888;
            TcpListener myListener;
            Service service 
= new Service(listbox);
            IPAddress[] addrIP 
= Dns.GetHostAddresses(Dns.GetHostName());
            localAddress 
= addrIP[0];
            myListener 
= new TcpListener(localAddress, port);
            myListener.Start();
            service.SetListBox(
string.Format("开始在{0}:{1}监听客户连接", localAddress, port));
            ThreadStart ts 
= new ThreadStart(ListenClientConnect);
            Thread myThread 
= new Thread(ts);
            myThread.Start();

我们可以看到建立了一个TcpListener,并且调用了TcpListener.Start();接着启动了一个线程,循环的接受客户端的请求并建立对应的TcpClient对象,看一下ListenClientConnect方法

while (true)
            {
                TcpClient newClient 
= null;
                
try
                {
                    newClient 
= myListener.AcceptTcpClient();
                }
                
catch
                {
                    
break;
                }
                User user 
= new User(newClient);
                userList.Add(user);
                service.SetListBox(
string.Format("{0}进入", newClient.Client.RemoteEndPoint));
                service.SetListBox(
string.Format("当前连接用户数:{0}",userList.Count));

            }

 

其中的while(true)用法看起来比较奇怪,但没什么问题,保证应答多个客户端的请求。

客户端的代码

 

TcpClient client = null;
             
try
            {
                client 
= new TcpClient(Dns.GetHostName(), 51888);

            }
            
catch
            {
                MessageBox.Show(
"与服务器连接失败""", MessageBoxButtons.OK, MessageBoxIcon.Information);
                
return;
            }

 

客户端代码很简单,因为不需要传输数据,这样当服务器端监听到请求后,利用建立的TcpClient对象的到该客户端的信息,通过调用service.SetListBox打印到ListBox中(在游戏中每个窗口都有一个ListBox,使大家看到服务器和客户端交互的一些信息,方便调试)

Service代码

 

class Service
      {
        
private ListBox listbox;
        
private delegate void SetListBoxCallback(string str);
        
private SetListBoxCallback setListBoxCallback;
        
public Service(ListBox listbox)
        {
            
this.listbox = listbox;
            setListBoxCallback 
= new SetListBoxCallback(SetListBox);
        }
        
public void SetListBox(string str)
        {
            
if (listbox.InvokeRequired)
            {
                listbox.Invoke(setListBoxCallback, str);
            }
            
else
            {
                listbox.Items.Add(str);
                listbox.SelectedIndex 
= listbox.Items.Count - 1;
                listbox.ClearSelected();
            }
        }
      }

 

大家一定会对SetListBox的写法比较奇怪,这里实际上是多线程中调用winform 的方法,来看网上的一段话

每一个从Control类中派生出来的WinForm类(包括Control类)都是依靠底层Windows消息和一个消息泵循环(message pump loop)来执行的。消息循环都必须有一个相对应的线程。由于最初消息循环的缘故,只有创建该form的线程才能调用其事件处理方法。

换句话说,如果你在你自己的线程中调用这些方法,则它们会在该线程中处理事件,而不是在创建该form的主线程中进行处理,这时就需要通过Control.Invoke方法返回窗体主线程执行相关操作。

同时,由于程序中大量使用了SetListBox方法,因此将其修改为自动判断是否需要Invoke,而使用该方法时不需要关心此细节。Invoke的第一个参数是一个SetListBoxCallback委托,此处也可以用匿名函数实现,代码更简洁。

以上是本文的几个需要注意的地方,本例的内容没有涉及数据传输,比较简单,但却非常基础,希望能够对大家有所帮助。

示例代码下载

posted @ 2008-09-21 13:54 BAsil 阅读(1312) | 评论 (4)编辑

看过网上很多的类似系列教程(博客园包包版网络大厅的+桥牌系统),写的很深入,感觉比较复杂,初学者不宜上手。我是在学习WCF的时候,发现自己对底层的传输原理都没有搞明白,于是又回头学习网络传输的一些知识,自己写了一个简单的网络游戏黑白棋,因此也想把学习的一个过程记录下来和初学者们一块交流。我的只是小儿科,还请网友多多包涵,高手们也不要吝惜你们的砖头。

第一次写系列教程,心里没底,本来自己水平就一般,却要完成这个命题作文,难煞我了。好歹程序写的差不多了(不过还没有最终完成),这里先把完成的部分分章介绍一下,程序我在慢慢补。

本系列源代码TCP网络传输参考《C#网络应用高级编程》人民邮电出版社,马骏编,黑白棋游戏部分为本人(http://www.basilwang.net/)编写。

写这个游戏只是为了我个人理解基于TCP网络游戏编程的基本思路,算法部分写的比较乱,没有优化,不过我都做了注释,方便大家阅读。

先说一下黑白棋,又叫反棋(Reversi)、奥赛罗棋(Othello),苹果棋,翻转棋。黑白棋在西方和日本很流行。但是这个游戏在中国目前还不够推广,下棋的水平还不高。黑白棋规则很简单,只要肯花点脑筋,新手也能玩得很好。因为棋盘小,下一局棋所花的时间也不多。对于黑白棋,有一种说法是:只需要几分钟学会它,却需要一生的时间去精通它(a minute to learn, a lifetime to master)。

早些年文曲星里面带的游戏就喜欢它了,我这个人脑子不开窍,很多游戏连电脑AI都打不过,好容易整了个能打败AI的游戏,还不往死里玩,嘿嘿。不过我写的这个黑白棋只是供网络对战使用,不涉及AI算法部分(我还不知道怎么做呢),斗胆发到网上,权作抛砖引玉,废话少说,进入正题。

系列的介绍打算以我学习TCP网络编程的过程为顺序,每一篇教程都能够完成功能,附上的源代码能够独立运行,我会把代码中碰到的相关知识做相应的介绍,使初学者能有直观的认识。

游戏完成的部分

1 网络大厅,可自定义桌数,人数 (完全参照《C#网络应用高级编程》,马老师应该不介意吧)

2 黑白棋游戏客户端

2.1 吃子

2.2  奇偶数统计

2.3 轮流下子

2.4 终局胜负提示

未完成部分

1 黑白棋游戏客户端

1.1 下子时间限制

1.2 甲方无子可下时,程序判定由乙方下子,甲方丢掉一次机会

1.3 判定任一方无子可下的程序(思路:需要计算盘中空子的列表,然后调用已完成的吃子程序,看能否下子,但并不真正下子)

源程序写的很简单,界面比较简陋,只是为了帮助大家更好的理解基于TCP的网络游戏传输的基本原理,还请大家多包涵。

posted @ 2008-09-21 13:47 BAsil 阅读(379) | 评论 (0)编辑

windows xp以后的系统自带了还原功能,可以创建系统的还原点,当系统出问题时可以利用该功能恢复到以前创建的还原点,同时由于还原点的创建采用了增量备份,每次还原点的创建的比较快,占用的空间也不是太大。

偏偏我不太喜欢使用这个还原功能,从来也记不得创建还原点,结果今天让我吃尽了苦头。今早电脑进了木马,任务管理器出现了explore.exe进程(不是explorer.exe),很多exe文件都无法执行,查了一下确认是中了灰鸽子。因为手头上有工作,我偷了个懒,想直接用自动还原恢复到以前的状态。检查还原点,手工还原点没有(我这个人最懒了),只有一个9月3日系统创建的还原点,就用这个了呵呵,我暗自窃喜:真是懒人自有天相。可是回复完了以后傻眼了,启动后报System32\Config\System文件损坏,无法启动,我的神阿!*%?!~~%^@@$(&@

看到这里,可能有童鞋会说,这个问题好说阿,用启动盘进入修复功能提示符下,把System32\repair下的相应文件拷贝过去就可以。这个方式可以恢复到系统安装的初始状态,而且大多数软件都可以运行,包括office。可是我很不幸,我是一个MS程序员,作为一个MS程序员的基本一条就是要和诡异的VS开发工具打交道,我的vs2005,vs2008,sql server 2005在这种恢复模式下统统不起作用,卸载再安装的话我的双休就拜拜了(vs2005 sp1的补丁安装就达到恐怖的4小时)。

好在我还有一个ghost备份,是2008年1月1日自己做的,7G,里面除了没有安装vs2008及其补丁剩下的基本都全了,接下来,恢复ghost,安装vs2008,打sp1补丁,2个小时ok。

在这里需要提醒大家的是

1.最好在完全重装系统后来一个完全的ghost,不过要有心理准备这个备份比较大;

2.另外要定时的做一些手工的还原点。

3.系统创建的还原点可能有问题,我就是吃了这个亏。

4.经常备份以下windows\system32\config下面的文件,绝对没有坏处

posted @ 2008-09-05 22:07 BAsil 阅读(24) | 评论 (0)编辑

网站已经上线,当然需要定时备份了。可是当我打开phpMyAdmin连接的mysql数据表准备备份的时候,却发现全是乱码,咨询了一下空间提供商,被告知数据库的编码就是这样设置的,没法改。可是我用phpMyAdmin备份到本地打开也一样是乱码,在网上找到了一个使用phpMyAdmin备份的解决方案,可是不起作用。后来试了一下WordPress DataBase Backup,发现问题解决。而且DB Backup支持周期Email数据库备份,这个功能太方便了。

另外需要提醒大家的是,备份的文件需要用UTF8的编码形式打开,本机的数据库导入用phpMyAdmin没问题,可是我用第三方的Mysql-Front却报错,至此问题解决。

后记

mysql的编码问题确实比较头疼,以上是我在wordpress 2.6版本的解决方案,建议大家碰到乱码的时候,各种方法多试几次,应该能解决。

posted @ 2008-09-01 22:09 BAsil 阅读(35) | 评论 (0)编辑

上次说到给俺们家糖糖建了一个网站,这次主要说一下建站的技术细节吧,希望对感兴趣的朋友有所帮助,大牛们请直接跳过。

建站的几个步骤如下

1 域名申请

2 空间选择

3 上传自己的程序代码并且建立数据库

4 广告申请

5 Alexa排名申请

6 备案

1 域名申请

因为我是第一次申请域名,我把碰到的问题和需要注意的事项一并记录一下,希望能对朋友们有所帮助。刚开始我想申请cn的顶级域名,网上给出的价格很便宜,一般不超过10块钱一年。可是当我打算下单购买的时候才发现cn域名的申请对个人来说几乎是不可操作,因为要求域名申请的单位以及一系列的手续,当然这也比较符合中国国情,只是不知奥运后限制会不会放宽。最后我选择了比较贵的国际域名,edong网给出的价格是68元/年,国际域名的注册没有太多的限制,很快就注册下来了。打款的时候也碰上了一点麻烦,我首先在开通了工行的网上银行,不过支付的时候总是报超出客户最大支付限额的错误,后来才知道需要到银行取办理口令卡开通才能完成,只好放弃(太不人性了,我半夜办理业务让我怎么去银行阿)。相比较招商银行就做的很不错,简单的注册就可以使用了。

2 空间选择

网络上建站有两种方式,租用虚拟主机或者服务器托管。对于个人站长,租用虚拟主机是相对经济的解决方案。我的网站http://www.basilwang.net就是租用了了edong网的虚拟主机,采用了它的k300的linux/unix解决方案,支持php+mysql,空间费用300元/年,当然也有支持.net的解决方案,而且比较便宜200元/年。这里为什么没有选用.net的解决方案,我做一下解释:就.net和php而言,我认为个人网站还是选用php更加灵活一点,而且网上php开源的东西一大把,有的已经非常稳定了。另外建议大家选择口碑比较不错的空间商,不过这方面我也没有什么经验,就不误导大家了。

3上传自己的程序代码并且建立数据库

代码方面我选用了wordpress,我之前没有任何php的编程经验,不过为了建站还是花了3天看了一部分原代码,了解了wordpress关于theme、插件还有widget等方面运行机制。不过这里提醒大家的是,wordpress已经完全能够做到一键式的安装,非常方便,没有程序开发经验的朋友也可以很快上手。当然我在阅读php代码的时候,还是有种回到asp编程的年代。

程序代码可以用上传软件如cuteftp等用提供商给的帐号上传。关于mysql,你可以建库并设置访问密码,通过phpMyAdmin进行访问,不支持类似mysql-front等第三方工具的访问。这里还要注意的是,网络提供商可能提供的是英文的编码方案,因此通过phpMyAdmin看到的中文都是乱码,不过显示都是没有问题的,只是备份还原的时候需要注意,网上有解决方案,也可以参考我接下来的文章。

4 广告申请

在写这篇文章的时候,申请的Google Adsense又一次被拒,原因是“确保您的网站已经注册6个月以上时间;”,只能等半年后再说了。相反alimama的申请到是很方便,不过希望马云所说的“电子商务的冬天”会不会影响到造福放大个人站长的alimama。最后提醒大家,单靠广告点击的收入保证网站的自负盈亏比较困难,请打算上贼船的朋友三思。我做网站的时候也考虑了这点,全当是我对儿子的投资了,呵呵,儿子不要有压力阿。

5 alexa排名

这个东西能给使你对你的网站有一个比较直观的认识,建议把网站提交到alexa以进行统计

6 备案

现在的网站都需要备案,当然这个备案目前是免费的,需要到工业和信息化部网站进行注册(原来的信息化部网站),也可以由你的空间提供商代为你注册。我个人感觉还是自己操作吧,熟悉一下流程,没有坏处。

更多内容,请阅读发表在[http://www.basilwang.net]的文章

posted @ 2008-08-29 11:46 BAsil 阅读(291) | 评论 (2)编辑

自打老婆怀孕的时候,我就琢磨着给未曾谋面的宝宝建一个网站,记录他的成长,无奈乱七八糟的事情太多,网站的事情一拖再拖。7月30日宝宝出生了,亲朋好友都希望看到他的近照,结果发邮件发到手疼,这才想起建站的事情不能在拖下去了,把宝宝的照片、我们的心情都记录下来,大家访问网站就行了,也算是对宝宝有个交代。说到网站,免费的空间限制太多,我个人不太喜欢,估计宝宝也不喜欢(随我,呵呵),咬咬牙,租了个空间,申请了一个域名,以后这里就是宝宝在网上的家了。

最后这句话是对俺家宝宝说的:糖糖,人生的路还很长,爸爸妈妈祝愿你健康快乐的成长!!

posted @ 2008-08-29 09:30 BAsil 阅读(108) | 评论 (0)编辑

tt

更多图片请访问糖糖的个人空间

http://www.basilwang.net

posted @ 2008-08-28 12:04 BAsil 阅读(36) | 评论 (6)编辑

我的儿子糖糖于2008年7月30日(农历6月28)生于山东大学齐鲁医院,体重7斤7两,记录一下。

posted @ 2008-08-28 12:02 BAsil 阅读(36) | 评论 (4)编辑
     摘要:

前几天,在园子里看了一个图片幻灯效果,很不错,不过今天想找却找不到了。这两天想把网站的图片幻灯效果给换了,原来的虽然做成了web控件,不过控件里硬编码了javascript和css,甚至还有document.write这样的输出,感觉很是不爽。偶然发现了一篇翻译文章如何使用 JavaScript 创建可维护的幻灯片效果,感觉原作者在构建可分离的javascript和css上颇有心得,不过这里和我想要的效果还有些差异,参照大部分国内网站的实现方式,应该能够实现定时的播放,同时在右下角还应该有一个序列号的指示,就像新浪网的首页那样,既然作者帮我们作了大部分的分离工作,剩下的这部分自己实现,也十分简单。

  阅读全文
posted @ 2008-07-04 13:16 BAsil 阅读(2156) | 评论 (12)编辑
我他妈的要是再看中国队的球我就是王八羔子!
posted @ 2008-06-07 21:50 BAsil 阅读(242) | 评论 (5)编辑
     摘要: 画了两张UML图,帮助理解asp.net 2.0的页面即时编译,建议结合Reflector查看
  阅读全文
posted @ 2008-06-04 21:46 BAsil 阅读(2810) | 评论 (14)编辑
最近在看《ASP.NET 2.0服务器控件与组件开发高级编程》,其中有一个以前没有注意的细节,思索了好久,还是没有答案,还请各位帮忙指点一下。
服务器控件,注意CardholderNameText是存到ViewState当中的
    public class CreditCardForm : Control
    
{
        
public CreditCardForm()
        
{
        }

        
public string CardholderNameText
        
{
            
get return ViewState["CardholderNameText"!= null ? (string)ViewState["CardholderNameText"] : "CardholderName"; }