你得学会并且学得会的Socket编程基础知识

2023-11-16

        这一篇文章,我将图文并茂地介绍Socket编程的基础知识,我相信,如果你按照步骤做完实验,一定可以对Socket编程有更好地理解。

        本文源代码,可以通过这里下载 http://files.cnblogs.com/chenxizhang/SocketWorkshop.rar

第一步:创建解决方案

image

 第二步:创建服务端程序

        这里可以选择“Console Application”这个类型,比较方便调试

image

然后编写如下代码,实现服务器的基本功能

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

//额外导入的两个命名空间
using System.Net.Sockets;
using System.Net;

namespace SocketServer
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //将该socket绑定到主机上面的某个端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.bind.aspx
            socket.Bind(new IPEndPoint(IPAddress.Any, 4530));

            //启动监听,并且设置一个最大的队列长度
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.listen(v=VS.100).aspx
            socket.Listen(4);

            Console.WriteLine("Server is ready!");
            Console.Read();
        }
    }
}

        现在可以启动调试一下看看效果如何,正常情况下应该会看到一个提示,因为我们需要在TCP 4530端口进行监听,所以防火墙会有提示。

image

点击“Allow access”

image

        这样,我们的服务器就可以开始监听了。但是这有什么用呢?是的,没有什么用。

        我们还需要为服务器添加一些功能,例如接受传入的请求,给客户端发送消息,或者从客户端接收消息等等

第三步:接受传入的请求

我们需要通过Accept,或者(BeginAccept)来接受传入的请求。

请注意下面代码中的红色部分

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//额外导入的两个命名空间
using System.Net.Sockets;
using System.Net;

namespace SocketServer
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //将该socket绑定到主机上面的某个端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.bind.aspx
            socket.Bind(new IPEndPoint(IPAddress.Any, 4530));

            //启动监听,并且设置一个最大的队列长度
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.listen(v=VS.100).aspx
            socket.Listen(4);

            //开始接受客户端连接请求
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginaccept.aspx
            socket.BeginAccept(new AsyncCallback((ar) =>
            {
                //这就是客户端的Socket实例,我们后续可以将其保存起来
                var client = socket.EndAccept(ar);

                //给客户端发送一个欢迎消息
                client.Send(Encoding.Unicode.GetBytes("Hi there, I accept you request at "+DateTime.Now.ToString()));
            }), null);

            Console.WriteLine("Server is ready!");
            Console.Read();
        }
    }
}

            socket.BeginAccept(new AsyncCallback((ar) =>
            {
                //这就是客户端的Socket实例,我们后续可以将其保存起来
                var client = socket.EndAccept(ar);

                //给客户端发送一个欢迎消息
                client.Send(Encoding.Unicode.GetBytes("Hi there, I accept you request at "+DateTime.Now.ToString()));
            }), null);

        wow,看起来不错对吧,我们赶紧做一个客户端来测试一下吧

 

第四步:创建客户端

我们还是使用一个Console Application

image

添加如下的代码,并且创建客户端连接

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

//导入的命名空间
using System.Net.Sockets;

namespace SocketClient
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //连接到指定服务器的指定端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connect.aspx
            socket.Connect("localhost", 4530);


            Console.WriteLine("connect to the server");
            Console.Read();

        }
    }
}

依次选择SocketServer和SocketClient这两个项目,分别将其启动为调试状态(右键菜单,Debug=>Start new instance)

image

我们看到两个程序都工作正常。

但是,在客户端怎么没有收到服务器发过来的消息呢?那是因为,我们没有在客户端提供这方面的功能。

 

第五步:在客户端中实现接受消息的方法

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

//导入的命名空间
using System.Net.Sockets;

namespace SocketClient
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //连接到指定服务器的指定端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connect.aspx
            socket.Connect("localhost", 4530);

            //实现接受消息的方法
            var buffer = new byte[1024];//设置一个缓冲区,用来保存数据
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginreceive.aspx
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback((ar) =>
            {
                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);
                //读取出来消息内容
                var message = Encoding.Unicode.GetString(buffer, 0, length);
                //显示消息
                Console.WriteLine(message);

            }), null);

            Console.WriteLine("connect to the server");
            Console.Read();

        }
    }
}

