1. 程式人生 > >學習C#高階程式設計之Socket程式設計

學習C#高階程式設計之Socket程式設計

一個簡單的伺服器端和客戶端程式

伺服器端

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

namespace _026_socket程式設計_TCP協議
{
    class Program
    {
        static void Main(string[] args)
        {
            //1.建立socket
            Socket tcpServer = new Socket
                (AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
            //對應屬性分別是內網,流資料以及Tcp協議
            //2.繫結ip和埠號   172.16.1.102
            IPAddress ipaddress = new IPAddress(new byte[] {172,16,1,102});//獲取ip地址
            EndPoint point = new IPEndPoint(ipaddress,7788);//IPEndPoint是對ip+埠做了一次封裝的類
            tcpServer.Bind(point);//向作業系統申請一個可用的ip跟埠號 用來做通訊
            //3.開始監聽(等待客戶端連線)
            tcpServer.Listen(100);//引數是最大連線數
            Console.WriteLine("開始監聽");

            Socket clientSocket = tcpServer.Accept();//暫停當前執行緒,直到有一個客戶端連線過來,之後進行下面的程式碼
            //使用返回的socket跟客戶端做通訊

            Console.WriteLine("一個客戶端連線過來了");
            string message = "hello 歡迎你";
            byte[] data = Encoding.UTF8.GetBytes(message);//對字串做編碼,得到一個字串的位元組陣列
            clientSocket.Send(data);

            Console.WriteLine("向客戶端傳送了一條資料");
            byte[] data2 = new byte[1024];//建立一個位元組陣列用來當做容器,去承接客戶端傳送過來的資料
            int length = clientSocket.Receive(data2);
            string message2 = Encoding.UTF8.GetString(data2, 0, length);//把位元組資料轉換成一個字串
            Console.WriteLine("接收到了一個從客戶端傳送來的訊息:" + message2);

            Console.ReadKey();
        }
    }
}

客戶端

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

namespace _001_socket程式設計_tcp協議_客戶端
{
    class Program
    {
        static void Main(string[] args)
        {
            //1.建立socket
            Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //2.發起建立連線的請求
            IPAddress ipaddress = IPAddress.Parse("172.16.1.102");//可以把一個字串的IP地址轉化成一個ipaddress的
            //物件
            EndPoint point = new IPEndPoint(ipaddress,7788); 
            tcpClient.Connect(point);//通過ip:埠號  定位一個要連線到的伺服器端

            byte[] data = new byte[1024];
            int length = tcpClient.Receive(data);//這裡傳遞一個byte陣列,實際上這個data陣列用來接收資料
            //length返回值表示接收了多少位元組的資料
            string message =  Encoding.UTF8.GetString(data,0,length);//只把接收到的資料做一個轉化
            Console.WriteLine(message);

            //向伺服器端傳送訊息
            string message2 = Console.ReadLine();//讀取使用者的輸入 把輸入傳送到伺服器端
            tcpClient.Send(Encoding.UTF8.GetBytes(message2));//把字串轉化成位元組陣列,然後傳送到伺服器端


            Console.ReadKey();
        }
    }
}

伺服器端結果

客戶端結果

聊天室_socket_tcp伺服器端

Client.cs用來和客戶端做通訊,處理客戶端來的訊息

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

namespace _027_聊天室_socket_tcp伺服器端
{
    //用來和客戶端做通訊
    class Client
    {
        private Socket clientSocket;
        private Thread t;
        private byte[] data = new byte[1024];//資料容器

        public Client(Socket s)
        {
            clientSocket = s;
            //啟動一個執行緒 處理客戶端的資料接收
            Thread t = new Thread(ReceiveMessage);
            t.Start();
        }

        private void ReceiveMessage()
        {
            //一直接收客戶端的資料
            while (true)
            {
                //在接收資料之前 判斷一下socket連結是否斷開
                if(clientSocket.Poll(10, SelectMode.SelectRead))
                {
                    clientSocket.Close();
                    break;//跳出迴圈 終止執行緒的執行
                }
                int length = clientSocket.Receive(data);
                string message = Encoding.UTF8.GetString(data, 0, length);
                //接收到資料的時候, 要把這個資料分發到客戶端
                //廣播這個訊息
                Program.BroadcastMessage(message);
                Console.WriteLine("收到了訊息:" + message);
            }
        }

