1. 程式人生 > >一個基於TCP/IP的伺服器與客戶端通訊的小專案(超詳細版)

一個基於TCP/IP的伺服器與客戶端通訊的小專案(超詳細版)

1.目的:實現客戶端向伺服器傳送資料

  原理:

2.建立兩個控制檯應用,一個為伺服器,用於接收資料。一個為客戶端,用於傳送資料。

  關鍵類與對應方法:

  1)類IPEndPoint:

  1.是抽象類EndPoint的實現類

  2.Socket物件的RemoteEndPoint、 LocalEndPoint都是這個型別

  3.屬性Address: 使用IPv4表示的地址

  4.屬性Port:使用int表示的埠

  2)類Socket:

  這個類即可以用於作伺服器端的開發,又可以作客戶端的開發

  構造方法:

  引數 AddressFamily:指定使用IPv4的地址InterNetwork

  引數SocketType:指定使用流式傳輸Stream

  引數ProtocolType:指定協議型別Tcp

  1.方法Bind()E 繫結IP與埠,這樣就成為了伺服器,可以監聽指定IP的特定埠

  2.方法Listen(); 置於監聽狀態,引數是最大的掛起數

  3.方法Accept(): 接收客戶端連線,返回Socket物件, 這個方法會阻塞當前執行緒,建議開啟新執行緒執行些方法,結合尾遞迴,這樣就可以接收多個客戶端

  4.方法Receive(): 接收客戶端傳送過來的訊息,以位元組為單位進行操作,此方法會阻塞當前執行緒,建議開啟新執行緒執行此方法,結合尾遞迴,就可以持續接收多條資訊

  5. 方法Send(): 傳送訊息,以位元組為單位

3.具體實現

     其他內容不做過多解釋了,備註做的超詳細,應該只有笨笨的人才寫這麼多備註吧。。

  1.伺服器

     主函式:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ServerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 呼叫建構函式,使用Start方法
            ServerControl server = new ServerControl();
            server.Start();

            Console.ReadKey();
        }
    }
}

     ServerControl類:

using System;
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 ServerTest
{
    public class ServerControl
    {
        // 宣告變數(使用Socket需using System.Net.Sockets;)
        private Socket serverSocket;

        // 自定義有參構造方法(IP地址,流程傳輸方式,TCP協議)
        public ServerControl()
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        // 建立啟動方法(IPEndPoint用於指定地址及埠初始化,需using System.Net;)
        public void Start()
        {
            // 伺服器啟動
            // 繫結IP地址(為任意IP)與埠(設定為12345)
            serverSocket.Bind(new IPEndPoint(IPAddress.Any,12345));
            serverSocket.Listen(10);
            Console.WriteLine("伺服器啟動成功");

            // 開啟執行緒:目的實現伺服器和客戶端一對多連線
            Thread threadAccept = new Thread(Accept);
            threadAccept.IsBackground = true;
            threadAccept.Start();
        }
         // Accept方法測試:接收客戶端連線
        private void Accept()
        {
            // 接收客戶端方法,會掛起當前執行緒(.RemoteEndPoint表示遠端地址)
            Socket client = serverSocket.Accept();
            IPEndPoint point = client.RemoteEndPoint as IPEndPoint;
            Console.WriteLine(point.Address + "[" + point.Port + "] 連線成功!");

            // 開啟一個新執行緒執行緒,實現訊息多次接收
            Thread threadReceive = new Thread(Receive);
            threadReceive.IsBackground = true;
            threadReceive.Start(client);

            // 尾遞迴
            Accept();
        }

        // Receive方法的使用測試
        // 接收客戶端傳送過來的訊息,以位元組為單位進行操作
        // 該方法會阻塞當前執行緒,所以適合開啟新的執行緒使用該方法
        // Accept()中將Receive作為執行緒傳遞物件,所以要注意一點,使用執行緒傳遞物件只能是object型別的!!
        private void Receive(object obj)
        {
            // 將object型別強行轉換成socket
            Socket client = obj as Socket;

            IPEndPoint point = client.RemoteEndPoint as IPEndPoint;

            // 此處的異常丟擲主要針對客戶端異常的問題
            // 比如,客戶端關閉或者連線中斷
            // 程式會停留在int msgLen = client.Receive(msg);這段程式碼,而導致無法繼續往下走
            try
            {
                byte[] msg = new byte[1024];
                // 實際接收到位元組陣列長度,該方法會阻塞當前執行緒,即(client.Receive(msg)開始掛起)
                // 同時,這裡還是尾遞迴掛起處
                int msgLen = client.Receive(msg);
                // 將msg裝換成字串
                Console.WriteLine(point.Address + "[" + point.Port + "]:" + Encoding.UTF8.GetString(msg, 0, msgLen));
                // 此處實現伺服器自動向客戶端返回一條訊息
                // 因為Send傳送資訊是以位元組為單位傳送的
                // 所以下面(Encoding.UTF8.GetString(msg,0,msgLen)+"   yes.boy")這一塊是把這一部分均搞成string
                // 後使用Encoding.UTF8.GetBytes統一轉化成位元組傳遞
                // 這裡呢,已經實現伺服器向客戶端傳送訊息了,客戶端只需要receive一下,格式一轉就可視化了
                client.Send(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(msg,0,msgLen)+"   yes.boy"));
                // 尾遞迴實現多條訊息的接收;和while同理。
                Receive(client);
            }
            catch
            {
                Console.WriteLine(point.Address + "[" + point.Port + "]積極斷開");
            }
        }
    }
}