请注意以上红色的部分,我们用了BeginReceive方法进行异步的消息侦听,如果收到了,我们就打印出来

        请注意以上红色的部分,我们用了BeginReceive方法进行异步的消息侦听,如果收到了,我们就打印出来。

            //实现接受消息的方法
            var buffer = new byte[1024];//设置一个缓冲区,用来保存数据
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginreceive.aspx
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback((ar) =>
            {
                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);
                //读取出来消息内容
                var message = Encoding.Unicode.GetString(buffer, 0, length);
                //显示消息
                Console.WriteLine(message);

            }), null);

 

image

看起来已经实现了我们需求了:服务器给客户端发了一个消息,而客户端也已经收到了。

但是,这远远不够,因为它们之间的通讯不仅仅是一次性的,那么如果服务器要不断地给客户端发消息,例如每隔两秒钟就发送一个消息,如何实现呢?

 

第六步:实现服务器定期向客户端发消息

服务器代码:

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

//额外导入的两个命名空间
using System.Net.Sockets;
using System.Net;

namespace SocketServer
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //将该socket绑定到主机上面的某个端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.bind.aspx
            socket.Bind(new IPEndPoint(IPAddress.Any, 4530));

            //启动监听,并且设置一个最大的队列长度
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.listen(v=VS.100).aspx
            socket.Listen(4);

            //开始接受客户端连接请求
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginaccept.aspx
            socket.BeginAccept(new AsyncCallback((ar) =>
            {
                //这就是客户端的Socket实例,我们后续可以将其保存起来
                var client = socket.EndAccept(ar);

                //给客户端发送一个欢迎消息
                client.Send(Encoding.Unicode.GetBytes("Hi there, I accept you request at "+DateTime.Now.ToString()));


                //实现每隔两秒钟给服务器发一个消息
                //这里我们使用了一个定时器
                var timer = new System.Timers.Timer();
                timer.Interval = 2000D;
                timer.Enabled = true;
                timer.Elapsed += (o, a) =>
                {
                    client.Send(Encoding.Unicode.GetBytes("Message from server at " +DateTime.Now.ToString()));
                };
                timer.Start();

            }), null);


            Console.WriteLine("Server is ready!");
            Console.Read();
        }
    }
}

我们还要实现在客户端一直监听消息的机制,而不是一次性接收.请注意下面红色的部分

客户端代码:

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

//导入的命名空间
using System.Net.Sockets;

namespace SocketClient
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //连接到指定服务器的指定端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connect.aspx
            socket.Connect("localhost", 4530);
            Console.WriteLine("connect to the server");

            //实现接受消息的方法
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginreceive.aspx
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage),socket);

            Console.Read();
        }


        static byte[] buffer = new byte[1024];
        public static void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                var socket = ar.AsyncState as Socket;

                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);
                //读取出来消息内容
                var message = Encoding.Unicode.GetString(buffer, 0, length);
                //显示消息
                Console.WriteLine(message);

                //接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch(Exception ex){
                Console.WriteLine(ex.Message);
            }
        }
    }
}

socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage),socket);

 

        public static void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                var socket = ar.AsyncState as Socket;

                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);
                //读取出来消息内容
                var message = Encoding.Unicode.GetString(buffer, 0, length);
                //显示消息
                Console.WriteLine(message);

                //接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch(Exception ex){
                Console.WriteLine(ex.Message);
            }
        }

重新调试起来,看起来的效果如下图所示

image

我们继续做下面的实验,一步一步地研究Socket通讯中可能遇到的一些问题

 

请先关闭掉客户端这个程序,而不要关闭服务端程序,这时会发现一个错误

image

