减小字体
增大字体
作者:烈·翼·焚·天 来源:新浪博客 发布时间:2010-10-12 06:51:29
与UDP不同,基于TCP协议的编程的服务器端有一个监听对象:TcpListener,它负责监听来自客户端的消息并处理,并且必须在保持连接的情况下与客户端保持互动,下面举个例子来说明这个问题。
示例一:基于TCP协议的网络编程
窗体:


Form2做为本程序的服务器端,当按下Start后,启动服务,剩下的是一个Form1,我启动了两次,都连接到Form2,当在Form1的Send栏里写入小写字母并按下Send按钮后,将该字符串发送至Form2,同时Form2将该字符串转换为大写,返回给发送者,说明完毕,出个谜语,谁知道两个Form1里字母是啥意思?
Form2:(服务器端)
public partial class Form2 : Form
{
//声明监听对象
private TcpListener tl;
//声明网络流
private NetworkStream ns;
public Form1()
{
CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
//开启8888端口的监听
tl = new TcpListener(8888);
tl.Start();
//开启线程
Thread th = new Thread(new ThreadStart(listen));
th.IsBackground = true;
th.Start();
}
private void listen()
{
while (true)
{
//获得响应的Socket
Socket sock = tl.AcceptSocket();
//通过该Socket实例化网络流
ns = new NetworkStream(sock);
//ClientTcp是添加的类,下面会做说明
ClientTcp ct = new ClientTcp(ns);
//ct_MyEvent方法注册ClientTcp类的MyEvent事件
ct.MyEvent += new MyDelegate(ct_MyEvent);
//开启线程
Thread th = new Thread(new ThreadStart(ct.TcpThread));
th.IsBackground = true;
th.Start();
}
}
void ct_MyEvent(string temp)
{
//设置服务器端TextBox的值
this.textBox1.Text = temp;
}
}
|
Form1:(客户端)
public partial class Form1 : Form
{
//声明Tcp客户端
private TcpClient tc;
//声明网络流
private NetworkStream ns;
public Form1()
{
CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
//注册本机8888端口
tc = new TcpClient("localhost",8888);
//实例化网络流对象
ns = tc.GetStream();
string temp = this.textBox1.Text;
StreamWriter sw = new StreamWriter(ns);
StreamReader sr = new StreamReader(ns);
//将TextBox1的值传给服务器端
sw.WriteLine(temp);
sw.Flush();
//接收服务器端回传的字符串
string str = sr.ReadLine();
this.textBox2.Text = str;
sr.Close();
sw.Close();
}
}
|
ClientTcp类:
//声明一个需要一个字符串参数的委托
public delegate void MyDelegate(string temp);
class ClientTcp
{
//设置网络流局部对象
private NetworkStream ns;
//声明类型为MyDelegate的事件MyEvent
public event MyDelegate MyEvent;
//构造函数中接收参数以初始化
public ClientTcp(NetworkStream ns)
{
this.ns = ns;
}
//服务器端线程所调用的方法
public void TcpThread()
{
//获得相关的封装流
StreamReader sr = new StreamReader(ns);
string temp = sr.ReadLine();
//接收到客户端消息后触发事件将消息回传
MyEvent(temp);
StreamWriter sw = new StreamWriter(ns);
//转换为大写后发送消息给客户端
sw.WriteLine(temp.ToUpper());
sw.Flush();
sw.Close();
sr.Close();
}
}
|
这里说下为什么需要ClientTcp这么个类,说这个之前,先说一下为什么服务器端需要开启一个新的线程来监控端口,这个原因比较简单,Socket sock = tl.AcceptSocket(); 这个方法会造成阻塞,也就是说如果没有得到客户端的响应,TcpListenr将一直监听下去,这就会造成程序的假死,因此我们需要单独开一个线程来监听我们的8888端口,我们观察服务器端(Form2)可以看出,NetworkStream是一个全局变量(实际上局部与全局都是一样),如果CPU忙的过来,直接把ClientTcp里的方法拿到Form2里写没问题,但是一旦客户端过多造成数据拥挤,那很可能当运算还未结束,NetworkStream就已经换人了,因此当我们取得某客户端对应的NetworkStream后,应该考虑立刻将它封装到一个类中,再在该类中再对该NetworkStream做相应的操作,ClientTcp这个类就是为这个设计的,而当封装了NetworkStream后,我们发现从客户端传过来的值是我们需要的,因此就用到了事件的回调,这个我前面有篇文章里讲过了,见http://blog.sina.com.cn/u/4c459776010008ws,基于TCP协议的网络编程基础的东西就这些,写法很固定,但是需要很多的技巧,前几天试着写一个聊天室程序,差点没吐血,果然不是一般的麻烦。
本文原文链接:http://blog.sina.com.cn/s/blog_4c459776010009c3.html
本文源自:翔宇亭——IT乐园(http://www.biye5u.com),转载请保留此信息!