2.客戶端:

   主函式:

client.Connect("127.0.0.1",12345);
修改IP可實現不同計算機之間的連線。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClientTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 呼叫建構函式
            ClientControl client = new ClientControl();
            // 輸入本機IP與埠號
            client.Connect("127.0.0.1",12345);
            // 提示操作方法
            Console.WriteLine("請輸入傳送至伺服器的內容或者輸入quit退出");
            // 輸入內容
            string msg = Console.ReadLine();
            // 非退出情況下操作方式,使用while可以持續不斷的接收使用者輸入
            while(msg != "quit")
            {
                client.Send(msg);
                msg = Console.ReadLine();
            }

            Console.ReadKey();
        }
    }
}

   ClientControl類:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ClientTest
{
    public class ClientControl
    {
        // 宣告變數
        private Socket clientSocket;

        // 自定義有參構造方法((IP地址,流程傳輸方式,TCP協議))
        public ClientControl()
        {
            clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
        }

        // 建立通過IP與埠號連線的方法
        public void Connect(string ip,int port)
        {
            clientSocket.Connect(ip, port);
            Console.WriteLine("連線伺服器成功");

            // 客戶端接收伺服器訊息的執行緒
            Thread threadReceive = new Thread(Receive);
            threadReceive.IsBackground = true;
            threadReceive.Start();
        }

        // 用於測試伺服器向客戶端返回一條訊息
        private void Receive()
        {
            while(true)
            {
                try
                {
                    // 用於接收伺服器的回覆資訊
                    byte[] msg = new byte[1024];
                    int msgLen = clientSocket.Receive(msg);
                    Console.WriteLine("伺服器:"+Encoding.UTF8.GetString(msg,0,msgLen));
                }
                // 異常處理方法
                catch
                {
                    Console.WriteLine("伺服器積極拒絕!!");
                    // 退出while迴圈
                    break;
                }
            }
        }

        // Send方法測試:即傳送訊息,以位元組為單位
        public void Send(string msg)
        {
            // 將字元創傳化為位元組陣列
            clientSocket.Send(Encoding.UTF8.GetBytes(msg));
        }
    }
}

4.實現

本地連線:IP為127.0.0.1

遠端連線:因為我只有一臺電腦,所以用騰訊雲伺服器作為我的伺服器,我本地的PC作為客戶端,實現連線。

1.修改客戶端主程式裡面的IP為我的騰訊雲IP

2.使用遠端桌面連線

3.連線成功