这个错误很容易理解,因为客户端已经关闭,也就是客户端那个Socket已经不存在了,服务器还继续向它发送消息当然会出错。所以,从可靠性方面的考虑,我们必须在发送消息之前检测Socket的活动状态

 

第七步:检测客户端的活动状态

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

//额外导入的两个命名空间
using System.Net.Sockets;
using System.Net;

namespace SocketServer
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //将该socket绑定到主机上面的某个端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.bind.aspx
            socket.Bind(new IPEndPoint(IPAddress.Any, 4530));

            //启动监听,并且设置一个最大的队列长度
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.listen(v=VS.100).aspx
            socket.Listen(4);

            //开始接受客户端连接请求
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginaccept.aspx
            socket.BeginAccept(new AsyncCallback((ar) =>
            {
                //这就是客户端的Socket实例,我们后续可以将其保存起来
                var client = socket.EndAccept(ar);

                //给客户端发送一个欢迎消息
                client.Send(Encoding.Unicode.GetBytes("Hi there, I accept you request at "+DateTime.Now.ToString()));


                //实现每隔两秒钟给服务器发一个消息
                //这里我们使用了一个定时器
                var timer = new System.Timers.Timer();
                timer.Interval = 2000D;
                timer.Enabled = true;
                timer.Elapsed += (o, a) =>
                {
                    //检测客户端Socket的状态
                    if(client.Connected)
                    {
                        try
                        {
                            client.Send(Encoding.Unicode.GetBytes("Message from server at " + DateTime.Now.ToString()));
                        }
                        catch(SocketException ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                    }
                    else
                    {
                        timer.Stop();
                        timer.Enabled = false;
                        Console.WriteLine("Client is disconnected, the timer is stop.");
                    }
                };
                timer.Start();

            }), null);


            Console.WriteLine("Server is ready!");
            Console.Read();
        }
    }
}

注意下面代码中红色的部分 :

                     //检测客户端Socket的状态

                    if(client.Connected)
                    {
                        try
                        {
                            client.Send(Encoding.Unicode.GetBytes("Message from server at " + DateTime.Now.ToString()));
                        }
                        catch(SocketException ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                    }
                    else
                    {
                        timer.Stop();
                        timer.Enabled = false;
                        Console.WriteLine("Client is disconnected, the timer is stop.");
                    }

        上面代码的逻辑很清楚,但有时候还是会触发那个SocketException。为什么呢?这是因为我们的Timer是每隔两秒钟检查一次,那么就很可能有一种情况,我们检查的时候,它还是连接状态,消息发出去之后,它断开了。这种情况肯定是存在的。所以要用Try..catch的结构

        目前我们实现的场景很简单,服务器只管发消息,客户端只管收消息。但实际工作中,可能希望服务器和客户端都能收发消息。请看下一节

 

第八步:实现双向收发消息

先看服务端的修改

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

//额外导入的两个命名空间
using System.Net.Sockets;
using System.Net;

namespace SocketServer
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //将该socket绑定到主机上面的某个端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.bind.aspx
            socket.Bind(new IPEndPoint(IPAddress.Any, 4530));

            //启动监听,并且设置一个最大的队列长度
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.listen(v=VS.100).aspx
            socket.Listen(4);

            //开始接受客户端连接请求
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginaccept.aspx
            socket.BeginAccept(new AsyncCallback((ar) =>
            {
                //这就是客户端的Socket实例,我们后续可以将其保存起来
                var client = socket.EndAccept(ar);

                //给客户端发送一个欢迎消息
                client.Send(Encoding.Unicode.GetBytes("Hi there, I accept you request at "+DateTime.Now.ToString()));


                //实现每隔两秒钟给服务器发一个消息
                //这里我们使用了一个定时器
                var timer = new System.Timers.Timer();
                timer.Interval = 2000D;
                timer.Enabled = true;
                timer.Elapsed += (o, a) =>
                {
                    //检测客户端Socket的状态
                    if(client.Connected)
                    {
                        try
                        {
                            client.Send(Encoding.Unicode.GetBytes("Message from server at " + DateTime.Now.ToString()));
                        }
                        catch(SocketException ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                    }
                    else
                    {
                        timer.Stop();
                        timer.Enabled = false;
                        Console.WriteLine("Client is disconnected, the timer is stop.");
                    }
                };
                timer.Start();


                //接收客户端的消息(这个和在客户端实现的方式是一样的)
                client.BeginReceive(buffer,0,buffer.Length,SocketFlags.None,new AsyncCallback(ReceiveMessage),client);

            }), null);


            Console.WriteLine("Server is ready!");
            Console.Read();
        }

        static byte[] buffer = new byte[1024];

        public static void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                var socket = ar.AsyncState as Socket;

                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);
                //读取出来消息内容
                var message = Encoding.Unicode.GetString(buffer, 0, length);
                //显示消息
                Console.WriteLine(message);

                //接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch(Exception ex){
                Console.WriteLine(ex.Message);
            }
        }
    }
}

