一個基於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.連接成功


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM