最近首部《网络成瘾诊断标准》通过专家论证,玩游戏成瘾被正式纳入精神病诊断范畴。突然间中国又多了数百万的精神病患者,也庆幸自己在被判为精神病前脱离了网游。

不过,在我看来,“玩游戏上瘾的就是精神病”这个判定简直就是胡扯。首先,不能说有瘾就是病。如果说上瘾就是病的话,那些购物上瘾、看书上瘾、搞学问上瘾的人都有病了。所以,做人一定要遵守社会的主流判别标准,否则就有可能被视为“病人”。其次,网瘾只是对网络游戏的一种沉浸而无法脱离,是一种精神上的依赖。玩网游是人生观和价值观在虚拟世界和真实世界间的转移和转换,当一个人认为他在虚拟世界中更有价值更能被“社会”认可的时候他就不愿意离开。而在虚拟世界实现个人价值是非常容易的,无所谓家庭环境,无所谓个人修养,无所谓教育背景,无所谓机遇命运,只要付出了时间就会有回报。这种简单的个人价值实现模式使得网游玩家不愿面对这真实、残酷、不公平的世界,选择网络就是最好的逃避和解脱。所以网瘾只是正常人的大脑在有意识或潜意识思维后的决定和习惯,并不是大脑或神经系统的损坏,这是与神经病的根本区别。从我的个人经历来说,脱离网瘾还是有可能的,而治愈精神病并不容易。

在新时代新背景下,把网瘾简单的归类到精神病是不负责任的。治疗网瘾并不需要任何物理药物,只需要珍爱生命、远离网络、转移目标、心理疏导、亲人信任,网瘾是完全可以戒掉的。

今天民主党候选人奥巴马毫无悬念的赢得了如同超女选秀一般的美国第56届总统竞选。奥巴马的胜利中有三个关键字引起了我的注意:黑人,非洲裔,网络。

一、黑人
奥巴马是美国历史上首位黑人总统,这即是美国黑人的胜利,也是美国人权与民主的体现。虽然“美国大选是有钱人的游戏”这个观念从小就植根在心中,但越来越发现这种游戏也是全民参与的。街头小巷到处都是拉票的海报,真有一种超女选秀般的全民参与力。这种金钱的游戏其实也是美国经济的强心针,无数的公司为其服务,从中获益。美国大选势必带动了餐饮、服务、汽车、金融、IT、医疗等产业链的发展。尤其在金融危机的今天,这种全民参与的政治经济活动是一股内在的驱动力。

二、非洲裔
英雄不问出处,这个也许比黑人更有看头。从华裔州长到非洲裔总统,体现了美国文化的包容和自由。我想除了美国这样年轻的国家外,很少有能接受这种并非根正苗红的人当选总统吧。美国大选是有钱人的游戏,不过可能每个人都有成为有钱人的可能性。奥巴马就是一个从社会底层逐步取得成就的议员最终成为了美国总统。而奥巴马的竞选资金也是靠个人魅力筹集,虽并未动用公共竞选资金,但也选超对手。据说,奥巴马的家乡肯尼亚为了庆祝奥巴马获胜全国放假一天。

三、网络
这届美国大选对于网络的依赖和影响达到了前所未有的高度和深度。尤其是奥巴马的网络营销更尤为成功,凭借Youtube, flickr, facebook, Myspace, twitter, Digg等知名的web2.0网站奥巴马的新闻、照片、生活可以了如指掌,就如同SNS上的朋友一样,奥巴马通过网络拉近了网民的距离。奥巴马就在身边,奥巴马无处不在。对比奥巴马麦凯恩的官方网站可以发现,奥巴马网站的配色非常讲究,用户体验也更胜一筹,想必一定出自行家里手。Logo标志设计上奥巴马也是当下最流行的3D水晶风格,非常精美,辨识度很高;而麦凯恩就没有明确的Logo和标识,难以辨识。在品牌营销上,奥巴马大获全胜,甚至可以以此品牌注册一家礼品公司,粉丝们一定会蜂拥而至。在众多细节上奥巴马都非常出色,给我的感觉是他在用心去战斗,正是细节决定成败!

如果我也有投票的资格,我一定也会选奥巴马。并非因为政治或思想上的认同,而是奥巴马的信息更容易获得。

TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程。然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实现不是一天两天的功夫,所幸的是在.net framework环境下,我们不必要去追究TCP协议底层的实现,一样可以很方便的编写出基于TCP协议进行网络通讯的程序。

要进行基于TCP协议的网络通讯,首先必须建立同远程主机的连接,连接地址通常包括两部分——主机名和端口,如www.yesky.com:80中,www.yesky.com就是主机名,80指主机的80端口,当然,主机名也可以用IP地址代替。当连接建立之后,就可以使用这个连接去发送和接收数据包,TCP协议的作用就是保证这些数据包能到达终点并且能按照正确的顺序组装起来。

在.net framework的类库(Class Library)中,提供了两个用于TCP网络通讯的类,分别是TcpClient和TcpListener。由其英文意义显而易见,TcpClient类是基于TCP协议的客户端类,而TcpListener是服务器端,监听(Listen)客户端传来的连接请求。TcpClient类通过TCP协议与服务器进行通讯并获取信息,它的内部封装了一个Socket类的实例,这个Socket对象被用来使用TCP协议向服务器请求和获取数据。因为与远程主机的交互是以数据流的形式出现的,所以传输的数据可以使用.net framework中流处理技术读写。在我们下边的例子中,你可以看到使用NetworkStream类操作数据流的方法。

在下面的例子中,我们将建立一个时间服务器,包括服务器端程序和客户端程序。服务器端监听客户端的连接请求,建立连接以后向客户端发送当前的系统时间。

先运行服务器端程序,下面截图显示了服务器端程序运行的状况:

然后运行客户端程序,客户端首先发送连接请求到服务器端,服务器端回应后发送当前时间到客户端,这是客户端程序的截图:

发送完成后,服务器端继续等待下一次连接:

通过这个例子我们可以了解TcpClient类的基本用法,要使用这个类,必须使用System.Net.Socket命名空间,本例用到的三个命名空间如下:

using System;
using System.Net.Sockets;
using System.Text;//从字节数组中获取字符串时使用该命名空间中的类

首先讨论一下客户端程序,开始我们必须初始化一个TcpClient类的实例:

TcpClient client = new TcpClient(hostName, portNum);

然后使用TcpClient类的GetStream()方法获取数据流,并且用它初始化一个NetworkStream类的实例:

NetworkStream ns = client.GetStream();

注意,当使用主机名和端口号初始化TcpClient类的实例时,直到跟服务器建立了连接,这个实例才算真正建立,程序才能往下执行。如果因为网络不通,服务器不存在,服务器端口未开放等等原因而不能连接,程序将抛出异常并且中断执行。

建立数据流之后,我们可以使用NetworkStream类的Read()方法从流中读取数据,使用Write()方法向流中写入数据。读取数据时,首先应该建立一个缓冲区,具体的说,就是建立一个byte型的数组用来存放从流中读取的数据。Read()方法的原型描述如下:

public override int Read(in byte[] buffer,int offset,int size)

buffer是缓冲数组,offset是数据(字节流)在缓冲数组中存放的开始位置,size是读取的字节数目,返回值是读取的字节数。在本例中,简单地使用该方法来读取服务器反馈的信息:

byte[] bytes = new byte[1024];//建立缓冲区
int bytesRead = ns.Read(bytes, 0, bytes.Length);//读取字节流

然后显示到屏幕上:

Console.WriteLine(Encoding.ASCII.GetString(bytes,0,bytesRead));

最后不要忘记关闭连接:

client.Close();

下面是本例完整的程序清单:

using System;
using System.Net.Sockets;
using System.Text;

namespace TcpClientExample
{
public class TcpTimeClient
{
private const int portNum = 13;//服务器端口,可以随意修改
private const string hostName = "127.0.0.1";//服务器地址,127.0.0.1指本机

[STAThread]
static void Main(string[] args)
{
try
{
Console.Write("Try to connect to "+hostName+":"+portNum.ToString()+"\r\n");
TcpClient client = new TcpClient(hostName, portNum);
NetworkStream ns = client.GetStream();
byte[] bytes = new byte[1024];
int bytesRead = ns.Read(bytes, 0, bytes.Length);

Console.WriteLine(Encoding.ASCII.GetString(bytes,0,bytesRead));

client.Close();
Console.ReadLine();//由于是控制台程序,故为了清楚的看到结果,可以加上这句

}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}

上面这个例子清晰地演示了客户端程序的编写要点,下面我们讨论一下如何建立服务器程序。这个例子将使用TcpListener类,在13号端口监听,一旦有客户端连接,将立即向客户端发送当前服务器的时间信息。

TcpListener的关键在于AcceptTcpClient()方法,该方法将检测端口是否有未处理的连接请求,如果有未处理的连接请求,该方法将使服务器同客户端建立连接,并且返回一个TcpClient对象,通过这个对象的GetStream方法建立同客户端通讯的数据流。事实上,TcpListener类还提供一个更为灵活的方法AcceptSocket(),当然灵活的代价是复杂,对于比较简单的程序,AcceptTcpClient()已经足够用了。此外,TcpListener类提供Start()方法开始监听,提供Stop()方法停止监听。

首先我们使用端口初始化一个TcpListener实例,并且开始在13端口监听:

private const int portNum = 13;
TcpListener listener = new TcpListener(portNum);
listener.Start();//开始监听

如果有未处理的连接请求,使用AcceptTcpClient方法进行处理,并且获取数据流:

TcpClient client = listener.AcceptTcpClient();
NetworkStream ns = client.GetStream();

然后,获取本机时间,并保存在字节数组中,使用NetworkStream.Write()方法写入数据流,然后客户端就可以通过Read()方法从数据流中获取这段信息:

byte[] byteTime = Encoding.ASCII.GetBytes(DateTime.Now.ToString());
ns.Write(byteTime, 0, byteTime.Length);
n
s.Close();//不要忘记关闭数据流和连接
client.Close();

服务器端程序完整的程序清单如下:

using System;
using System.Net.Sockets;
using System.Text;

namespace TimeServer
{
class TimeServer
{
private const int portNum = 13;

[STAThread]
static void Main(string[] args)
{
bool done = false;
TcpListener listener = new TcpListener(portNum);
listener.Start();
while (!done)
{
Console.Write("Waiting for connection...");
TcpClient client = listener.AcceptTcpClient();

Console.WriteLine("Connection accepted.");
NetworkStream ns = client.GetStream();

byte[] byteTime = Encoding.ASCII.GetBytes(DateTime.Now.ToString());

try
{
ns.Write(byteTime, 0, byteTime.Length);
ns.Close();
client.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

listener.Stop();
}
}
}

把上面两段程序分别编译运行,OK,我们已经用C#实现了基于TCP协议的网络通讯,怎么样?很简单吧!

使用上面介绍的基本方法,我们可以很容易的编写出一些很有用的程序,如FTP,电子邮件收发,点对点即时通讯等等,你甚至可以自己编制一个QQ来!

C#是微软随着VS.net新推出的一门语言。它作为一门新兴的语言,有着C++的强健,又有着VB等的RAD特性。而且,微软推出C#主要的目的是为了对抗Sun公司的Java。大家都知道Java语言的强大功能,尤其在网络编程方面。于是,C#在网络编程方面也自然不甘落后于人。本文就向大家介绍一下C#下实现套接字(Sockets)编程的一些基本知识,以期能使大家对此有个大致了解。首先,我向大家介绍一下套接字的概念。

套接字基本概念:
套接字是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。可以将套接字看作不同主机间的进程进行双向通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。
套接字可以根据通信性质分类,这种性质对于用户是可见的。应用程序一般仅在同一类的套接字间进行通信。不过只要底层的通信协议允许,不同类型的套接字间也照样可以通信。套接字有两种不同的类型:流套接字和数据报套接字。

套接字工作原理:
要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户机端,我们称之为ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
所谓服务器监听,是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
所谓客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
所谓连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

C#中的套接字编程实例:
通过向大家简单的介绍套接字的基本概念和实现套接字编程的基本原理,我想大家对套接字编程已有了初步的了解。不过,上面介绍的仅仅是基本概念和原理,要真正运用还是需要一定的工作的。对基本概念和原理的真正理解的最好方法莫过于自己动手做一个实例,下面我就向大家介绍一个很好的用C#实现套接字编程的实例――聊天室程序。
本程序是基于C/S(服务器/客户端)构架的,程序包含一个服务器端的应用程序和一个客户端的应用程序。首先,在服务器上运行服务器端的应用程序,该程序一运行就开始服务器监听。然后,在客户机上就可以打开客户端的应用程序。程序打开后可以与服务器端应用程序进行连接,即进行客户端请求。在连接确认后,客户端用户可以和其他的客户端用户进行聊天。客户端人数没有限制,同时还支持“悄悄话”聊天模式,支持聊天记录。所以这是一个学习套接字编程的相当不错的例子。而且,程序中为了处理每个客户端的信息还用到了多线程机制。在每个客户端与服务器端连接成功后,它们之间就建立一个线程。这样运用了多线程之后,客户端之间就不会相互影响,即使其中一个出了错误也不会影响到另一个。

下面,我就向大家具体介绍该实例:
服务器端程序:
1. 打开VS.net,新建一个C#的模板为“Windows 应用程序”的项目,不妨命名为“ChatServer”。
2. 布置界面。只需在界面上添加一个ListBox控件即可,该控件主要用于显示客户端的用户的一些信息的。图象如下:
3. 服务器端程序的代码编写。
对于服务器端,主要的作用是监听客户端的连接请求并确认其请求。程序一开始便打开一个StartListening()线程。
private void StartListening()
{
listener = new TcpListener(listenport);
listener.Start();
while (true)
{
try
{
Socket s = listener.AcceptSocket();
clientsocket = s;
clientservice = new Thread(new ThreadStart(ServiceClient));
clientservice.Start();
}
catch(Exception e)
{
Console.WriteLine(e.ToString() );
}
}
}

该线程是一直处于运行状态的。当服务器端接收到一个来自客户端的连接请求后,它就打开一个ServiceClient()线程来服务客户端。当一个连接被建立后,每个客户端就被赋予一个属于它自己的套接字。同时,一个Client类的对象被建立。该对象包含了客户端的一些相关信息,该信息被保存在一个数组列表中。Client类如下(也可参见源代码中的Client.cs文件):
using System;
using System.Threading;
namespace ChatServer
{
using System.Net.Sockets;
using System.Net;

///
/// Client 的摘要说明。
///
public class Client
{
private Thread clthread;
private EndPoint endpoint;
private string name;
private Socket sock;
public Client(string _name, EndPoint _endpoint, Thread _thread, Socket _sock)
{
// TODO: 在此处添加构造函数逻辑
clthread = _thread;
endpoint = _endpoint;
name = _name;
sock = _sock;
}
public override string ToString()
{
return endpoint.ToString()+ " : " + name;
}

public Thread CLThread
{
get{return clthread;}
set{clthread = value;}
}
public EndPoint Host
{
get{return endpoint;}
set{endpoint = value;}
}
public string Name
{
get{return name;}
set{name = value;}
}

public Socket Sock
{
get{return sock;}
set{sock = value;}
}
}
}
程序的主体部分应是ServiceClient()函数。该函数是一个独立的线程,其主要部分是一个while循环。在循环体内,程序处理各种客户端命令。服务器端接收来自客户端的以ASCII码给出的字符串,其中包含了一个“|”形式的分隔符。字符串中“|”以前的部分就是具体的命令,包括CONN、CHAT、PRIV、GONE四种类型。CONN命令建立一个新的客户端连接,将现有的用户列表发送给新用户并告知其他用户有一个新用户加入。CHAT命令将新的信息发送给所有用户。PRIV命令将悄悄话发送给某个用户。GONE命令从用户列表中除去一个已离开的用户并告知其他的用户某某已经离开了。同时,GONE命令可以设置布尔型的变量keepalive为false从而结束与客户端连接的线程。ServiceClient()函数
如下:

private void ServiceClient()

{

Socket client = clientsocket;

bool keepalive = true;

while (keepalive)

{

Byte[] buffer = new Byte[1024];

client.Receive(buffer);

string clientcommand = System.Text.Encoding.ASCII.GetString(buffer);

string[] tokens = clientcommand.Split(new Char[]{'|'});

Console.WriteLine(clientcommand);

if (tokens[0] == "CONN")

{

for(int n=0; n

{

Client cl = (Client)clients[n];

SendToClient(cl, "JOIN|" + tokens[1]);

}
客户端程序:

1. 打开VS.net,新建一个C#的模板为“Windows 应用程序”的项目,不妨命名为“ChatClient”。

2. 布置界面。往界面上添加一个ListBox控件(用于显示用户列表),一个RichTextBox控件(用于显示聊天消息以及系统消息),一个TextBox控件(用于发送消息),一个CheckBox控件(确定是否为悄悄话),一个StatusBar控件以及四个Button控件(分别为“连接”、“断开连接”、“开始记录”、“发送”)。各个控件的属性设置可以参见源代码中的具体设置,这里从略。界面设计好后的图象如下:

3. 客户端程序的代码编写。

当客户端试图和服务器端进行连接时,一个连接必须建立而且得向服务器端进行注册。EstablishConnection()函数运用一个TcpClient来和服务器端取得连接,同时创建一个NetworkStream来发送消息。还有,端口号和服务器端的是保持一致的,均为5555。EstablishConnection()函数如下:

private void EstablishConnection()

{

statusBar1.Text = "正在连接到服务器";

try

{

clientsocket = new TcpClient(serveraddress,serverport);

ns = clientsocket.GetStream();

sr = new StreamReader(ns);

connected = true;

}

catch (Exception)

{

MessageBox.Show("不能连接到服务器!","错误",

MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

statusBar1.Text = "已断开连接";

}

}

在和服务器端连接成功后,程序就用RegisterWithServer()函数向服务器端发送一个CONN命令。该命令先是发送该用户的名称,然后从服务器端获得其他所有用户的列表,所有用户列表是在ListBox控件中显示的。该函数如下:

private void RegisterWithServer()

{

try

{

string command = "CONN|" + ChatOut.Text;

Byte[] outbytes = System.Text.Encoding.ASCII.GetBytes(command.ToCharArray());

ns.Write(outbytes,0,outbytes.Length);

string serverresponse = sr.ReadLine();

serverresponse.Trim();

string[] tokens = serverresponse.Split(new Char[]{'|'});

if(tokens[0] == "LIST")

{

statusBar1.Text = "已连接";

btnDisconnect.Enabled = true;

}

for(int n=1; n

lbChatters.Items.Add(tokens[n].Trim(new char[]{'\r','\n'}));

this.Text = clientname + ":已连接到服务器";

}

catch (Exception)

{

MessageBox.Show("注册时发生错误!","错误",

MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

}

}

}

if (tokens[0] == "JOIN")

{

rtbChatIn.AppendText(tokens[1].Trim() );

rtbChatIn.AppendText(" has joined the Chat\r\n");

if(logging)

{

logwriter.WriteLine(tokens[1]+" has joined the Chat");

}

string newguy = tokens[1].Trim(new char[]{'\r','\n'});

lbChatters.Items.Add(newguy);

}

if (tokens[0] == "GONE")

{

rtbChatIn.AppendText(tokens[1].Trim() );

rtbChatIn.AppendText(" has left the Chat\r\n");

if(logging)

{

logwriter.WriteLine(tokens[1]+" has left the Chat");

}

lbChatters.Items.Remove(tokens[1].Trim(new char[]{'\r','\n'}));

}

if (tokens[0] == "QUIT")

{

ns.Close();

clientsocket.Close();

keepalive = false;

statusBar1.Text = "服务器端已停止";

connected= false;

btnSend.Enabled = false;

btnDisconnect.Enabled = false;

}

}

catch(Exception){}

}

}

通过以上的一些函数,客户端程序之间就可以进行自由地聊天了,各个用户之间还可以互相发送悄悄话。所以程序已经实现了聊天室的基本功能了,不过最后各个用户还要正常地退出,那就要用到QuitChat()函数了。该函数的具体实现如下:

private void QuitChat()

{

if(connected)

{

try

{

string command = "GONE|" + clientname;

Byte[] outbytes = System.Text.Encoding.ASCII.GetBytes(command.ToCharArray());

ns.Write(outbytes,0,outbytes.Length);

clientsocket.Close();

}

catch(Exception)

{

}

}

if(logging)

logwriter.Close();

if(receive != null && receive.IsAlive)

receive.Abort();

this.Text = "客户端";

}

到此为止,客户端程序的主要部分都已经介绍完毕。还有一些按钮控件的消息处理函数可以参见源代码。同时,程序中还有一个聊天记录功能,该功能和现在流行的聊天软件的记录功能类似。不过限于篇幅,在这里就不一一介绍了,
有兴趣的读者可以研究一下本文后面的源代码。

这样,客户端程序就完成了。程序运行图示如下:

总结:

本文向大家初步介绍了套接字的基本概念和实现套接字编程的基本原理,还通过一个很好的实例向大家展示了在C#下进行套接字编程的实现方法和一些编程技巧。从中,我们不难发现运用C#进行套接字编程乃至网络编程有许多优越之处。实例程序实现的思路清晰明了而且通俗易懂,是一个相当不错的例子,希望各位能好好研读。同时还希望大家能进一步完善该程序,使之功能更强大、界面更友好。最后还要注明的是:该实例程序是在VS.net正式版下编译、运行成功的,如果你还是Beta版的话可能会有一些差别。

[Edit on 2004-8-24 1:14:54 By Felix]
京ICP备05053527号
经过26次查询历时0.550秒终于生成了此页面
Powered by WordPress & Designed by Felix © 2012