注意红色的代码。

                //给客户端发送一个欢迎消息
                client.Send(Encoding.Unicode.GetBytes("Hi there, I accept you request at "+DateTime.Now.ToString()));

 

        static byte[] buffer = new byte[1024];

        public static void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                var socket = ar.AsyncState as Socket;

                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);
                //读取出来消息内容
                var message = Encoding.Unicode.GetString(buffer, 0, length);
                //显示消息
                Console.WriteLine(message);

                //接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch(Exception ex){
                Console.WriteLine(ex.Message);
            }

        }

可以看出来,为了让服务器可以接受消息,其实并不需要什么特别的设计,与客户端接受消息其实可以是一样的

 

再来看看客户端的修改

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

//导入的命名空间
using System.Net.Sockets;

namespace SocketClient
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //连接到指定服务器的指定端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connect.aspx
            socket.Connect("localhost", 4530);
            Console.WriteLine("connect to the server");

            //实现接受消息的方法

            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginreceive.aspx
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);

            //接受用户输入,将消息发送给服务器端
            while(true)
            {
                var message = "Message from client : " + Console.ReadLine();
                var outputBuffer = Encoding.Unicode.GetBytes(message);
                socket.BeginSend(outputBuffer, 0, outputBuffer.Length, SocketFlags.None, null, null);
            }

        }


        static byte[] buffer = new byte[1024];

        public static void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                var socket = ar.AsyncState as Socket;

                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);
                //读取出来消息内容
                var message = Encoding.Unicode.GetString(buffer, 0, length);
                //显示消息
                Console.WriteLine(message);

                //接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

            //接受用户输入,将消息发送给服务器端
            while(true)
            {
                var message = "Message from client : " + Console.ReadLine();
                var outputBuffer = Encoding.Unicode.GetBytes(message);
                socket.BeginSend(outputBuffer, 0, outputBuffer.Length, SocketFlags.None, null, null);
            }

 

我在这里做了一个死循环,用户可以不断地输入,这些消息会被发送给服务器。如下图所示

image

【备注】因为服务器每隔两秒钟会发送新消息过来,所以在输入的时候,动作要稍快一点啦

本文最后探讨一个问题,就是如何让我们的服务器可以支持多个客户端

 

第九步:支持多个客户端

这个步骤只需要修改服务端程序即可

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

//额外导入的两个命名空间
using System.Net.Sockets;
using System.Net;

namespace SocketServer
{
    class Program
    {
        /// <summary>
        /// Socket Server 演示
        /// 作者:陈希章
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //将该socket绑定到主机上面的某个端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.bind.aspx
            socket.Bind(new IPEndPoint(IPAddress.Any, 4530));

            //启动监听,并且设置一个最大的队列长度
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.listen(v=VS.100).aspx
            socket.Listen(4);

            //开始接受客户端连接请求
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginaccept.aspx
            socket.BeginAccept(new AsyncCallback(ClientAccepted), socket);


            Console.WriteLine("Server is ready!");
            Console.Read();
        }


