1. 程式人生 > WINDOWS開發 >C# socket程式設計

C# socket程式設計

C#Socket程式設計

一、簡單瞭解服務端和客戶端各自的功能。

  首先應該清楚服務端(Server)和客戶端(Client)它們各自的功能。

  (1)服務端(Server):

    負責接收客戶端的請求,然後根據客戶端請求的內容不同而給客戶端返回相應的資料。

  (2)客戶端(Client):

    連結服務端,向服務端傳送自己的業務需求(也就是資料),然後接受服務端返回過來的資訊。

  (3)分析服務端和客戶端的功能,可以很清楚的知道,它們完成了資料之間的交流,或者說是業務之間的相互傳遞與獲取。

二、伺服器與客戶端之間資訊傳遞的橋樑(Socket)

  (1)伺服器和客戶端進行資訊傳遞的通道,socket套接字分為很多種型別,它是一個協議族,常用的協議TCP/IP和UDP兩種。

  (2)簡單來說就是通過socket協議能夠進行通訊,每種程式語言socket的寫法都八九不離十,建立socket通訊的步驟都十分接近。

  (3)服務端socket和客戶端socket通過對方的IP地址和對應應用程式的PORT(埠號)進行連線和資料傳輸。

三、C#中建立socket的一般方式以及大致業務流程

  (1)首先定義一個Socket套接字物件(這裡協議族的型別我們選擇TCP協議,TCP傳輸資料時安全、穩定、可靠,當然對應的效能比UDP差)

    

Socket socket_server = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

  (2)不管是客戶端還是服務端,建立socket套接字的方式都一樣,但是服務端(Server需要繫結該服務端的IP和埠號,以便讓客戶端進行連線),為了方便這裡我繫結的是本地的IP(LocalHost)

    

IPAddress iPAddress = IPAddress.Parse("127.0.0.1");

 

int port = 8888;

  (3)為服務端繫結IP和埠

socket_server.Bind(new IPEndPoint(iPAddress,port));

   (4) 繫結好IP和PORT後就開始監聽(將建立的套接字變為監聽套接字,以及設定最大同時監聽數量)

socket_server.Listen(100);

  (5)監聽完畢就可以等待客戶端的連線了(注意:如果沒有客戶端連線到來,那麼Accept()方法會一直處於阻塞狀態,會 阻塞當前執行緒,直到有連線到來,執行緒才會接阻塞,然後將客戶端的套接字儲存下來,以便接下來的資料傳輸使用。)

Socket clientSocket = socket_server.Accept();

  (6)接受客戶端的訊息(使用我們上一步儲存的客戶端套接字來接收)

//這裡接受訊息的方式是以位元組流來接收的,所有用一個byte型別的陣列來儲存,Receive方法還返回接受直接的實際長度,Encoding.UTF8.GetString()方法將byte型別的資料轉化為字串
byte[] strbyte = new byte[1024]; int count = clientsocket.Receive(strbyte); string ret = Encoding.UTF8.GetString(strbyte,count)

  (7)向客戶端傳送訊息(傳送訊息使用Send方法進行)

Console.WriteLine("請輸入要傳送的訊息:");
string str = Console.ReadLine();
byte[] strbyte = Encoding.UTF8.GetBytes(str);
clientsocket.Send(strbyte);

 四、完整的服務端程式碼如下:

