Socket 客户端 服务端详解

Socket Server And Client:

1、socket 架构图:

2、端口的分类:

1)公认端口(well known ports):从0到1023,它们紧密绑定(binding)于一些服务。通常这些端口的通讯明确表明了某种服务的协议。例如:80 端口实际上总是HTTP通讯。

2)注册端口(registered ports):从1024到49151。它们松散的邦定于一些服务。也就是说有许多服务邦定于这些端口,这些端口同样用于许多其他目的。例如:许多系统处理动态端口从1024左右开始。

3)动态和/或者私有端口(Dynamic and/or private ports):从49152到65535。理论上,不应该为服务分配这些端口。实际上,机器通常从1024起分配动态端口。但也有例外的情况。

3、Socket Server:

 

 

public List<Socket> proxSockets = new List<Socket>();
        public SocketServer()
        {
            InitializeComponent();
        }

        private void btn_Listen_Click(object sender, RoutedEventArgs e)
        {
            //1、创建服务器的socket
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //Log 创建了服务器socket对象
            this.txt_Log.Text = "创建了服务器Socket\r\n" + txt_Log.Text;
            //2、绑定IP和端口
            IPAddress ip = IPAddress.Parse(txt_IP.Text);
            IPEndPoint endPoint = new IPEndPoint(ip, Convert.ToInt32(txt_port.Text));
            serverSocket.Bind(endPoint);
            //3、开始侦听,这个10表示等待连接的队列大小,例如:同一时间来了100个连接,处理一个,另外99个有10个放入队列,剩下的报错
            serverSocket.Listen(10);
            //4、接受客户端的connect,Accept 方法会阻塞当前线程,为防止阻塞主线程,因此放到线程池执行
            this.txt_Log.Text = "开始接受客户端连接\r\n" + txt_Log.Text;
            ThreadPool.QueueUserWorkItem(new WaitCallback(StartAcceptClient), serverSocket);
        }

        private void StartAcceptClient(object state)
        {
            Socket serverSocket = state as Socket;
            //可能有多个client 连接
            while (true)
            {
                Socket proxSocket = serverSocket.Accept();
                proxSockets.Add(proxSocket);
                //并且代理socket 对象需要接受client 的Message
                ThreadPool.QueueUserWorkItem(new WaitCallback(RecevieData), proxSocket);
                关闭代理socket (看实际应用场景决定)
                //proxSocket.Shutdown(SocketShutdown.Both);
                //proxSocket.Close();
            }

        }

        private void RecevieData(object state)
        {
            Socket proxSocket = state as Socket;
            byte[] data = new byte[1024 * 1024];
            //每一个代理都需要接受
            while (true)
            {
                //try catch  防止客户端异常退出
                try
                {
                    int realLen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None);
                    if (realLen == 0)
                    {
                        //对方正常退出的case,对方有shutdown
                        StopConnect(proxSocket);
                        
                        proxSockets.Remove(proxSocket);
                        return;
                    }
                }
                catch (Exception ex)
                {
                    //异常退出也要关闭
                    StopConnect(proxSocket);
                    throw ex;
                }
                
            }
        }

        private void StopConnect(Socket proxSocket)
        {
            if (proxSocket.Connected)
            {
                proxSocket.Shutdown(SocketShutdown.Both);
                //超过100s 强制关闭
                proxSocket.Close(100);
            }
        }

        //群发到每一个client
        private void btn_Send_Click(object sender, RoutedEventArgs e)
        {
            foreach (var item in proxSockets)
            {
                if (item.Connected)
                {
                    string str = this.txt_port.Text.Trim().ToUpper();
                    byte[] data = Encoding.Default.GetBytes(str);
                    item.Send(data, 0, data.Length, SocketFlags.None);
                }
            }
        }

 

 

 

 

 

 

 

4、socket Client:

