C#Socket客戶端和服務器端編寫

最近在做個多人遊戲的demo,試試自己編寫服務器端,在寫之前,就不介紹Tcp和Udp的區別了,網上資料很多,在看socket編程之前最好先去看看基礎的Tcp和Udp的理論內容,再去看Socket編程會好很多。

C#的socket編程主要用到一下命名空間:

  1. System.Net;//socket的重要基礎
  2.  System.Net.Sockets;//socket的重要基礎
  3. System.Text;//因爲socket只能發送byte數組,也只能接收byte數組,需要把接收到的byte數組轉化爲字符串
  4. System.Threading;//主要用來開啓一個負責監聽和接收消息的線程

socket的常用類和方法

  相關類:

  1.    IPAddress:包含了一個IP地址
  2.    IPEndPoint:包含了一對IP地址和端口號

相關方法 

  1.    Socket():創建一個Socket
  2.    Bind():綁定一個本地的IP和端口號(IPEndPoint)
  3.    Listen():讓Socket偵聽傳入的連接吃那個病,並指定偵聽隊列容量
  4.    Connect():初始化與另一個Socket的連接
  5.    Accept():接收連接並返回一個新的Socket
  6.    Send():輸出數據到Socket,只能發送一個byte數組
  7.    Receive():從Socket中讀取數據,返回接收到的數據的長度
  8.    Close():關閉Socket,銷燬連接

 客戶端和服務器端結構如圖

客戶端與服務器結構圖

       

 

通過上圖,我們可以看出,首先服務器會創建一個負責監聽的socket,然後客戶端通過socket連接到服務器指定端口,最後服務器端負責監聽的socket,監聽到客戶端有連接過來了,就創建一個負責和客戶端通信的socket。

下面我們來看下Socket更具體的通信過程:

Socket的通訊過程

  服務器端:

    01,申請一個socket

    02,綁定到一個IP地址和一個端口上

    03,開啓偵聽,等待接收連接

  客戶端:

    01,申請一個socket

   02,連接服務器(指明IP地址和端口號)

   服務器端接收到連接請求後,產生一個新的socket(端口大於1024)與客戶端建立連接並進行通信,原監聽socket繼續監聽。

  注意:負責通信的Socket不能無限創建,創建的數量和操作系統有關。

Socket的構造函數

    Public Socket(AddressFamily addressFamily,SocketType  socketType,ProtocolType  protocolTYpe)

    AddressFamily:指定Socket用來解析地址的尋址方案。例如:InterNetWork指示當Socket使用一個IP版本4地址連接

   SocketType:定義要打開的Socket的類型

   Socket類使用ProtocolType枚舉向Windows  Sockets  API通知所請求的協議

示例

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

/*

注意如果addr是指定ip地址,那麼就只能在同一個ip下進行通信,如果作爲服務器端的話addr必須是IpAddress.any,

表示任意ip都可以在指定端口下訪問服務器

*/

IPEndPoint endp = new IPEndPoint(addr,,9000);

         服務端先綁定:serverWelcomeSocket.Bind(endp)

         客戶端再連接:clientSocket.Connect(endp)

注意

   1,一個Socket一次只能連接一臺主機

   2,Socket關閉後無法再次使用

   3,每個Socket對象只能與一臺遠程主機連接。如果你想連接到多臺遠程主機,你必須創建多個Socket對象。

 

服務器端代碼

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

namespace Server {
    class Program {

        //記錄通信用的scoket的字典
        static Dictionary<string, Socket> sockets = new Dictionary<string, Socket> ();
        static void Main (string[] args) {
            //設置ip和端口
            IPAddress ipAddress = IPAddress.Any;
            int port = 30001;
            //綁定本機的IP和端口
            IPEndPoint endPoint = new IPEndPoint (ipAddress, port);
            //新建socket
            Socket socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //綁定服務器的監聽Socket
            try {
                socket.Bind (endPoint);
                //設置最大連接數
                socket.Listen (10); //如果是0表示不限制連接數
                ShowMessage ("開始監聽");

                // 後臺線程用於監聽來連接上來的客戶端
                Thread t = new Thread (AcceptInfo);
                t.IsBackground = true;
                t.Start (socket);

            } catch (Exception e) {
                // 打印錯誤信息
                ShowMessage (e.Message);
            }

            Console.ReadKey ();
        }