技術分享圖片
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SocketServer
{
    /// <summary>
    /// 服務端
    /// </summary>
    class Serversocket
    {
        /// <summary>
        /// 服務端入口
        /// </summary>
        private static Serversocket socketserver;
        public static Serversocket Instance()
        {
            if (socketserver == null)
            {
                socketserver = new Serversocket();
            }
            return socketserver;
        }

        private Socket server;
        private IPAddress iPAddress;
        private int port;
        
        /// <summary>
        /// 初始化資料
        /// </summary>
        public void Init()
        {
            iPAddress = IPAddress.Parse("127.0.0.1");
            port = 8888;
            CreateSocket();
            BindAndListen();
            WaitClientConnection();
        }

        /// <summary>
        /// 1.建立服務端的套接字
        /// </summary>
        private void CreateSocket()
        {
            server = new Socket(AddressFamily.InterNetwork,ProtocolType.Tcp);
            
        }
        /// <summary>
        /// 2.繫結伺服器ip和埠
        /// </summary>
        private void BindAndListen()
        {
            server.Bind(new IPEndPoint(iPAddress,port));
            server.Listen(100);
        }

        /// <summary>
        /// 用一個列表儲存連線成功的客戶端
        /// </summary>
        private List<Socket> ConnectSockeList = new List<Socket>();
        /// <summary>
        /// 用一個字典儲存所有接受客戶端訊息以及傳送給客戶端訊息的執行緒
        /// </summary>
        private Dictionary<int,List<Thread>> recvThreadList = new Dictionary<int,List<Thread>>();

        /// <summary>
        /// 3.等待客戶端的連線
        /// </summary>
        private void WaitClientConnection()
        {
            int index = 1;
            while (true)
            {
                Console.WriteLine("當前連結數量:"+recvThreadList.Count);
                Console.WriteLine("等待客戶端的連結:");
                Socket ClientSocket = server.Accept();
                if (ClientSocket!=null)
                {
                    Console.WriteLine("{0}連線成功!",ClientSocket.RemoteEndPoint);
                    ConnectSockeList.Add(ClientSocket);
                    //建立接受客戶端訊息的執行緒,並將其啟動
                    Thread recv = new Thread(RecvMessage);
                    recv.Start(new ArrayList { index,ClientSocket });
                    Thread send = new Thread(SendMessage);
                    send.Start(new ArrayList { index,ClientSocket });
                    recvThreadList.Add(index,new List<Thread> { recv,send });
                    index++;
                }
            }
        }

        /// <summary>
        /// 接受客戶端的訊息
        /// </summary>
        /// <param name="clientsocket"></param>
        private void RecvMessage(object client_socket)
        {
            ArrayList arraylist = client_socket as ArrayList;
            int index = (int)arraylist[0];
            Socket clientsocket = arraylist[1] as Socket;
            while (true)
            {
                try
                {
                    byte[] strbyte = new byte[1024];
                    int count = clientsocket.Receive(strbyte);
                    string ret = Encoding.UTF8.GetString(strbyte,count);
                    Console.WriteLine("{0}給你傳送了訊息:{1}",clientsocket.RemoteEndPoint,ret);
                }
                catch (Exception)
                {
                    //客戶端離去時終止執行緒
                    Console.WriteLine("代號為:{0}的客戶端已經離去!",index);
                    recvThreadList[index][0].Abort();
                }
            }
        }

        /// <summary>
        /// 向客戶端返回訊息
        /// </summary>
        private void SendMessage(object client_socket)
        {
            ArrayList arraylist = client_socket as ArrayList;
            int index = (int)arraylist[0];
            Socket clientsocket = arraylist[1] as Socket;
            while (true)
            {
                try
                {
                    Console.WriteLine("請輸入要傳送的訊息:");
                    string str = Console.ReadLine();
                    byte[] strbyte = Encoding.UTF8.GetBytes(str);
                    clientsocket.Send(strbyte);
                }
                catch (Exception)
                {

                    Console.WriteLine("代號為:{0}的客戶端已經離去!訊息傳送失敗!");
                    recvThreadList[index][1].Abort();
                    recvThreadList.Remove(index);
                }
            }
        }
    }
}
技術分享圖片

啟動伺服器的程式碼:

技術分享圖片
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SocketServer
{
    class Program
    {
        static void Main(string[] args)
        {
            Serversocket.Instance().Init();
            Console.ReadKey();
        }
    }
}
技術分享圖片

啟動後的執行效果:

技術分享圖片