        public void SendMessage(string message)
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            clientSocket.Send(data);
        }

        public bool Connected
        {
            get
            {
                return clientSocket.Connected;
            }
        }
    }
}

Program.cs

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

namespace _027_聊天室_socket_tcp伺服器端
{
    class Program
    {
        static List<Client> clientList = new List<Client>();
        //廣播訊息
        public static void BroadcastMessage(string message)
        {
            var notConnectedList = new List<Client>();
            foreach (var client in clientList)
            {
                if(client.Connected)
                    client.SendMessage(message);
                else
                {
                    notConnectedList.Add(client);
                }
            }
            foreach(var temp in notConnectedList)
            {
                clientList.Remove(temp);
            }
        }
        static void Main(string[] args)
        {
            Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
                ProtocolType.Tcp);
            tcpServer.Bind(new IPEndPoint(IPAddress.Parse("192.168.184.1"), 7788));
            tcpServer.Listen(100);
            Console.WriteLine("Server running...");

            while (true)
            {
                Socket clientSocket = tcpServer.Accept();
                Console.WriteLine("a client is connected!");
                Client client = new Client(clientSocket);//把與每個客戶端通訊的邏輯(收發訊息)
                //放到client物件裡面進行處理
                clientList.Add(client);
            }
        }
    }
}

客戶端使用unity,介面如下:

核心程式碼:

using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;

public class ChatManager : MonoBehaviour {

    public string ipaddress = "192.168.184.1";
    public int port = 7788;
    public UIInput textInput;
    public UILabel chatLabel;

    private Socket clientSocket;
    private Thread t;
    private byte[] data = new byte[1024];//資料容器
    private string message = "";//訊息容器
    // Use this for initialization
    void Start () {
        ConnectToServer();
	}
	
	// Update is called once per frame
	void Update () {
        if (message != null && message != "")
        {
            chatLabel.text += "\n" + message;
            message = "";//清空訊息
        }
	}

    void ConnectToServer()
    {
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //跟伺服器端建立連線
        clientSocket.Connect(new IPEndPoint(IPAddress.Parse(ipaddress), port));

        //建立一個新的執行緒 用來接收訊息
        t = new Thread(ReceiveMessage);
        t.Start();
    }
    
    //這個執行緒方法用來迴圈接收方法
    void ReceiveMessage()
    {
        while (true)
        {
            if (clientSocket.Connected == false)
                break;
            int length = clientSocket.Receive(data);
            message = Encoding.UTF8.GetString(data, 0, length);
            //chatLabel.text += "\n" + message;

        }
        
    }

    void SendMessage(string message)
    {
        byte[] data = Encoding.UTF8.GetBytes(message);
        clientSocket.Send(data);
    }

    public void OnSendButtonClick()
    {
        string value = textInput.value;
        SendMessage(value);
        textInput.value = "";
    }

    void OnDestroy()
    {
        clientSocket.Shutdown(SocketShutdown.Both);//關閉:既不接受,也不傳送
        clientSocket.Close();//關閉連結
    }
}

結果展示如下:

UDP協議網路程式設計

伺服器端

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 _028_socket程式設計_UDP協議_伺服器端
{
    class Program
    {
        static Socket udpServer;
        static void Main(string[] args)
        {
            udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            udpServer.Bind(new IPEndPoint(IPAddress.Parse("172.16.1.102"), 7788));

            new Thread(ReceiveMessage) {IsBackground = true }.Start();

            //udpServer.Close();
            Console.ReadKey();
        }


        static void ReceiveMessage()
        {
            //接收資料
            while (true)
            {
                EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = new byte[1024];
                int length = udpServer.ReceiveFrom(data, ref remoteEndPoint);//這個方法會把資料的來源(ip:port)放到第二個引數上
                string message = Encoding.UTF8.GetString(data, 0, length);
                Console.WriteLine("從ip:" + (remoteEndPoint as IPEndPoint).Address.ToString() + ":" +
                    (remoteEndPoint as IPEndPoint).Port + "收到了資料:" + message);
            }
        }
    }
}

