0004-異步編程分包

如果我們要處理客戶端的每一條消息,針對與上面的粘包的問題,我們無法處理消息的涧尿,我們不知道消息到底有多長(雖說我們的案例是一樣的,但實際開發(fā)中可能是不一樣的)檬贰,那么我們就要涉及到分包的問題了姑廉。

在上一篇的文章中我們可以看到一篇文章被接受了多次才接受完,加入客戶端一下發(fā)了多篇文章翁涤?那么我們怎么才能把每一篇文章分出來呢桥言?這就涉及到了分包。
我們在客戶端每次發(fā)消息的時候我們都要將發(fā)送信息的長度加到消息的前面迷雪。由于TCP消息都是按順序到達的限书,那么我們在讀消息的時候,先讀出消息長度(len)章咧,然后從消息長度后開始讀取len個字節(jié),那么這len個字節(jié)就是我們要的消息能真。
對前面的代碼進行修改如下:

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

namespace ASYNServer
{
    class Server
    {
        private TcpListener tcpListener;
        byte[] buffer = new byte[512];
        /// <summary>
        /// 接受消息
        /// </summary>
        List<byte> receiveDataList = new List<byte>();
        public Server()
        {
            tcpListener = new TcpListener(new IPEndPoint(IPAddress.Parse("127.0.0.1"),10001));
            //開啟偵聽赁严,最多只能連接20個客戶端數(shù)目
            tcpListener.Start(20);
            tcpListener.BeginAcceptSocket(ClientConnect, tcpListener);
        }
        /// <summary>
        /// 客戶端連接
        /// </summary>
        /// <param name="ar"></param>
        private void ClientConnect(IAsyncResult ar)
        {
            try
            {
                TcpListener listener = (TcpListener)ar.AsyncState;
                Socket client = listener.EndAcceptSocket(ar);
                Console.WriteLine(client.RemoteEndPoint.ToString() + "連接成功");
                //偵聽結(jié)束后開始接收數(shù)據(jù)
                ReceiveData(client);
                //重新偵聽是否有新的客戶端連接
                //如果這里不寫  那么只能偵聽一次
                tcpListener.BeginAcceptSocket(ClientConnect, tcpListener);
            }
            catch (Exception e)
            {

                Console.WriteLine("ClientConnect"+e.Message);
            }
        }
        /// <summary>
        /// 接受數(shù)據(jù)的回調(diào)
        /// </summary>
        /// <param name="client"></param>
        private  void ReceiveData(Socket client)
        {
            SocketError socketError;
            client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, out socketError, ReceiveCallback, client);
        }
        /// <summary>
        /// 一次接收的數(shù)據(jù)的長度
        /// </summary>
        int receLen = 0;
        private void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                SocketError socketError;
                Socket client = (Socket)ar.AsyncState;
                receLen = client.EndReceive(ar, out socketError);
                if (receLen>0)
                {
                    lock (receiveDataList)
                    {
                        //處理接收到的數(shù)據(jù)

                        OnReceiveData(client);
                    }
                    
                }else//當receLen<=0的時候表示客戶端斷開和服務(wù)器的連接
                {
                    Console.WriteLine(client.LocalEndPoint.ToString()+"關(guān)閉了");
                }
                //重新接受數(shù)據(jù)
                ReceiveData(client);
            }
            catch (Exception e)
            {
                Console.WriteLine("ReceiveCallback:" + e.Message + "   "+e.StackTrace);
            }
        }
        /// <summary>
        /// 接受數(shù)據(jù)
        /// </summary>
        /// <param name="socket"></param>
        private void OnReceiveData(Socket socket)
        {
            
            byte[] tempArr = new byte[receLen];
            Array.Copy(buffer, tempArr, receLen);
            receiveDataList.AddRange(tempArr);

            if (receiveDataList.Count>=4)
            {
                byte[] dataArray = receiveDataList.GetRange(0, 4).ToArray();
                int len = BitConverter.ToInt32(dataArray, 0);
                
                if (receiveDataList.Count>=len+4)
                {
                    receiveDataList.RemoveRange(0, 4);
                    dataArray = receiveDataList.GetRange(0, len).ToArray();
                    string message = Encoding.UTF8.GetString(dataArray, 0, len);
                    receiveDataList.RemoveRange(0, len);
                    Console.WriteLine("來自客戶端的數(shù)據(jù)是:" + message);
                    Console.WriteLine();
                }
            }
        }
        /// <summary>
        /// 發(fā)送數(shù)據(jù)
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="data"></param>
        public void SendData(Socket socket, byte[] data)
        {
            SocketError socketError;
            socket.BeginSend(data, 0, data.Length, SocketFlags.None, out socketError, SendCallBack, socket);//異步發(fā)送數(shù)據(jù)
        }
        /// <summary>
        /// 發(fā)送數(shù)據(jù)的回調(diào)
        /// </summary>
        /// <param name="ar"></param>
        private void SendCallBack(IAsyncResult ar)
        {
            SocketError socketError;
            Socket client = (Socket)ar.AsyncState;
            client.EndSend(ar, out socketError);
        }
    }
}