        public static void ClientAccepted(IAsyncResult ar)
        {

            var socket = ar.AsyncState as Socket;

            //这就是客户端的Socket实例,我们后续可以将其保存起来
            var client = socket.EndAccept(ar);

            //给客户端发送一个欢迎消息
            client.Send(Encoding.Unicode.GetBytes("Hi there, I accept you request at " + DateTime.Now.ToString()));


            //实现每隔两秒钟给服务器发一个消息
            //这里我们使用了一个定时器
            var timer = new System.Timers.Timer();
            timer.Interval = 2000D;
            timer.Enabled = true;
            timer.Elapsed += (o, a) =>
            {
                //检测客户端Socket的状态
                if(client.Connected)
                {
                    try
                    {
                        client.Send(Encoding.Unicode.GetBytes("Message from server at " + DateTime.Now.ToString()));
                    }
                    catch(SocketException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
                else
                {
                    timer.Stop();
                    timer.Enabled = false;
                    Console.WriteLine("Client is disconnected, the timer is stop.");
                }
            };
            timer.Start();


            //接收客户端的消息(这个和在客户端实现的方式是一样的)
            client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), client);

            //准备接受下一个客户端请求
            socket.BeginAccept(new AsyncCallback(ClientAccepted), socket);
        }

        static byte[] buffer = new byte[1024];

        public static void ReceiveMessage(IAsyncResult ar)
        {

            try
            {
                var socket = ar.AsyncState as Socket;

                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);
                //读取出来消息内容
                var message = Encoding.Unicode.GetString(buffer, 0, length);
                //显示消息
                Console.WriteLine(message);

                //接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch(Exception ex){
                Console.WriteLine(ex.Message);
            }
        }
    }
}

注意红色的代码:

            //开始接受客户端连接请求
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginaccept.aspx
            socket.BeginAccept(new AsyncCallback(ClientAccepted), socket);

 

        public static void ClientAccepted(IAsyncResult ar)
        {

            var socket = ar.AsyncState as Socket;

            //这就是客户端的Socket实例,我们后续可以将其保存起来
            var client = socket.EndAccept(ar);

            //给客户端发送一个欢迎消息
            client.Send(Encoding.Unicode.GetBytes("Hi there, I accept you request at " + DateTime.Now.ToString()));


            //实现每隔两秒钟给服务器发一个消息
            //这里我们使用了一个定时器
            var timer = new System.Timers.Timer();
            timer.Interval = 2000D;
            timer.Enabled =
true;
            timer.Elapsed += (o, a) =>
            {

                //检测客户端Socket的状态
                if(client.Connected)
                {
                    try
                    {
                        client.Send(Encoding.Unicode.GetBytes("Message from server at " + DateTime.Now.ToString()));
                    }
                   
catch(SocketException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }

                else
                {
                    timer.Stop();
                    timer.Enabled =
false;
                    Console.WriteLine("Client is disconnected, the timer is stop.");
                }

            };
            timer.Start();

            //接收客户端的消息(这个和在客户端实现的方式是一样的)
            client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), client);

            //准备接受下一个客户端请求
            socket.BeginAccept(new AsyncCallback(ClientAccepted), socket);
        }

 

最后调试起来看到的效果如下图

image

本文源代码,可以通过这里下载 http://files.cnblogs.com/chenxizhang/SocketWorkshop.rar

陈希章 《Office 365开发入门指南》作者

分类: Microsoft .NET

https://www.cnblogs.com/chenxizhang/archive/2011/09/10/2172994.html

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