客戶端

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

namespace _002_socket程式設計_udp協議_客戶端
{
    class Program
    {
        static void Main(string[] args)
        {
            //建立socket
            Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            //傳送資料
            while (true)
            {
                EndPoint serverPoint = new IPEndPoint(IPAddress.Parse("172.16.1.102"), 7788);
                string message = Console.ReadLine();
                byte[] data = Encoding.UTF8.GetBytes(message);
                udpClient.SendTo(data, serverPoint);
            }
           
            Console.ReadKey();    
        }
    }
}

TCP與UDP的區別:

1.基於連線與無連線;

2.對系統資源的要求(TCP較多,UDP少);

3.UDP程式結構較簡單;

4.流模式與資料報模式 ;

5.TCP保證資料正確性,UDP可能丟包,TCP保證資料順序,UDP不保證。

TcpListener和TcpClient:兩個封裝類

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

namespace _029_tcplistener
{
    class Program
    {
        static void Main(string[] args)
        {
            //1.TcpListener對socket進行了封裝,這個類裡面自己會去建立socket物件
            TcpListener listener = new TcpListener(IPAddress.Parse("172.16.1.102"),7788);
            //2.開始進行監聽
            listener.Start();//開始進行監聽

            //3.等待客戶端連線過來
            TcpClient client = listener.AcceptTcpClient();
            //4.取得客戶端傳送過來的資料
            NetworkStream stream = client.GetStream();//得到了一個網路流 從這個網路流可以取得客戶端傳送過來的資料
            while (true)
            {
                byte[] data = new byte[1024];//建立一個數據容器,用來承接資料
                int length = stream.Read(data, 0, 1024);//讀取資料
                //0表示從陣列的哪個索引開始存放資料
                //1024表示最大讀取的位元組數
                string message = Encoding.UTF8.GetString(data, 0, length);
                Console.WriteLine("收到了訊息:" + message);

            }
            
           
            stream.Close();
            client.Close();
            listener.Stop();
            Console.ReadKey();
        }
    }
}

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

namespace tcpclient
{
    class Program
    {
        static void Main(string[] args)
        {
            //當我們建立tcpclient物件的時候,就會跟server去建立連線
            TcpClient client = new TcpClient("172.16.1.102",7788);
            NetworkStream stream = client.GetStream();
            //read用來讀寫資料,write用來寫入資料就是傳送資料
            while (true)
            {
                string message = Console.ReadLine();
                byte[] data = Encoding.UTF8.GetBytes(message);
                stream.Write(data, 0, data.Length);
            }
            

            stream.Close();
            client.Close();
            Console.ReadKey();
        }
    }
}

UdpClient

伺服器端

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

namespace _030_UdpClient
{
    class Program
    {
        static void Main(string[] args)
        {
            //建立udpclient 繫結ip跟埠號
            UdpClient udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse("172.16.1.102"),7788));

            //接收資料
            IPEndPoint point = new IPEndPoint(IPAddress.Any, 0);
            while (true)
            {
                byte[] data = udpClient.Receive(ref point);//確定point確定資料來自哪個IP的哪個埠號,返回的是一個位元組陣列,就是我們的資料
                string message = Encoding.UTF8.GetString(data);
                Console.WriteLine("收到了訊息:" + message);
            }
          

            udpClient.Close();
            Console.ReadKey();
        }
    }
}

客戶端

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

namespace _004_udpclient
{
    class Program
    {
        static void Main(string[] args)
        {
            //建立udpclient物件
            UdpClient client = new UdpClient();

            while (true)
            {
                string message = Console.ReadLine();
                byte[] data = Encoding.UTF8.GetBytes(message);
                client.Send(data, data.Length, new IPEndPoint(IPAddress.Parse("172.16.1.102"), 7788));
            }
            client.Close();
            Console.ReadKey();
        }
    }
}

結果