2014年11月5日 星期三

Socket Programming in C# (Client side)





NOTE!!
The source code is deprecated! Please go to my Github: KarateJB/DotNetCore.Socket.Sample, for the latest Socket Server/Client sample code.

注意!!
本文中的程式碼已經過時,請您參考我Github上的最新Socket Server/Client範例程式碼:KarateJB/DotNetCore.Socket.Sample




延續上一篇Socket Programming in C# (Server side) 我們來實作Client的程式。
一開始仍先放上整個Server-client架構供參考

1    Server

1.1   為了讓Socket Server開始Listening後,仍可顯示連線的Client資訊。 故建立Socket Server後,使用BackgroundWorker來開啟Socket Server
1.2   Socket Server每次接收到新的Client要求時,再開另一條Thread來處理上傳的資料。
1.3   處理完畢後,回傳訊息給Client
1.4   結束Client的連線。
1.5   (重要) 關閉Socket Server時,要先停止Socket Listener (即第一點的BackgroundWorker),才能關閉Socket Server
2    Client

2.1   將上傳檔案名稱及其內容拆成[檔案名稱長度][檔案名稱] [檔案內容],存成Byte Array
2.2   建立TcpClientSocket物件來連線Socket Server
2.3   資料上傳完畢,利用NetworkStream來接收Server端回傳的訊息。
2.4   斷線。

一、 方案參考
1.    UploadSocket.Common : 只有放Log4Net module
2.    UploadSocket.Infra : Model
3.    UploadSocket.Service :
l  封裝Socket的類別 (UploadServer)
l  Client連線後的事件處理 (ClientHandler)
4.    Server and Client as WinForm application


二、Client side

1.    Global
We will have one file for upload-testing.
private String uploadaFileFullPath = @"D:\UploadFile.txt";

2.    Use System.Net.Sockets.Socket to build connection
l   按下SocketClient連線】的事件
private void bt_ConnSocketClick(object sender, EventArgs e)
{
            //Make IP end point same as Server.
            IPAddress[] ipAddress = Dns.GetHostAddresses("localhost");
            IPEndPoint ipEnd = new IPEndPoint(ipAddress[1], 5656);
            Socket clientSocket = null;

            try
            {
                //Make a client socket to send data to server.
                clientSocket = new Socket(
                    AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.IP);

                //連線到Socket Server
                clientSocket.Connect(ipEnd);

                //取得檔案轉成要送出的資料格式
                var byteData = this.getFileByteArray();

                #region Send without callback
                //clientSocket.Send(byteData);
                #endregion

                #region Send with callback
                clientSocket.BeginSend(
                    byteData, 0, byteData.Length, 0,
                    new AsyncCallback(sendCallback), clientSocket);
                #endregion

                //取得Server傳回的訊息
                String rtnMsg = String.Empty;
                NetworkStream serverStream = new NetworkStream(clientSocket);
                this.getServerReturnData(
serverStream, clientSocket.ReceiveBufferSize, out rtnMsg);

                tb_ServerRMsg.Text = rtnMsg;           
}
            catch (Exception ex){
                // Exception handling … }
            finally{
                ipAddress = null;
                ipEnd = null;
                clientSocket.Close();
                clientSocket.Dispose();
            }
}

l   取得Server傳回的資料 (訊息)
private void getServerReturnData(
NetworkStream serverStream, int dataLength, out String serverMsg)
{
//讀取 socket server 回傳值並轉為 string
byte[] inStream = new byte[10025];
serverStream.Read(inStream, 0, dataLength);
serverMsg= System.Text.Encoding.UTF8.GetString(inStream);
}

l   取得檔案轉成要送出的資料格式
private byte[] getFileByteArray()
{               
/* 取得檔案名稱 路徑 */
uploadaFileFullPath = uploadaFileFullPath.Replace("\\", "/");
String fileName = Path.GetFileName(uploadaFileFullPath);

//讀取檔案名稱並轉為Byte[]
byte[] fileNameByte = Encoding.ASCII.GetBytes(fileName);
      //讀取檔案內容並轉為Byte[]
byte[] fileData = File.ReadAllBytes(filePath + fileName);

      //取得檔案名稱的長度並轉為Byte[]
byte[] fileNameLen =
Encoding.ASCII.GetBytes(fileNameByte.Length.ToString());
//定義 clientData : 存放[檔案名稱長度][檔案名稱] [檔案內容]
byte[] clientData =
new byte[4 + fileNameByte.Length + fileData.Length];

//開始放入資料到 clientData : 存放
//[檔案名稱長度 byte0~3][檔案名稱 byte4~7] [檔案內容 byte7~]
fileNameLen.CopyTo(clientData, 0);
fileNameByte.CopyTo(clientData, 4);
fileData.CopyTo(clientData, 4 + fileNameByte.Length);
return clientData;
}

l   Callback
private void sendCallback(IAsyncResult ar)
{
Debug.WriteLine("Callback!");
}

1.    Use System.Net.Sockets.TcpClient to build connection
l   按下【TcpClient連線】的事件
private void bt_ConnTcpClick(object sender, EventArgs e)
{
//遠端 socket server IP 位址
String remoteIpAddress = "127.0.0.1";
//遠端 socket server 所監聽的 port number
int remotePortNumber = 5656;

TcpClient tcpClient = null;
try{
        //初始化Socket client
tcpClient = new TcpClient();
tcpClient.Connect(remoteIpAddress, remotePortNumber);
//取得用來傳送訊息至 socket server stream 物件
NetworkStream serverStream = tcpClient.GetStream();
//將資料轉為 byte[]
byte[] outStream = getFileByteArray();

//將資料寫入 stream object (表示傳送資料至 socket server)
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
//讀取 socket server 回傳值並轉為 string
String rtnMsg = String.Empty;
this.getServerReturnData(
serverStream, tcpClient.ReceiveBufferSize, out rtnMsg);
//顯示Server回傳訊息
tb_ServerRMsg.Text = rtndata;
}
catch (Exception ex){ }
finally{
tcpClient.Close();
tcpClient = null;
}
}

三、 Server side : Snapshot



沒有留言:

張貼留言