五、上面的服務端程式碼允許多個客戶端同時訪問,而且能對不同的客戶端分開處理主要應用到了C#中的Thead模組

  (1)建立一條執行緒

 Thread t1 = new Thread(對應要執行的函式)
//Thread方法有四個過載,常用的三個
public Thread(ThreadStart start);//接受一個無引數的且無返回值的委託
public Thread(ParameterizedThreadStart start);//接受一個帶object型別引數的且無返回值的委託
public Thread(ThreadStart start,int maxStackSize);//接受一個無引數的且無返回值的委託,可以指定執行緒最大堆疊大小

   (2)啟動一條執行緒

t1.start()//可以指定object型別的引數

(3)終止一條執行緒

t1.Abort()

  (4)如果不用多執行緒去處理單個客戶端的訊息傳送和接受,那麼當有多個客戶端連線時,如果其中一個客戶端發生阻塞,那麼後面的所有客戶端都將阻塞。

六、簡單客戶端程式碼:

技術分享圖片
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Socket_Client_02
{
    /// <summary>
    /// 客戶端
    /// </summary>
    class Socket_Client
    {
        public Socket_Client(string ip,int port)
        {
            this.iPAddress = IPAddress.Parse(ip);
            this.port = port;
        }

        /// <summary>
        /// 套接字
        /// </summary>
        private Socket client_socket;
        /// <summary>
        /// 客戶端要連線的ip地址
        /// </summary>
        private IPAddress iPAddress;
        /// <summary>
        /// 客戶端要連線的埠好
        /// </summary>
        private int port;
        /// <summary>
        /// 建立客戶端連線的套接字
        /// </summary>
        /// <returns></returns>
        public Socket Create_Client_Socket()
        {
            return new Socket(AddressFamily.InterNetwork,ProtocolType.Tcp);
        }
        /// <summary>
        /// 連線伺服器
        /// </summary>
        public void Connect_Server()
        {
            client_socket = Create_Client_Socket();
            //tcp連線伺服器的時候只需要連線一次,因為tcp是長連結
            client_socket.Connect(new IPEndPoint(iPAddress,port));
        }

        /// <summary>
        /// 接收來自伺服器的訊息
        /// </summary>
        public void Recv_Msg_By_Client()
        {
            while (true)
            {
                byte[] ser_msg = new byte[1024];
                int count = client_socket.Receive(ser_msg);
                string str_msg = Encoding.UTF8.GetString(ser_msg,count);
                if (count > 0)
                {
                    Console.WriteLine("接收到來自{0}的訊息為:{1}",client_socket.RemoteEndPoint,str_msg);
                }
            }
        }

        /// <summary>
        /// 向伺服器傳送請求
        /// </summary>
        public void Request_Client()
        {
            while (true)
            {
                Console.WriteLine("請輸入你要傳送到伺服器的訊息:");
                string send_msg = Console.ReadLine();
                byte[] by_msg = Encoding.UTF8.GetBytes(send_msg);
                client_socket.Send(by_msg);
            }
        }
    }
}
技術分享圖片

客戶端啟動程式碼:

技術分享圖片
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Socket_Client_02
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("請輸入你要連線的伺服器的ip地址:");
            string ip = Console.ReadLine();
            Console.WriteLine("請輸入你要連線的伺服器的埠號:");
            int port = int.Parse(Console.ReadLine());

            //建立套接字
            Socket_Client s = new Socket_Client(ip,port);
            //連線伺服器
            s.Connect_Server();
            //接收伺服器的訊息
            Thread recv = new Thread(s.Recv_Msg_By_Client);
            //給伺服器傳送訊息
            Thread send = new Thread(s.Request_Client);
            recv.Start();
            send.Start();

            recv.Join();
            send.Join();
            Console.ReadKey();
        }
    }
}
技術分享圖片

執行效果圖:

技術分享圖片

先開啟服務端,然後用客戶端去連線,連線之後就能相互聊天了。