2015年8月20日 星期四

Asynchronous Client Socket Example

The following example program creates a client that connects to a server. The client is built with an asynchronous socket, so execution of the client application is not suspended while the server returns a response. The application sends a string to the server and then displays the string returned by the server on the console.

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;

// State object for receiving data from remote device.
public class StateObject {
    // Client socket.
    public Socket workSocket = null;
    // Size of receive buffer.
    public const int BufferSize = 256;
    // Receive buffer.
    public byte[] buffer = new byte[BufferSize];
    // Received data string.
    public StringBuilder sb = new StringBuilder();
}

public class AsynchronousClient {
    // The port number for the remote device.
    private const int port = 11000;

    // ManualResetEvent instances signal completion.
    private static ManualResetEvent connectDone = 
        new ManualResetEvent(false);
    private static ManualResetEvent sendDone = 
        new ManualResetEvent(false);
    private static ManualResetEvent receiveDone = 
        new ManualResetEvent(false);

    // The response from the remote device.
    private static String response = String.Empty;

    private static void StartClient() {
        // Connect to a remote device.
        try {
            // Establish the remote endpoint for the socket.
            // The name of the 
            // remote device is "host.contoso.com".
            IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
            IPAddress ipAddress = ipHostInfo.AddressList[0];
            IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);

            // Create a TCP/IP socket.
            Socket client = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);

            // Connect to the remote endpoint.
            client.BeginConnect( remoteEP, 
                new AsyncCallback(ConnectCallback), client);
            connectDone.WaitOne();

            // Send test data to the remote device.
            Send(client,"This is a test");
            sendDone.WaitOne();

            // Receive the response from the remote device.
            Receive(client);
            receiveDone.WaitOne();

            // Write the response to the console.
            Console.WriteLine("Response received : {0}", response);

            // Release the socket.
            client.Shutdown(SocketShutdown.Both);
            client.Close();
            
        } catch (Exception e) {
            Console.WriteLine(e.ToString());
        }
    }

    private static void ConnectCallback(IAsyncResult ar) {
        try {
            // Retrieve the socket from the state object.
            Socket client = (Socket) ar.AsyncState;

            // Complete the connection.
            client.EndConnect(ar);

            Console.WriteLine("Socket connected to {0}",
                client.RemoteEndPoint.ToString());

            // Signal that the connection has been made.
            connectDone.Set();
        } catch (Exception e) {
            Console.WriteLine(e.ToString());
        }
    }

    private static void Receive(Socket client) {
        try {
            // Create the state object.
            StateObject state = new StateObject();
            state.workSocket = client;

            // Begin receiving the data from the remote device.
            client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReceiveCallback), state);
        } catch (Exception e) {
            Console.WriteLine(e.ToString());
        }
    }

    private static void ReceiveCallback( IAsyncResult ar ) {
        try {
            // Retrieve the state object and the client socket 
            // from the asynchronous state object.
            StateObject state = (StateObject) ar.AsyncState;
            Socket client = state.workSocket;

            // Read data from the remote device.
            int bytesRead = client.EndReceive(ar);

            if (bytesRead > 0) {
                // There might be more data, so store the data received so far.
            state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));

                // Get the rest of the data.
                client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
                    new AsyncCallback(ReceiveCallback), state);
            } else {
                // All the data has arrived; put it in response.
                if (state.sb.Length > 1) {
                    response = state.sb.ToString();
                }
                // Signal that all bytes have been received.
                receiveDone.Set();
            }
        } catch (Exception e) {
            Console.WriteLine(e.ToString());
        }
    }

    private static void Send(Socket client, String data) {
        // Convert the string data to byte data using ASCII encoding.
        byte[] byteData = Encoding.ASCII.GetBytes(data);

        // Begin sending the data to the remote device.
        client.BeginSend(byteData, 0, byteData.Length, 0,
            new AsyncCallback(SendCallback), client);
    }

    private static void SendCallback(IAsyncResult ar) {
        try {
            // Retrieve the socket from the state object.
            Socket client = (Socket) ar.AsyncState;

            // Complete sending the data to the remote device.
            int bytesSent = client.EndSend(ar);
            Console.WriteLine("Sent {0} bytes to server.", bytesSent);

            // Signal that all bytes have been sent.
            sendDone.Set();
        } catch (Exception e) {
            Console.WriteLine(e.ToString());
        }
    }
    
    public static int Main(String[] args) {
        StartClient();
        return 0;
    }
}

from:
https://msdn.microsoft.com/zh-tw/library/bew39x2a(v=vs.100).aspx

C#: 多執行緒, Socket-Server/Clinet