public Socket ClientSocket { get; set; }
        public SocketClient()
        {
            InitializeComponent();
        }

        private void btn_Connect_Click(object sender, RoutedEventArgs e)
        {
            //1、创建服务器的socket
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ClientSocket = clientSocket;
            //2、连接到服务器
            while (true)
            {
                try
                {
                    clientSocket.Connect(IPAddress.Parse(txt_IP.Text), Convert.ToInt32(txt_port.Text));
                    break;
                }
                catch (Exception)
                {
                    //Log 连接失败 并重新连接
                }
            }

            //连接OK 那个socket 开始receive
            Thread th = new Thread(new ParameterizedThreadStart(ReceiveData));
            th.IsBackground = true;
            th.Start(ClientSocket);
        }

        private void ReceiveData(object obj)
        {
            Socket socket = obj as Socket;
            byte[] data = new byte[1024 * 1024];
            //每一个代理都需要接受
            while (true)
            {
                //try catch  防止客户端异常退出
                try
                {
                    int realLen = socket.Receive(data, 0, data.Length, SocketFlags.None);
                    if (realLen == 0)
                    {
                        //对方正常退出的case,对方有shutdown
                        StopConnect(socket);
                                               
                        return;
                    }
                }
                catch (Exception ex)
                {

                    throw ex;
                }

            }
        }

        private void StopConnect(Socket socket)
        {
            if (socket.Connected)
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Close(100);
            }           
        }

        private void btn_Send_Click(object sender, RoutedEventArgs e)
        {
            if (ClientSocket.Connected)
            {
                string str = this.txt_port.Text.Trim().ToUpper();
                byte[] data = Encoding.Default.GetBytes(str);
                ClientSocket.Send(data, 0, data.Length, SocketFlags.None);
            }
        }
    }

 

 

 

 

 

5、Socket的通信的本质&阻塞:

send方法把数据复制到发送缓冲区中,它做的事情只是这个,然后TCP协议底层就根据在TCP三步握手时确定的MSS大小(比如:1024字节)从发送缓冲区中取1024字节放到TCP数据包的数据部分,然后从发送缓冲区中删除1024个字节。并且TCP不是真正意义上的停止等待协议。也就是并不是发送一个TCP数据包就停止发送TCP数据包,直到收到接收方发送得ACK数据包才发送下一个TCP数据包。而Receive 则是从接收缓冲区中取数据。那么根据这个原理

阻塞原因

产生阻塞的原因可能是下面几个:
第一:你的发送缓冲区太小了。比如:只有1MB或者小于1MB,那么你的发送缓冲区会被迅速填满。导致阻塞
第二: 对方的网络环境很差,接收方的接收缓冲区太小了.你的发送缓冲区发送速度就很慢。发送缓冲区的数据清空速度就很慢。
第三:整个网路环境很差.数据要经过很多路由器。路由器很有自己的缓冲区,路由器的缓冲区如果被填满,那么你的发送数据就降低。你的发送缓冲区清空数据的速度就降低。连续三次发1MB的速度就迅速填满发送缓冲区。

 

6、Socket的可能问题:

由于第五点我们知道,socket本质是通过基础系统的空间来交换数据的,那么主要可能引发以下两点问题

 

1,Socket receive的实际字节数不等于预期接收的字节数

receive时,虽然我们给Buffer定义了一个长度和要接收到的size,但是Socket接收到的字节数(即返回值)并不一定等于Size

通常我们会用一个较大的size来接收,另外需要采用循环接受的办法,上面的代码

 

2,服务器发送多次,客户端一次接收的问题。(理论上设置的receive buffer 足够大,可以一次拿到基础系统空间的数据即所有的森send)

存在一个问题:假如客户端想一次接收服务器多次发送过来的数据,客户端定义缓存body的长度等于服务端多次发送的数据长度和,客户端在服务器发送完成后开始一次接收,客户端肯定能接收到服务器发送过来的所有数据,不会只接收服务器发送过来的部分数据。

打个比方,假如服务器分两次分别发送“Hello”,“World”。这样客户端一定能接受到“HelloWorld”。

这样有可能会导致如果需要一条send 一条receive的情况(实时聊天工具或者工业上的设备间通信),就很难处理。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值