編寫程序代碼
如果你已經看完了上面一節C#網絡編程,那麼本章完全沒有講解的必要了,所以我只列出代碼,對個別值得注意的地方稍微地講述一下。首 先需要了解的就是,我們采用的是三個模式中開發起來難度較大的一種,無服務器參與的模式。還有就是我們沒有使用廣播消息,所以需要提 前知道連接到的遠程主機的地址和端口號。
1.實現IMessageSender接口
public class MessageSender : IMessageSender {
TcpClient clIEnt;
Stream streamToServer;
// 連接至遠程
public bool Connect(IPAddress ip, int port) {
try {
client = new TcpClIEnt();
clIEnt.Connect(ip, port);
streamToServer = clIEnt.GetStream(); // 獲取連接至遠程的流
return true;
} catch {
return false;
}
}
// 發送消息
public bool SendMessage(Message msg) {
try {
lock (streamToServer) {
byte[] buffer = Encoding.Unicode.GetBytes(msg.ToString());
streamToServer.Write(buffer, 0, buffer.Length);
return true;
}
} catch {
return false;
}
}
// 注銷
public void SignOut() {
if (streamToServer != null)
streamToServer.Dispose();
if (clIEnt != null)
clIEnt.Close();
}
}
這段代碼可以用樸實無華來形容,所以我們直接看下一段。
2.實現IMessageReceiver接口
public delegate void PortNumberReadyEventHandler(int portNumber);
public class MessageReceiver : IMessageReceiver {
public event MessageReceivedEventHandler MessageReceived;
public event ConnectionLostEventHandler ClIEntLost;
public event ClientConnectedEventHandler ClIEntConnected;
// 當端口號Ok的時候調用 -- 需要告訴用戶界面使用了哪個端口號在偵聽
// 這裡是業務上體現不出來,在實現中才能體現出來的
public event PortNumberReadyEventHandler PortNumberReady;
private Thread workerThread;
private TcpListener listener;
public MessageReceiver() {
((IMessageReceiver)this).StartListen();
}
// 開始偵聽:顯示實現接口
void IMessageReceiver.StartListen() {
ThreadStart start = new ThreadStart(ListenThreadMethod);
workerThread = new Thread(start);
workerThread.IsBackground = true;
workerThread.Start();
}
// 線程入口方法
private void ListenThreadMethod() {
IPAddress localIp = IPAddress.Parse("127.0.0.1");
listener = new TcpListener(localIp, 0);
listener.Start();
// 獲取端口號
IPEndPoint endPoint = listener.LocalEndpoint as IPEndPoint;
int portNumber = endPoint.Port;
if (PortNumberReady != null) {
PortNumberReady(portNumber); // 端口號已經OK,通知用戶界面
}
while (true) {
TcpClient remoteClIEnt;
try {
remoteClient = listener.AcceptTcpClIEnt();
} catch {
break;
}
if (ClIEntConnected != null) {
// 連接至本機的遠程端口
endPoint = remoteClient.ClIEnt.RemoteEndPoint as IPEndPoint;
ClIEntConnected(endPoint); // 通知用戶界面遠程客戶連接
}
Stream streamToClient = remoteClIEnt.GetStream();
byte[] buffer = new byte[8192];
while (true) {
try {
int bytesRead = streamToClIEnt.Read(buffer, 0, 8192);
if (bytesRead == 0) {
throw new Exception("客戶端已斷開連接");
}
string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);
if (MessageReceived != null) {
MessageReceived(msg); // 已經收到消息
}
} catch (Exception ex) {
if (ClIEntLost != null) {
ClIEntLost(ex.Message); // 客戶連接丟失
break; // 退出循環
}
}
}
}
}
// 停止偵聽端口
public void StopListen() {
try {
listener.Stop();
listener = null;
workerThread.Abort();
} catch { }
}
}
這裡需要注意的有這樣幾點:我們StartListen()為顯式實現接口,因為只能通過接口才能調用此方法,接口的實現類看不到此方法;這通 常是對於一個接口采用兩種實現方式時使用的,但這裡我只是不希望MessageReceiver類型的客戶調用它,因為在MessageReceiver的構造函數 中它已經調用了StartListen。意思是說,我們希望這個類型一旦創建,就立即開始工作。我們使用了兩個嵌套的while循環,這個它可以為多 個客戶端的多次請求服務,但是因為是同步操作,只要有一個客戶端連接著,我們的後台線程就會陷入第二個循環中無法自拔。所以結果是: 如果有一個客戶端已經連接上了,其它客戶端即使連接了也無法對它應答。最後需要注意的就是四個事件的使用,為了向用戶提供偵聽的端口 號以進行連接,我又定義了一個PortNumberReadyEventHandler委托。