要寫到Socket, 多執行緒就變成是必然要學會的, 當然你可以用非同步的方式來完成 Socket,改由委派的方式透過類似事件的型式來避開多緒這一塊, 但個人覺得非同步背後應當也是多緒的原理, 只是程式幫你處理好了。Any way, 不管非同步如何進行, 我們這裡要討論的是如何使用多緒。Socket 之所以要用到多緒, 是因為 Server 端在執行 Accept() 等待Client端連上線時,程式會被hand住直到 Client 端連上線為止, 如此主程式便無法再做任何事!!
這裡附帶要提的是, 一般若不是因為程式會被 hand 住或者要執行某一個很複雜耗時的函式的話, 是不需用到多執行緒的, 簡單的程式或不耗時的程式用多緒反而會不效率喔!!
  
要使用 Socket 和 Thread 之前要分別 using System.Sockets 和 System.Threading, 一般用到 Socket 我們會多 using System.Net 方便取得一些電腦的資訊.
using System.Net;using System.Net.Sockets;using System.Threading;

Server端:
Server 部份的功能是聆聽等待 Clinet 端連線, 並分配每一個 Client 對應一個 Socket; 另外還有接收來自 Client 的資料, 或者傳送資料給 Client 端等功能.

//  在宣告區先行宣告 Socket 物件 Socket[] SckSs;   // 一般而言 Server 端都會設計成可以多人同時連線. int SckCIndex;    // 定義一個指標用來判斷現下有哪一個空的 Socket 可以分配給Client 端連線;string LocalIP;   int SPort = 6101;
  
// 聆聽private void Listen(){
// 用 Resize 的方式動態增加 Socket 的數目
Array.Resize(ref SckSs, 1);
SckSs[0] = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);
SckSs[0].Bind(new IPEndPoint(IPAddress.Parse(LocalIP), SPort));

// 其中 LocalIP 和 SPort 分別為 string 和 int 型態, 前者為 Server 端的IP,後者為S erver 端的Port
SckSs[0].Listen(10); // 進行聆聽; Listen( )為允許 Client 同時連線的最大數
SckSsWaitAccept();   // 另外寫一個函數用來分配 Client 端的 Socket
}

// 等待Client連線
private void SckCWaitAccept()
{
// 判斷目前是否有空的 Socket 可以提供給Client端連線
bool FlagFinded = false;
     for (int i = 1; i <= SckSs.Length - 1; i++)
     {
// SckSs[i] 若不為 null 表示已被實作過, 判斷是否有 Client 端連線
if (SckSs[i] != null) 
         {
// 如果目前第 i 個 Socket 若沒有人連線, 便可提供給下一個 Client 進行連線
     if (SckSs[i].Connected == false) 
{
                  SckCIndex = i;
                  FlagFinded = true;
break;
}
}
}
        // 如果 FlagFinded 為 false 表示目前並沒有多餘的 Socket 可供 Client 連線
if (FlagFinded == false)
     {
         // 增加 Socket 的數目以供下一個 Client 端進行連線
         SckCIndex = SckSs.Length;
Array.Resize(ref SckSs, SckCIndex + 1);
}

// 以下兩行為多執行緒的寫法, 因為接下來 Server 端的部份要使用 Accept() 讓Cleint 進行連線;
// 該執行緒有需要時再產生即可, 因此定義為區域性的 Thread. 命名為SckSAcceptTd;
// 在 new Thread( ) 裡為要多執行緒去執行的函數. 這裡命名為SckSAcceptProc;
Thread SckSAcceptTd = new Thread(SckSAcceptProc);
     SckSAcceptTd.Start();  // 開始執行 SckSAcceptTd 這個執行緒

      // 這裡要點出 SckSacceptTd 這個執行緒會在 Start( ) 之後開始執行SckSAcceptProc 裡的程式碼, 同時主程式的執行緒也會繼續往下執行各做各的. 
            // 主程式不用等到 SckSAcceptProc 的程式碼執行完便會繼續往下執行.
}


// 接收來自Client的連線與Client傳來的資料
private void SckSAcceptProc()
{
// 這裡加入 try 是因為 SckSs[0] 若被 Close 的話, SckSs[0].Accept() 會產生錯誤
try
     {
SckSs[SckCIndex] = SckSs[0].Accept();  // 等待Client 端連線
// 為什麼 Accept 部份要用多執行緒, 因為 SckSs[0] 會停在這一行程式碼直到有 Client 端連上線, 並分配給 SckSs[SckCIndex] 給 Client 連線之後程式才會繼續往下, 若是將 Accept 寫在主執行緒裡, 在沒有Client連上來之前, 主程式將會被hand在這一行無法再做任何事了!!

// 能來這表示有 Client 連上線. 記錄該 Client 對應的 SckCIndex
int Scki = SckCIndex;
// 再產生另一個執行緒等待下一個 Client 連線
SckCWaitAccept();   

long IntAcceptData;
byte[] clientData = new byte[RDataLen];  // 其中RDataLen為每次要接受來自 Client 傳來的資料長度

while (true)
{
// 程式會被 hand 在此, 等待接收來自 Client 端傳來的資料
              IntAcceptData = SckSs[Scki].Receive(clientData);
// 往下就自己寫接收到來自Client端的資料後要做什麼事唄~^^”

          }
}
     catch
     {
// 這裡若出錯主要是來自 SckSs[Scki] 出問題, 可能是自己 Close, 也可能是Client 斷線, 自己加判斷吧~
     }
}