客戶端代碼:

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

namespace ASYNClient
{
    class Client
    {
        private TcpClient tcpclient = null;
        byte[] result;
        public Client()
        {
            tcpclient = new TcpClient();
            //連接服務(wù)器
            tcpclient.BeginConnect(IPAddress.Parse("127.0.0.1"), 10001, ConnectCallback, tcpclient);
        }
        /// <summary>
        /// 連接回調(diào)
        /// </summary>
        /// <param name="ar"></param>
        private void ConnectCallback(IAsyncResult ar)
        {
            TcpClient client = (TcpClient)ar.AsyncState;
            try
            {
                if (client.Connected)
                {
                    //連接成功
                    client.EndConnect(ar);
                    //開始接受數(shù)據(jù)
                    ReceiveData(client);

                }
            }
            catch (Exception e)
            {
                Console.WriteLine("ConnectCallback:"+e.Message);
            }
        }
        /// <summary>
        /// 接收數(shù)據(jù)
        /// </summary>
        /// <param name="client"></param>
        private void ReceiveData(TcpClient client)
        {
            NetworkStream stream = client.GetStream();

            result = new byte[client.Available];
          
            try
            {
                stream.BeginRead(result, 0, result.Length, ReadCallBack, stream);
            }
            catch (Exception e)
            {
                Console.WriteLine("ReceiveData:"+e.Message );
            }
        }
        /// <summary>
        /// 接受消息回調(diào)
        /// </summary>
        /// <param name="ar"></param>
        private void ReadCallBack(IAsyncResult ar)
        {
            NetworkStream stream;
            try
            {
                stream = (NetworkStream)ar.AsyncState;
                stream.EndRead(ar);
            }
            catch (IOException e)
            {
                Console.WriteLine("遠程服務(wù)器關(guān)閉");
                
            }
           
        }
        /// <summary>
        /// 發(fā)送消息
        /// </summary>
        /// <param name="data"></param>
        public void SendData(byte[] data)
        {
            if (tcpclient.Connected)
            {
                NetworkStream stream = tcpclient.GetStream();
                stream.BeginWrite(data,0,data.Length, SendCallback, stream);

            }
        }
        /// <summary>
        /// 發(fā)送消息回調(diào)
        /// </summary>
        /// <param name="ar"></param>
        private void SendCallback(IAsyncResult ar)
        {
            NetworkStream stream = (NetworkStream)ar.AsyncState;
            stream.EndWrite(ar);
        }
    }
}

客戶端測試代碼:

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

namespace ASYNClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Client client = new Client();
            //等待客戶端連接服務(wù)器后我們按下回車鍵模擬客戶端一次性發(fā)送多條數(shù)據(jù)
            Console.ReadLine();
            for (int i = 0; i < 3; i++)
            {
                //string message = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
                string message = "燕子去了扰柠,有再來的時候;楊柳枯了疼约,有再青的時候卤档;桃花謝了,有再開的時 候程剥。但是劝枣,聰明的,你告訴我织鲸,我們的日子為什么一去不復返呢舔腾?——是有人偷了他 們罷:那是誰?又藏在何處呢搂擦?是他們自己逃走了罷——如今又到了哪里呢稳诚?我不知道他們給了我多少日子,但我的手確乎(1)是漸漸空虛(2)了瀑踢。在默默里算著扳还,八千多日子已經(jīng)從我手中溜去,像針尖上一滴水滴在大海里橱夭,我的日子滴在時間的流里氨距,沒有聲音,也沒有影子棘劣。我不禁頭涔涔(3)而淚潸潸(4)了衔蹲。去的盡管去了,來的盡管來著呈础;去來的中間舆驶,又怎樣地匆匆呢?早上我起來的時候而钞,小屋里射進兩三方斜斜的太陽沙廉。太陽他有腳啊,輕輕悄悄地挪移了臼节;我也茫茫然跟著旋轉(zhuǎn)撬陵。于是——洗手的時候,日子從水盆里過去网缝;吃飯的時候巨税,日子從飯碗里過去;默默時粉臊,便從凝然的雙眼前過去草添。我覺察他去的匆匆了,伸出手遮挽時扼仲,他又從遮挽著的手邊過去远寸,天黑時抄淑,我躺在床上,他便伶伶俐俐(5)地從我身上跨過驰后,從我腳邊飛去了肆资。等我睜開眼和太陽再見,這算又溜走了一日灶芝。我掩著面嘆息郑原。但是新來的日子的影兒又開始在嘆息里閃過了。在逃去如飛的日子里夜涕,在千門萬戶的世界里的我能做些什么呢犯犁?只有徘徊(6)罷了(7),只有匆匆罷了钠乏;在八千多日的匆匆里栖秕,除徘徊外,又剩些什么呢晓避?過去的日子如輕煙簇捍,被微風吹散了,如薄霧俏拱,被初陽蒸融了暑塑;我留著些什么痕跡呢?我何曾留著像游絲(8)樣的痕跡呢锅必?我赤裸裸來到這世界事格,轉(zhuǎn)眼間也將赤裸裸的回去罷?但不能平的搞隐,為什么偏要白白走這一遭熬杂蕖?你聰明的劣纲,告訴我逢捺,我們的日子為什么一去不復返呢?"; 
                byte[] messageData = Encoding.UTF8.GetBytes(message);
                Console.WriteLine("messageData:"+ messageData.Length);
                byte[] lenData = BitConverter.GetBytes(messageData.Length);
                byte[] data = new byte[lenData.Length+messageData.Length];
                Array.Copy(lenData, data, lenData.Length);
                Array.Copy(messageData,0,data,lenData.Length,messageData.Length);
                client.SendData(data);
            }
            Console.ReadLine();
        }
    }
}
測試結(jié)果
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末癞季,一起剝皮案震驚了整個濱河市劫瞳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绷柒,老刑警劉巖志于,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異废睦,居然都是意外死亡伺绽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來憔恳,“玉大人瓤荔,你說我怎么就攤上這事净蚤≡孔椋” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵今瀑,是天一觀的道長程梦。 經(jīng)常有香客問我,道長橘荠,這世上最難降的妖魔是什么屿附? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮哥童,結(jié)果婚禮上挺份,老公的妹妹穿的比我還像新娘。我一直安慰自己贮懈,他們只是感情好匀泊,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著朵你,像睡著了一般各聘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抡医,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天躲因,我揣著相機與錄音,去河邊找鬼忌傻。 笑死大脉,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的水孩。 我是一名探鬼主播镰矿,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼荷愕!你這毒婦竟也來了衡怀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤安疗,失蹤者是張志新(化名)和其女友劉穎抛杨,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荐类,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡怖现,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屈嗤。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡潘拨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饶号,到底是詐尸還是另有隱情铁追,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布茫船,位于F島的核電站琅束,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏算谈。R本人自食惡果不足惜涩禀,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望然眼。 院中可真熱鬧艾船,春花似錦、人聲如沸高每。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽觉义。三九已至雁社,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晒骇,已是汗流浹背霉撵。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洪囤,地道東北人徒坡。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像瘤缩,于是被迫代替她去往敵國和親喇完。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 計算機網(wǎng)絡(luò)概述 網(wǎng)絡(luò)編程的實質(zhì)就是兩個(或多個)設(shè)備(例如計算機)之間的數(shù)據(jù)傳輸剥啤。 按照計算機網(wǎng)絡(luò)的定義锦溪,通過一定...
    蛋炒飯_By閱讀 1,227評論 0 10
  • 經(jīng)常想王柯上課時是什么樣子的,坐的正嗎府怯?能認真聽課嗎刻诊?老師提問題能主動舉手回答嗎?好多的問題都會在腦子里出現(xiàn)牺丙。今天...
    王柯媽媽閱讀 189評論 8 6
  • 深夜適合思考则涯。 人們總在夜深人靜的時候复局,輾轉(zhuǎn)難眠,腦袋里充滿各種天馬行空的幻想粟判。 是的亿昏,我就是睡不著。 如今煩心的...
    小陳物語閱讀 200評論 0 3
  • 今天就二十歲了呀。 真是一個浪漫的開始呢事秀。 我想彤断,二十歲往后的日子野舶, 應(yīng)該會越來越浪漫吧易迹。 十九歲發(fā)生了太多太多跟...
    屁桃三三閱讀 446評論 3 6