Hashtable socketHolder = new Hashtable();
Hashtable threadHolder = new Hashtable();
public Form1()
{
// Required for Windows Form Designer support
//
InitializeComponent();
tcpLsn = new TcpListener(8002);
tcpLsn.Start();
// tcpLsn.LocalEndpoint may have a bug, it only show 0.0.0.0:8002
stpanel.Text = "Listen at: " + tcpLsn.LocalEndpoint.
ToString
();
Thread tcpThd = new Thread(new ThreadStart(WaitingForClient));
threadHolder.
Add
(connectId, tcpThd);
tcpThd.Start() ;
}
2. TcpClient與TcpSrv連接上后,發(fā)送客戶端信息數(shù)據(jù)包至TcpServer,然后發(fā)射線程,該線程是用來接收通過Socket傳來的數(shù)據(jù)。
private void menuConn_Click(object sender, System.EventArgs e)
{ ConnectDlg myDlg = new ConnectDlg();
myDlg.ShowDialog(this);
if( myDlg.DialogResult==DialogResult.OK)
{
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp );
IPAddress hostadd = IPAddress.Parse(myDlg.IpAdd);
int port=Int32.Parse(myDlg.PortNum);
IPEndPoint EPhost = new IPEndPoint(hostadd, port);
Try
{
s.Connect(EPhost);
if (s.Connected)
{
Byte[] bBuf;
string buf;
buf = String.Format("{0}:{1}", myDlg.UserName,myDlg.PassWord);
bBuf=ASCII.GetBytes(buf);
s.Send(bBuf, 0 , bBuf.Length,0);
t = new Thread(new ThreadStart(StartRecieve));
t.Start();
sbar.Text="Ready to recieve data";
}
}
catch (Exception e1)
{
MessageBox.Show(e1.ToString());
}
}
}
private void StartRecieve()
{
miv = new MethodInvoker(this.UpdateListView);
int cnt=0;
string tmp=null;
Byte[] firstb= new Byte[1];
while (true)
{
try
{
Byte[] receive = new Byte[1];
int ret = s.Receive(receive, 1, 0);
if (ret > 0)
{
switch(receive[0])
{
case 11: //check start message
cnt=0;
break;
case 10: // check end message
cnt=0;
if(firstb[0] == ':')
HandleCommand(tmp);
else if(firstb[0] == '<')
HandleXml(tmp);
else
HandleText(tmp);
tmp=null;
break;
default:
if (cnt == 0)
firstb[0] = receive[0];
tmp += System.Text.Encoding
.ASCII.GetString(receive);
cnt++;
break;
}
}
}
catch (Exception e)
{
if( !s.Connected )
{
break;
}
}
}
t.Abort();
}
|
3.TcpServer接收來自TcpClient的連接請(qǐng)求,并且將socket 實(shí)例保存到Hash表中,然后發(fā)射線程以便控制socket的通訊,同時(shí)將客戶端信息在listview 控件中顯示出來。
public void WaitingForClient()
{
while(true)
{
// Accept will block until someone connects
Socket sckt = tcpLsn.AcceptSocket();
if (connectId < 10000)
Interlocked.Increment(ref connectId);
Else
connectId = 1;
if (socketHolder.Count < MaxConnected ) {
while (socketHolder.Contains(connectId) )
{
Interlocked.Increment(ref connectId);
}
Thread td = new Thread(new ThreadStart(ReadSocket));
lock(this)
{
// it is used to keep connected Sockets
socketHolder.Add(connectId, sckt);
// it is used to keep the active thread
threadHolder.Add(connectId, td);
}
td.Start();
}
}
}
// follow function handle the communication from the clients and close the
// socket and the thread when the socket connection is down
public void ReadSocket()
{
// the connectId is keeping changed with new connection added. it can't
// be used to keep the real connectId, the local variable realId will
// keep the value when the thread started.
long realId = connectId;
int ind=-1;
Socket s = (Socket)socketHolder[realId];
while (true)
{
if(s.Connected)
{
Byte[] receive = new Byte[37] ;
Try
{
// Receive will block until data coming
// ret is 0 or Exception happen when Socket connection
// is broken
int ret=s.Receive(receive,receive.Length,0);
if (ret > 0)
{
string tmp = null;
tmp=System.Text.Encoding.ASCII.GetString(receive);
if(tmp.Length > 0)
{
DateTime now1=DateTime.Now;
String strDate;
strDate = now1.ToShortDateString() + " " + now1.ToLongTimeString();
ListViewItem newItem = new ListViewItem();
string[] strArry=tmp.Split(':');
int code = checkUserInfo(strArry[0]);
if(code==2)
{
userHolder.Add(realId, strArry[0]);
newItem.SubItems.Add(strArry[0]);
newItem.ImageIndex = 0;
newItem.SubItems.Add(strDate);
this.listView2.Items.Add(newItem);
ind=this.listView2.Items.IndexOf(newItem);
}
else if( code==1)
}
}
else
{
this.listView2.Items[ind].ImageIndex=1;
keepUser=false;
break;
}
}
catch (Exception e)
{
if( !s.Connected )
{
this.listView2.Items[ind].ImageIndex=1;
keepUser=false;
break;
}
}
}
} CloseTheThread(realId);
}
private void CloseTheThread(long realId)
{
socketHolder.Remove(realId);
if(!keepUser) userHolder.Remove(realId);
lock(this)
{
Thread thd = (Thread)threadHolder[realId];
threadHolder.Remove(realId);
}
thd.Abort();
}
4. 點(diǎn)擊Load
Data菜單,從文件中載入信息,然后把所有信息傳送到每個(gè)將與TcpServer相連接的客戶端,客戶端會(huì)自己更新它的listview。不管是
TcpServer 還是 TcpClient ,它們都從運(yùn)作中的線程之中獲取數(shù)據(jù),再在主線程中更新Listview
control。下面則講述的是通過MethodInvoker實(shí)現(xiàn)該功能。
public void LoadThread()
{
MethodInvoker mi = new MethodInvoker(this.UpdateListView);
string tmp = null;
StreamReader sr = File.OpenText("Issue.txt");
while((tmp = sr.ReadLine()) !=null )
{
if (tmp =="")
break;
isu.symbol= Mid(tmp, 0, 4);
isu.bid = Mid(tmp, 4, 5);
isu.offer = Mid(tmp, 9, 5);
isu.volume = Mid(tmp, 16, tmp.Length-16);
sendMsg ="\v" + tmp + "\n"; //add send message's head and end char
SendDataToAllClient(tmp);
this.BeginInvoke(mi);
JobDone.WaitOne();
}
sr.Close();
fThd.Abort();
}
private void SendDataToAllClient(string str)
{
foreach (Socket s in socketHolder.Values)
{
if(s.Connected)
{
Byte[] byteDateLine=ASCII.GetBytes(str.ToCharArray());
s.Send(byteDateLine, byteDateLine.Length, 0);
}
}
}
|
以下代碼 操縱XML文件,并且為客戶端生成XML文件。
public void LoadXmlThread()
{
MethodInvoker miv = new MethodInvoker(this.UpdateListView);
string tmp = null;
string xmlString = null;
int recordFlg = -1;
int textCount =0;
xmlString = "\v"+"";
XmlTextReader tr = new XmlTextReader("issue.xml");
while(tr.Read())
{
switch (tr.NodeType)
{
case XmlNodeType.Element:
if (tr.Name == "Issue")
{
recordFlg++;
if(recordFlg > 0)
{
textCount=0;
xmlString += CreateXmlElement(
tr.Name, 2);
xmlString += "\n";
SendDataToAllClient(xmlString);
xmlString = "\v"+" version='1.0'?>";
this.BeginInvoke(miv);
JobDone.WaitOne();
}
}
if (recordFlg >= 0)
{
xmlString += CreateXmlElement(
tr.Name, 1);
tmp = tr.Name;
}
break;
case XmlNodeType.Text:
switch(++textCount)
{
case 1:
isu.symbol=tr.Value;
break;
case 2:
isu.bid=tr.Value;
break;
case 3:
isu.offer=tr.Value;
break;
case 4:
isu.volume=tr.Value;
break;
}
xmlString += tr.Value;
xmlString += CreateXmlElement(tmp, 2);
break;
}
}
fThd.Abort();
}
string CreateXmlElement(string elem, int ord)
{
string tmp = null;
if (ord == 1)
tmp = String.Format("<{0}>", elem);
else
tmp = String.Format("", elem);
return tmp;
}
以下功能演示的是如何設(shè)置TcpClient中Listview控件的 BackColor和 Forecolor屬性 。 private void UpdateListView()
{
int ind=-1;
for (int i=0; i < this.listView1.Items.Count;i++)
{
if (this.listView1.Items[i].Text == isu.symbol.ToString())
{
ind=i;
break;
}
}
if (ind == -1)
{
ListViewItem newItem new ListViewItem(isu.symbol.ToString());
newItem.SubItems.Add(isu.bid);
newItem.SubItems.Add(isu.offer);
newItem.SubItems.Add(isu.volume);
this.listView1.Items.Add(newItem);
int i=this.listView1.Items.IndexOf(newItem);
setRowColor(i, System.Drawing.Color.FromA#ffffaf);
setColColorHL(i, 0, System.Drawing.Color.FromA#800000);
setColColorHL(i, 1, System.Drawing.Color.FromA#800000);
this.listView1.Update();
Thread.Sleep(300);
setColColor(i, 0, System.Drawing.Color.FromA#ffffaf);
setColColor(i, 1, System.Drawing.Color.FromA#ffffaf);
}
else
{
this.listView1.Items[ind].Text = isu.symbol.ToString();
this.listView1.Items[ind].SubItems[1].Text = (isu.bid);
this.listView1.Items[ind].SubItems[2].Text = (isu.offer);
this.listView1.Items[ind].SubItems[3].Text = (isu.volume);
setColColorHL(ind, 0, System.Drawing.Color.FromA#800000);
setColColorHL(ind, 1, System.Drawing.Color.FromA#800000);
this.listView1.Update();
Thread.Sleep(300);
setColColor(ind, 0, System.Drawing.Color.FromA#ffffaf);
setColColor(ind, 1, System.Drawing.Color.FromA#ffffaf);
}
JobDone.Set();
}
private void setRowColor(int rowNum, Color colr )
{
for (int i=0; i < this.listView1.Items[rowNum].SubItems.Count;i++)
if (rowNum%2 !=0)
this.listView1.Items[rowNum].SubItems[i].BackColor = colr;
}
private void setColColor(int rowNum, int colNum, Color colr )
{
if (rowNum%2 !=0)
this.listView1.Items[rowNum].SubItems[colNum].BackColor=colr;
else
this.listView1.Items[rowNum].SubItems[colNum].BackColor =
System.Drawing.Color.FromA#f8f8f8;
if (colNum==0)
{
this.listView1.Items[rowNum].SubItems[colNum].ForeColor =
System.Drawing.Color.FromA#800040;
this.listView1.Items[rowNum].SubItems[colNum].BackColor =
System.Drawing.Color.FromA#c5c5b6;
}
else
this.listView1.Items[rowNum].SubItems[colNum].ForeColor =
System.Drawing.Color.FromA#141414;
}
private void setColColorHL(int rowNum, int colNum, Color colr )
{
this.listView1.Items[rowNum].SubItems[colNum].BackColor = colr;
this.listView1.Items[rowNum].SubItems[colNum].ForeColor =
System.Drawing.Color.FromA#ffffff;
}
|
運(yùn)行該例子的步驟
1. 在A機(jī)上運(yùn)行TcpServer.exe文件。
2. 在A機(jī)或B機(jī)上運(yùn)行一次或多次TcpClient.exe文件。
3. 在TcpClient端,點(diǎn)擊菜單連接,進(jìn)入TcpServer正在運(yùn)行中的服務(wù)器端。在編輯欄鍵入用戶名及口令,點(diǎn)擊確認(rèn)。
4. 當(dāng)在TcpServer頂部的istview上瞧見客戶端的提示時(shí),則在TcpServer,上點(diǎn)擊Load Data菜單,然后實(shí)時(shí)數(shù)據(jù)則會(huì)出現(xiàn)在TcpServer 和TcpClien上。
注意:請(qǐng)確認(rèn)Data file, Issue.txt and Issue.xml等文件總是處于同一根目錄下,正如TcpSvr.exe 和 MaskedTextBox.dll, WTcpClient.exe.是處于同一目錄下一樣。
Update at 10/20/2001.
當(dāng)添加/刪除項(xiàng)目時(shí),請(qǐng)鎖住Hash表,這樣可以確保線程的安全。
添加功能,以便生成和處理XML格式文件。
在發(fā)送訊息至客戶端時(shí),請(qǐng)?jiān)诜?wù)器端添加發(fā)送訊息起始和結(jié)尾的字符。
上述方法可以增加客戶端的穩(wěn)定性。
|
|