// Server 傳送資料給所有Client
private void SckSSend()
{
for (int Scki = 1; Scki <= SckSs.Length - 1; Scki++)
{
         if (null != SckSs [Scki] && SckSs [Scki].Connected == true)
{
try
              {
                   // SendS 在這裡為 string 型態, 為 Server 要傳給 Client 的字串
                   SckSs[Scki].Send(Encoding.ASCII.GetBytes(SendS));
}
              catch
{
                   // 這裡出錯, 主要是出在 SckSs[Scki] 出問題, 自己加判斷吧~
}
}
}
}

Client端:
Client 端連線 Sever 端的部份比 Server 端的聆聽來得簡單多了, 至於等待接收來自 Server 端的資料和傳送資料給 Server 端的部份幾乎和Server端是一樣的, 這部份就不重覆說明了。

Socket SckSPort; // 先行宣告Socket
string RmIp;
int SPort = 6101;

// 連線
private void ConnectServer()
{
try
     {
SckSPort = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
         SckSPort.Connect(new IPEndPoint(IPAddress.Parse(RmIp), SPort));
         // RmIp和SPort分別為string和int型態, 前者為Server端的IP, 後者為Server端的Port

// 同 Server 端一樣要另外開一個執行緒用來等待接收來自 Server 端傳來的資料, 與Server概念同
Thread SckSReceiveTd = new Thread(SckSReceiveProc);
         SckSReceiveTd.Start();
} catch { }
}
// 接受來自 Server 端的 SckSReceiveProc 函式內容幾乎同 Server 端, 自己寫吧;至於 Send 資料給 Server 端的部份也同 Server 端要 Send 給 Client 端一樣, 只差在Client 只有一個Socket. Good Luck!!  :)

refer to:

How to C# Chat Client

How to C# Chat Client

The Microsoft .NET framework provides two namespaces,System.Net and System.Net.Sockets for managed implementation of Internet protocols that applications can use to send or receive data over the Internet . The C# Chat Client here is a Windows based Application and its main function is to send message to the Chat Server.
In the previous section C# Multi Threaded Socket Program we saw a C# Multithreaded Server Socket Program communicate with more than one Client at the same time . If you don't know the basics of Socket programming , take a look at the section C# Socket Programming before you start this section.
The C# Chat Server Program has two sections.
2. Chat Client
csharp-chat-client
Chat Client
The Chat Client here is to connect the PORT 8888 of the C# Chat Server in " 127.0.0.1 " . Here we give " 127.0.0.1 " , because Chat Server and Chat Client are running on the same machine . When we start the Chat Client program , we have to enter a User Name for identifying the client in Server side . The Client program connect to the Chat Server and start a Thread for receive the messages from Server side client . Here we implement an infinite loop in the function getMessage() and call this function in a Thread .
Create a new C# Windows based project and put the source code in it.


using System;
using System.Windows.Forms;
using System.Text;
using System.Net.Sockets ;
using System.Threading;

namespace WindowsApplication2
{
    public partial class Form1 : Form
    {
        System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
        NetworkStream serverStream = default(NetworkStream);
        string readData = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            byte[] outStream = System.Text.Encoding.ASCII.GetBytes(textBox2.Text + "$");
            serverStream.Write(outStream, 0, outStream.Length);
            serverStream.Flush();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            readData = "Conected to Chat Server ...";
            msg();
            clientSocket.Connect("127.0.0.1", 8888);
            serverStream = clientSocket.GetStream();

            byte[] outStream = System.Text.Encoding.ASCII.GetBytes(textBox3.Text + "$");
            serverStream.Write(outStream, 0, outStream.Length);
            serverStream.Flush();

            Thread ctThread = new Thread(getMessage);
            ctThread.Start();
        }

        private void getMessage()
        {
            while (true)
            {
                serverStream = clientSocket.GetStream();
                int buffSize = 0;
                byte[] inStream = new byte[10025];
                buffSize = clientSocket.ReceiveBufferSize;
                serverStream.Read(inStream, 0, buffSize);
                string returndata = System.Text.Encoding.ASCII.GetString(inStream);
                readData = "" + returndata;
                msg();
            }
        }

        private void msg()
        {
            if (this.InvokeRequired)
                this.Invoke(new MethodInvoker(msg));
            else
                textBox1.Text = textBox1.Text + Environment.NewLine + " >> " + readData;
        } 

    }
}

The C# Chat Server Program has two sections. 
1. C# Chat Server 

2. Chat Client 

Refer to: http://csharp.net-informations.com/communications/csharp-chat-client.htm