你得学会并且学得会的Socket编程基础知识 的相关文章

  • 关于 clock_gettime() 的一个问题以及解决方法

    在新的2 6x内核上 编译使用这个函数的程序的时候 会发现 如果 gcc lpthead 无法链接成功 原因在于 libpthread so中没有这个函数的实现 但是 libpthread a中有 还有一个librt so librt a中
  • 堆和栈的通俗解释【转】

    数据结构的栈和堆 首先在数据结构上要知道堆栈 尽管我们这么称呼它 但实际上堆栈是两种数据结构 堆和栈 堆和栈都是一种数据项按序排列的数据结构 栈就像装数据的桶或箱子 我们先从大家比较熟悉的栈说起吧 它是一种具有后进先出性质的数据结构 也就是
  • 医院RFID药物跟踪管理解决方案

    1 技术背景 基于 2018年2月6日 药物调配商QuVa Pharma 与Kit Check合作 为其医院药房客户 提供基于RFID药物跟踪管理解决方案 QuVa提供的系统包括Kit Check的 无源 超高频 UHF RFID 标签 该
  • WEB基础-变形动画

    字体图标 IconFont 图标字体也叫字体图标 就是字体做的图标 可以通过设置字体的方式改变图标的样式 受到近些年 扁平化设计 的影响 越来越多的图标都开始使用 IconFont 下载字体图标 首先打开IconFont 阿里巴巴矢量图标库
  • websocket详解

    之前利用websocket以及jQuery做了一个聊天通讯应用 最近在总结整个过程中的一些问题 也借此机会聊聊websocket协议 webSocket本身不存在跨域问题 所以可以利用webSocket来进行非同源之间的通信 webSock
  • pytorch打印模型梯度

    简介 有时候在调试模型训练过程时 我们需要打印模型中参数的梯度 去查看是否存在梯度消失或者梯度爆炸的问题 可以通过在backward之后查看params的grad属性来确认 参考代码如下所示 import torch 定义模型 class

