2.3 ProtocolHelper輔助類
這個類專用於將XML格式的協議映射為我們上面定義的強類型對象,這裡我沒有加入try/catch異常處 理,因為協議對用戶來說是不可見的,而且客戶端應該總是發送正確的協議,我覺得這樣可以讓代碼更加 清晰:
public class ProtocolHelper {
private XMLNode fileNode;
private XMLNode root;
public ProtocolHelper(string protocol) {
XmlDocument doc = new XMLDocument();
doc.LoadXML(protocol);
root = doc.DocumentElement;
fileNode = root.SelectSingleNode("file");
}
// 此時的protocal一定為單條完整protocal
private FileRequestMode GetFileMode() {
string mode = fileNode.Attributes["mode"].Value;
mode = mode.ToLower();
if (mode == "send")
return FileRequestMode.Send;
else
return FileRequestMode.Receive;
}
// 獲取單條協議包含的信息
public FileProtocol GetProtocol() {
FileRequestMode mode = GetFileMode();
string fileName = "";
int port = 0;
fileName = fileNode.Attributes["name"].Value;
port = Convert.ToInt32(fileNode.Attributes["port"].Value);
return new FileProtocol(mode, port, fileName);
}
}
OK,我們又耽誤了點時間,下面就讓我們進入正題吧。
3.客戶端發送數據
3.1 服務端的實現
我們還是將一個問題分成兩部分來處理,先是發送數據,然後是接收數據。我們先看發送數據部分的 服務端。如果你從第一篇文章看到了現在,那麼我覺得更多的不是技術上的問題而是思路,所以我們不再 將重點放到代碼上,這些應該很容易就看懂了。
class Server {
static void
Main(string[] args) {
Console.WriteLine("Server is running ... ");
IPAddress ip = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(ip, 8500);
listener.Start(); // 開啟對控制端口 8500 的偵聽
Console.WriteLine("Start Listening ...");
while (true) {
// 獲取一個連接,同步方法,在此處中斷
TcpClient client = listener.AcceptTcpClIEnt();
RemoteClient wapper = new RemoteClient(clIEnt);
wapper.BeginRead();
}
}
}
public class RemoteClIEnt {
private TcpClient clIEnt;
private NetworkStream streamToClIEnt;
private const int BufferSize = 8192;
private byte[] buffer;
private ProtocolHandler handler;
public RemoteClient(TcpClient clIEnt) {
this.client = clIEnt;
// 打印連接到的客戶端信息
Console.WriteLine("\nClIEnt Connected!{0} <-- {1}",
client.Client.LocalEndPoint, client.ClIEnt.RemoteEndPoint);
// 獲得流
streamToClient = clIEnt.GetStream();
buffer = new byte[BufferSize];
handler = new ProtocolHandler();
}
// 開始進行讀取
public void BeginRead() {
AsyncCallback callBack = new AsyncCallback(OnReadComplete);
streamToClIEnt.BeginRead(buffer, 0, BufferSize, callBack, null);
}
// 再讀取完成時進行回調
private void OnReadComplete(IAsyncResult ar) {
int bytesRead = 0;
try {
lock (streamToClIEnt) {
bytesRead = streamToClIEnt.EndRead(ar);
Console.WriteLine("
Reading data, {0} bytes ...", bytesRead);
}
if (bytesRead == 0) throw new Exception("讀取到0字節");
string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);
Array.Clear(buffer,0,buffer.Length); // 清空緩存,避 免髒讀
// 獲取protocol數組
string[] protocolArray = handler.GetProtocol(msg);
foreach (string pro in protocolArray) {
// 這裡異步調用,不然這裡可能會比較耗時
ParameterizedThreadStart start =
new ParameterizedThreadStart(handleProtocol);
start.BeginInvoke(pro, null, null);
}
// 再次調用BeginRead(),完成時調用自身,形成無限循環
lock (streamToClIEnt) {
AsyncCallback callBack = new AsyncCallback (OnReadComplete);
streamToClIEnt.BeginRead(buffer, 0, BufferSize, callBack, null);
}
} catch(Exception ex) {
if(streamToClIEnt!=null)
streamToClIEnt.Dispose();
clIEnt.Close();
Console.WriteLine(ex.Message); // 捕獲異常時退出程序
}
}
// 處理protocol
private void handleProtocol(object obj) {
string pro = obj as string;
ProtocolHelper helper = new ProtocolHelper(pro);
FileProtocol protocol = helper.GetProtocol();
if (protocol.Mode == FileRequestMode.Send) {
// 客戶端發送文件,對服務端來說則是接收文件
receiveFile(protocol);
} else if (protocol.Mode == FileRequestMode.Receive) {
// 客戶端接收文件,對服務端來說則是發送文件
// sendFile(protocol);
}
}
private void receiveFile(FileProtocol protocol) {
// 獲取遠程客戶端的位置
IPEndPoint endpoint = client.ClIEnt.RemoteEndPoint as IPEndPoint;
IPAddress ip = endpoint.Address;
// 使用新端口號,獲得遠程用於接收文件的端口
endpoint = new IPEndPoint(ip, protocol.Port);
// 連接到遠程客戶端
TcpClient localClIEnt;
try {
localClient = new TcpClIEnt();
localClIEnt.Connect(endpoint);
} catch {
Console.WriteLine("無法連接到客戶端 --> {0}", endpoint);
return;
}
// 獲取發送文件的流
NetworkStream streamToClient = localClIEnt.GetStream();
// 隨機生成一個在當前目錄下的文件名稱
string path =
Environment.CurrentDirectory + "/" + generateFileName (protocol.FileName);
byte[] fileBuffer = new byte[1024]; // 每次收1KB
FileStream fs = new FileStream(path, FileMode.CreateNew, FileAccess.Write);
// 從緩存buffer中讀入到文件流中
int bytesRead;
int totalBytes = 0;
do {
bytesRead = streamToClIEnt.Read(buffer, 0, BufferSize);
fs.Write(buffer, 0, bytesRead);
totalBytes += bytesRead;
Console.WriteLine("Receiving {0} bytes ...", totalBytes);
} while (bytesRead > 0);
Console.WriteLine("Total {0} bytes received, Done!", totalBytes);
streamToClIEnt.Dispose();
fs.Dispose();
localClIEnt.Close();
}
// 隨機獲取一個圖片名稱
private string generateFileName(string fileName) {
DateTime now = DateTime.Now;
return String.Format(
"{0}_{1}_{2}_{3}", now.Minute, now.Second, now.Millisecond, fileName
);
}
}