• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

[C#网络编程系列]专题五:TCP编程

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

前言

 

前面专题的例子都是基于应用层上的HTTP协议的介绍, 现在本专题来介绍下传输层协议——TCP协议,主要介绍下TCP协议的工作过程和基于TCP协议的一个简单的通信程序,下面就开始本专题的正文了。

 

一、TCP的工作过程

首先TCP是一种面向连接的,可靠的,基于字节流的传输层通信协议。TCP的工作过程可以分为三个阶段:一、连接的建立;  二、传输数据; 三、断开连接,下面就对这三个过程分别介绍下:

1.1 连接的建立

TCP的连接建立就像打电话一样, 我们打电话时,拨一个号码的号码并不是立即就可以接通的,期间会有一个“嘟 嘟”的呼叫过程, 这就好比是TCP协议的连接的建立阶段。当我们用TCP编写的程序,必须先建立TCP连接。TCP协议的连接建立通过三次握手来完成的,下面是在网上找的一张TCP三次握手的图片:

 

下面就对这三次握手简单的介绍:

第一次握手:建立连接时,客户端发送SYN包(seq=x)到服务器,并进入SYN_Send状态,等待服务器确认

第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=x+1),同时自己也发送一个SYN包(SEQ=y),即SYN+ACK包,此时服务器进入SYN_Recv状态

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=y+1),此包发送完毕,客户端和服务器进入Established(建立)状态,完成三次握手。

简单理解三次握手就是发送一个检验包给对方然后互相确认,双方都接到确认的一个信号时,这时候双方就建立了连接(就像我们打电话时,如果没人说话时就会说下 “喂”,说这句“喂” 也就是希望得到对方的一个确认,虽然这里双方已经建立了连接的,这里只是更形象的说明下三次握手的过程)。

1.2 传输数据

双方建立了连接,即在双方建立了一个通信通道(就像一座桥一样,在两端建立了一个通路,用桥来比喻通信通道主要是因为最近有一则新闻:哈尔滨阳明滩大桥坍塌事件),建立连接之后,当然是传输我们需要传输的数据到对方的,这里就开始简单介绍下传输数据的过程。

利用TCP传输数据时,数据是以字节流的形式进行传输,客户端与服务器端建立连接后,发送方需要先将发送的数据转换为字节流,然后将其发送给对方,发送数据时,可以通过程序不断地将数据流陆续写入TCP的发送缓冲中,然后TCP自动从发送缓冲中提取一定量的数据,将其组成TCP报文段发送到IP层,再通过IP层(也就是网络层)之下的网络接口发送出去;接受端从IP层接收到TCP报文段后,将其暂时保存在接受缓冲中,然后我们通过程序依次读取接受缓冲中的数据,从而达到相互通信的目的(简单的说就发送方把数据转换为数据流,再把数据流存储在发送缓冲中,然后传输层低层的协议从发送缓冲中读取数据把数据发送出去,然后接收端从底层接受到数据把数据存储在接收端的缓冲中,然后我们写的程序只是从缓冲中依次读取数据,然后显示出来,在客户端我们写代码做的事情是把数据写入Write写入发送端的缓冲中,然后服务器端(接收端)用Read方法在自己的缓冲中读取数据,用一句话概括,TCP的传输就是对数据的写——读操作)括号中的内容只是我个人理解,因为这样我感觉理解起来比较容易,对于刚开始接触TCP的朋友可以这样理解,然后再一句句话去扩展。

1.3 断开连接

发送完数据之后,最后就是断开连接了,下面是网上断开的连接的一张图片(断开一个连接需要经过四次握手):

 

TCP的工作过程就分为上面三个过程,TCP编程是作为上层应用编程的基础,就像之前专题中基于HTTP协议的Web服务器,Web浏览器,其传输层都用的是TCP协议进行传输的,还有基于FTP(文件传输协议),IMAP(交互式邮件存取协议) POP3(邮局协议的第3个版本) 和SMTP(简单邮件传输协议)的网络应用其传输层都用的是TCP协议,而不是UDP等其他传输层协议。

 

 

二、基于TCP协议的简单通信程序

这里简单实现了一个客户端与服务器间的通信程序,核心代码为:

客户端连接服务器端代码:

  1. private void btnConnect_Click(object sender, EventArgs e)  
  2.         {  
  3.             // 通过一个线程发起请求,多线程  
  4.             Thread connectThread = new Thread(ConnectToServer);  
  5.             connectThread.Start();  
  6.         }  
  7.   
  8.         // 连接服务器方法,建立连接的过程  
  9.         private void ConnectToServer()  
  10.         {  
  11.             try  
  12.             {  
  13.                 // 调用委托  
  14.                 statusStripInfo.Invoke(showStatusCallBack, "正在连接...");  
  15.                 if (tbxserverIp.Text == string.Empty || tbxPort.Text == string.Empty)  
  16.                 {  
  17.                     MessageBox.Show("请先输入服务器的IP地址和端口号");  
  18.                 }  
  19.   
  20.                 IPAddress ipaddress = IPAddress.Parse(tbxserverIp.Text);  
  21.                 tcpClient = new TcpClient();               
  22.                 tcpClient.Connect(ipaddress, int.Parse(tbxPort.Text));                 
  23.                   
  24.                 // 延时操作  
  25.                 Thread.Sleep(1000);  
  26.                 if (tcpClient != null)  
  27.                 {  
  28.                     statusStripInfo.Invoke(showStatusCallBack, "连接成功");  
  29.                     networkStream = tcpClient.GetStream();  
  30.                     reader = new BinaryReader(networkStream);  
  31.                     writer =new BinaryWriter(networkStream);  
  32.                 }  
  33.                    
  34.   
  35.             }  
  36.             catch  
  37.             {  
  38.                 statusStripInfo.Invoke(showStatusCallBack,"连接失败");  
  39.                 Thread.Sleep(1000);  
  40.                 statusStripInfo.Invoke(showStatusCallBack,"就绪");  
  41.             }  
  42.         }  
private void btnConnect_Click(object sender, EventArgs e)
        {
            // 通过一个线程发起请求,多线程
            Thread connectThread = new Thread(ConnectToServer);
            connectThread.Start();
        }

        // 连接服务器方法,建立连接的过程
        private void ConnectToServer()
        {
            try
            {
                // 调用委托
                statusStripInfo.Invoke(showStatusCallBack, "正在连接...");
                if (tbxserverIp.Text == string.Empty || tbxPort.Text == string.Empty)
                {
                    MessageBox.Show("请先输入服务器的IP地址和端口号");
                }

                IPAddress ipaddress = IPAddress.Parse(tbxserverIp.Text);
                tcpClient = new TcpClient();             
                tcpClient.Connect(ipaddress, int.Parse(tbxPort.Text));               
                
                // 延时操作
                Thread.Sleep(1000);
                if (tcpClient != null)
                {
                    statusStripInfo.Invoke(showStatusCallBack, "连接成功");
                    networkStream = tcpClient.GetStream();
                    reader = new BinaryReader(networkStream);
                    writer =new BinaryWriter(networkStream);
                }
                 

            }
            catch
            {
                statusStripInfo.Invoke(showStatusCallBack,"连接失败");
                Thread.Sleep(1000);
                statusStripInfo.Invoke(showStatusCallBack,"就绪");
            }
        }

客户端发送消息的代码:

 

  1. // 发送消息  
  2.         private void btnSend_Click(object sender, EventArgs e)  
  3.         {  
  4.             Thread sendThread = new Thread(SendMessage);  
  5.             sendThread.Start(tbxMessage.Text);  
  6.         }  
  7.   
  8.         private void SendMessage(object state)  
  9.         {  
  10.             statusStripInfo.Invoke(showStatusCallBack, "正在发送...");  
  11.             try  
  12.             {  
  13.                 writer.Write(state.ToString());  
  14.                 Thread.Sleep(5000);  
  15.                 writer.Flush();  
  16.                 statusStripInfo.Invoke(showStatusCallBack, "完毕");  
  17.   
  18.                 tbxMessage.Invoke(resetMessageCallBack, null);  
  19.                 lstbxMessageView.Invoke(showMessageCallback, state.ToString());  
  20.             }  
  21.             catch  
  22.             {  
  23.                 if (reader != null)  
  24.                 {  
  25.                     reader.Close();  
  26.                 }  
  27.                 if (writer != null)  
  28.                 {  
  29.                     writer.Close();  
  30.                 }  
  31.                 if (tcpClient != null)  
  32.                 {  
  33.                     tcpClient.Close();  
  34.                 }  
  35.   
  36.                 statusStripInfo.Invoke(showStatusCallBack, "断开了连接");  
  37.             }  
  38.         }  