        //打印信息
        static void ShowMessage (string msg) {
            Console.WriteLine (msg);
        }

        //創建鏈接成功後用於通信的Socket
        static void AcceptInfo (Object o) {
            Socket socket = o as Socket;

            while (true) {

                try {
                    //通信用的socket,根據連接過來的scoket返回一個新的用於通信的socket
                    Socket tSocket = socket.Accept ();
                    string point = tSocket.RemoteEndPoint.ToString ();
                    //把通信socket加入字典
                    sockets.Add (point, tSocket);
                    ShowMessage (point + " 連接成功");
                    //接收客戶端的信息
                    Thread t = new Thread (ReceiveMsg);
                    t.IsBackground = true;
                    t.Start (tSocket);

                } catch (Exception e) {
                    ShowMessage (e.Message);
                    break;
                }
            }

        }

        static void ReceiveMsg (Object o) {
            Socket client = o as Socket;

            while (true) {
                try {
                    byte[] buffer = new byte[1024 * 1024];
                    // 獲得實際接受的數據的長度
                    int n = client.Receive (buffer);
                    // 如果是一個有內容的請求
                    if (n > 0) {
                        GetRequestByCode (buffer[0], buffer, n);
                    }
                    // string words = Encoding.UTF8.GetString(buffer,0,n);
                    // ShowMessage(client.RemoteEndPoint.ToString() + ":" + words);    

                } catch (Exception e) {
                    ShowMessage (e.Message);
                    break;
                }

            }
        }

        /// <summary>
        /// 根據請求的code來獲得對應的請求,即buffer[0] = code
        /// </summary>
        /// <param name="code">請求的code</param>
        /// <param name="buffer">請求的內容</param>
        /// <param name="length">實際請求內容的長度</param>
        static void GetRequestByCode (byte code, byte[] buffer, int length) {
            switch (code) {
                case 0:
                    GetRequestContent (buffer, length);
                    break;
                case 1:
                    GetRequestContent (buffer, length);
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 負責獲取請求的內容
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="length"></param>
        static void GetRequestContent (byte[] buffer, int length) {
            //因爲指定了buffer[0]是請求的code區分是哪個請求,請求內容從1開始
            string words = Encoding.UTF8.GetString (buffer, 1, length);
            Console.WriteLine (words);
        }

    }
}

客戶端代碼

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

namespace Client {
    class Program {
        static Socket client = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        static void Main (string[] args) {
            IPAddress ip = IPAddress.Parse ("39.107.234.74");
            IPEndPoint point = new IPEndPoint (ip, 30001);

            //開始連接到服務器
            try {
                client.Connect (point);
                ShowMessage ("服務器" + client.RemoteEndPoint.ToString ());
                ShowMessage ("客戶端" + client.LocalEndPoint.ToString ());
                SendMessage (client, 0, "我是" + client.LocalEndPoint.ToString () + "請求0過來了");
                // SendMessage(client,1,"我是" + client.LocalEndPoint.ToString() + "請求1過來了");
            } catch (Exception e) {
                ShowMessage (e.Message);
            }

            Console.ReadKey ();
        }

        static void ShowMessage (string msg) {
            Console.WriteLine (msg);
        }

        /// <summary>
        /// 發送請求(code存在byte[]的0,content存在byte[]0後面的空間裏)
        /// </summary>
        /// <param name="client">發送請求的socket</param>
        /// <param name="requestCode">請求的編碼</param>
        /// <param name="context">請求的內容</param>
        static void SendMessage (Socket client, byte requestCode, string context) {
            if (client != null && client.Connected) {
                try {
                    byte[] buffer = Encoding.UTF8.GetBytes (context);
                    //新的請求格式buffer[0]是請求的編碼,buffe後面的內容是請求的內容
                    List<byte> list = new List<byte> ();
                    list.Add (requestCode);
                    list.AddRange (buffer);
                    byte[] newbuffer = list.ToArray ();
                    client.Send (newbuffer);
                } catch (Exception e) {

                    ShowMessage (e.Message);
                }
            }
        }
    }
}

至此結束。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章