随机推荐

  • Python3 面向对象编程

    好记性不如烂笔头 对之前阅读书籍进行梳理与总结 此文为 Python3面向对象编程 阅读笔记 文章目录 第一章 面向对象设计 第二章 Python对象 第三章 对象相似时 第四章 异常捕获 第五章 何时使用面向对象编程 第六章 Python
  • C# 导出 Excel 方法

    第一种 使用 Microsoft Office Interop Excel dll public void ExportExcel DataTable dt if dt null Microsoft Office Interop Excel
  • 关键词生成器在线-在线免费关键词生成器

    关键词生成 什么是关键词生成 关键词生成就是根据你输入的一个关键词生成成千上百的核心关键词 围绕着你输入的核心词来生成的 优先生成大量用户搜索的关键词 今天就给大家分享一款免费关键词生成工具 关键词生成的来源主要是用户都在搜索的词 相关搜索
  • CentOS-8-x86_64-1905安装

    CentOS 8 x86 64 1905安装 安装前准备 VMware Workstation 点击下载虚拟机软件 VMware Workstation是一款功能强大的桌面虚拟计算机软件 提供用户可在单一的桌面上同时运行不同的操作系统 和进
  • Crazy Thairs【树状数组+高精度+DP思想】

    题目链接 POJ 3378 题意 有N个点 问的是要求组成一个长度为5的上升子序列的组成有多少种 最搞事情的是这道题不用取模 所以 是一定会爆long long的 首先 很容易想到一点就是我们可以开一个dp maxN 5 表示的是 dp i
  • Vagrant学习笔记:搭建K8s集群

    通常情况下 我们在使用VMware VirtualBox这一类虚拟机软件创建虚拟开发环境时 往往需要经历寻找并下载操作系统的安装镜像文件 然后根据该镜像文件启动的安装向导一步一步地安装与配置操作系统 最后还需要从零开始安装开发与运维工具 整
  • 计算机考研复试机试怎么速成,西安电子大学计算机考研复试机试(2019)+ 学长讲解(6)+ 作业...

    学长讲的就是算法笔记的入门算法 作业1 链接 https www nowcoder com questionTerminal 0f64518fea254c0187ccf0ea05019672 来源 牛客网 有一个网络日志 记录了网络中计算任
  • Java各种集合判空总结

    目录 集合判空 CollectionUtils isEmpty推荐 原始判断 isEmpty 其他 数组判空 集合判空 CollectionUtils isEmpty推荐 这个使用到了spring的工具类 需要提前引入依赖 import o
  • 机器学习2018-12-28

    机器学习 组成 主要任务 分类 classification 将实例数据划分到合适的类别中 应用实例 判断网站是否被黑客入侵 二分类 手写数字的自动识别 多分类 回归 regression 主要用于预测数值型数据 应用实例 股价价格波动的预
  • 用编程器免拆夹子刷斐讯K2 K2P解决难搞固件 刷BREED 无损原EEPROM

    文章中放的几个地址都是思路来源 感谢各路大神原帖子的思路 因为我刷机时候没有拍照 只好借用各位大佬的图来说明步骤 我做一下整理会放出本篇刷机流程 很简单 K2 22 6 532 231软件版本已经无解 UBOOT等可以软刷的方式都被堵死 编
  • .NET编程——利用C#实现TCP协议的异步通信Socket套接字(WinForm)

    本文将介绍利用基于TCP通信协议的Socket实现服务器与客户端之间的数据传输 目录 前言 计算机通信 创建服务器 服务器通信 创建客户端 客户端通信 前言 TCP IP Transmission Control Protocol Inte
  • 【编码-校验码】奇偶校验码

    https zhuanlan zhihu com p 26509678 引自原文 1 定义 奇偶校验位 英语 Parity bit 或校验比特 check bit 是一个表示给定位数的二进制数中1的个数是奇数还是偶数的二进制数 奇偶校验位是
  • 炫酷的Android智能下拉刷新框架,值得一看

    1 Android智能下拉刷新框架 SmartRefreshLayout 支持所有的 View AbsListView RecyclerView WebView View 和多层嵌套的视图结构 支持自定义并且已经集成了很多炫酷的 Heade
  • BRDF详解

    光照模型主要分为三类 测量模型 经验模型和基于物理的分析模型 在计算机图形学中介绍的光照模型为经验模型中的Phong模型 也就是本人理解的 根据点距光源的位置 入射角度等信息计算的 反射强度 包括漫反射强度 镜面反射强度 但是Phong模型
  • 《effective c++》笔记

    序 1 object 在 object oriented技术中的真正意义是 物件 物体 而非 对象 目标 导读 1 术语 1 声明式 告诉编译器名称和类型 但略去细节 std size t numDigit int number std命名
  • 软工导论知识框架(六)面向对象分析

    前言 绘制各种类型的图是重点 对于面向对象建模中需要绘制的图总结在第五期中 软工导论知识框架 五 面向对象方法学 一 分析过程 1 获取需求 与用户交谈 向用户提问题 参观用户的工作流程 观察用户的操作 向用户群体发调查问卷 与同行 专家交
  • 【满分】【华为OD机试真题2023 JAVA&JS】查找重复代码

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 查找重复代码 时间限制 1s 空间限制 32MB 限定语言 不限 题目描述 小明负责维护项目下的代码 需要查找出重复代码 用以支撑后续的代码优化 请你帮助小明找出重复的代码 重复
  • 使用IntelliJ IDEA查看类的继承关系图形

    最近正好也没什么可忙的 就回过头来鼓捣过去的知识点 到Servlet部分时 以前学习的时候硬是把从上到下的继承关系和接口实现记得乱七八糟 这次利用了IDEA的diagram 结果一目了然 也是好用到炸裂 就此分享 1 查看图形形式的继承链
  • Golang基础 流程控制 条件判断

    条件判断 01 条件判断 if 02 分支选择 switch 03 异步处理 select 参考资料 条件语句一般指定一个或多个条件 并通过测试条件是否为 true 来决定是否执行指定语句 并在条件为 false 的情况在执行另外的语句 0
  • 你得学会并且学得会的Socket编程基础知识

    这一篇文章 我将图文并茂地介绍Socket编程的基础知识 我相信 如果你按照步骤做完实验 一定可以对Socket编程有更好地理解 本文源代码 可以通过这里下载 http files cnblogs com chenxizhang Socke