// 发送消息
        private void btnSend_Click(object sender, EventArgs e)
        {
            Thread sendThread = new Thread(SendMessage);
            sendThread.Start(tbxMessage.Text);
        }

        private void SendMessage(object state)
        {
            statusStripInfo.Invoke(showStatusCallBack, "正在发送...");
            try
            {
                writer.Write(state.ToString());
                Thread.Sleep(5000);
                writer.Flush();
                statusStripInfo.Invoke(showStatusCallBack, "完毕");

                tbxMessage.Invoke(resetMessageCallBack, null);
                lstbxMessageView.Invoke(showMessageCallback, state.ToString());
            }
            catch
            {
                if (reader != null)
                {
                    reader.Close();
                }
                if (writer != null)
                {
                    writer.Close();
                }
                if (tcpClient != null)
                {
                    tcpClient.Close();
                }

                statusStripInfo.Invoke(showStatusCallBack, "断开了连接");
            }
        }

服务器端接受开始监听客户端请求的代码:

 

  1. // 开始监听  
  2.         private void btnStart_Click(object sender, EventArgs e)  
  3.         {  
  4.              
  5.             tcpLister = new TcpListener(ipaddress,Port);  
  6.             tcpLister.Start();  
  7.             // 启动一个线程来接受请求  
  8.             Thread acceptThread =new Thread(acceptClientConnect);  
  9.             acceptThread.Start();  
  10.         }  
  11.   
  12.         // 接受请求  
  13.         private void acceptClientConnect()  
  14.         {  
  15.             statusStripInfo.Invoke(showStatusCallBack,"正在监听");  
  16.             Thread.Sleep(1000);  
  17.             try  
  18.             {  
  19.                 statusStripInfo.Invoke(showStatusCallBack,"等待连接");  
  20.                 tcpClient = tcpLister.AcceptTcpClient();  
  21.                 if (tcpLister != null)  
  22.                 {  
  23.                     statusStripInfo.Invoke(showStatusCallBack,"接受到连接");  
  24.                     networkStream = tcpClient.GetStream();  
  25.                     reader = new BinaryReader(networkStream);  
  26.                     writer = new BinaryWriter(networkStream);  
  27.                 }  
  28.             }  
  29.             catch  
  30.             {  
  31.                 statusStripInfo.Invoke(showStatusCallBack, "停止监听");  
  32.                 Thread.Sleep(1000);  
  33.                 statusStripInfo.Invoke(showStatusCallBack, "就绪");  
  34.             }  
  35.         }  
// 开始监听
        private void btnStart_Click(object sender, EventArgs e)
        {
           
            tcpLister = new TcpListener(ipaddress,Port);
            tcpLister.Start();
            // 启动一个线程来接受请求
            Thread acceptThread =new Thread(acceptClientConnect);
            acceptThread.Start();
        }

        // 接受请求
        private void acceptClientConnect()
        {
            statusStripInfo.Invoke(showStatusCallBack,"正在监听");
            Thread.Sleep(1000);
            try
            {
                statusStripInfo.Invoke(showStatusCallBack,"等待连接");
                tcpClient = tcpLister.AcceptTcpClient();
                if (tcpLister != null)
                {
                    statusStripInfo.Invoke(showStatusCallBack,"接受到连接");
                    networkStream = tcpClient.GetStream();
                    reader = new BinaryReader(networkStream);
                    writer = new BinaryWriter(networkStream);
                }
            }
            catch
            {
                statusStripInfo.Invoke(showStatusCallBack, "停止监听");
                Thread.Sleep(1000);
                statusStripInfo.Invoke(showStatusCallBack, "就绪");
            }
        }

 

现在看看运行的结果:
首先先启动服务器然后点开始监听,此时线程会堵塞,直到接受到一个连接请求位置

然后运行客户端,在IP地址和端口处输入服务器端的IP地址和端口号,点击连接服务器按钮后的界面如下:

通过接受按钮和发送按钮来实现双方的通信,实现界面如下:

 

三、总结

到这里本专题的内容将的差不多了, 本专题主要介绍了基于TCP协议工作过程和在net平台下自定义了一个简单通信的程序,希望本专题可以给那些初次接触TCP协议的朋友一些帮助,(大牛们应该直接可以闪过的),在后面的专题我将和大家分享UDP编程,讲完UDP编程后将结合这两章的内容实现一个类似QQ的即时聊天的工具,希望这些对大家有帮助,如果大家有任何问题和有感兴趣的专题需要了解的,可以给我留言,在之后的文章都会和大家来分享。

 

 

觉得看了后有帮助的朋友麻烦推荐下,也给我继续下去的动力,如果大家有什么感兴趣的专题也可以留言告诉我,我会通过学习后也会相继和大家分享。

下面是本程序源代码:TCP编程


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap