• 两端通信


    上篇博文:http://www.cnblogs.com/wolf-sun/p/3329558.html

         介绍了客户端连接服务端,一对一,多对一的情况,下面实现服务器接收消息的功能。LZ这些弄的比较慢,也是边学习,边动手实现的。具体步骤在注释中写的比较清楚,不懂的可以留言,LZ会尽快回复。共同学习,共同进步。

    接收消息时机

          什么时候接收消息?当服务器开始监听,有客户端连接,并且连接成功,此时负责通信的Socket已经创建,此时就可以接收消息了,可以通过Socket的Receive()方法接收消息。

     View Code

    // 摘要:
    // 从绑定的 System.Net.Sockets.Socket 套接字接收数据,将数据存入接收缓冲区。
    //
    // 参数:
    // buffer:
    // System.Byte 类型的数组,它是存储接收到的数据的位置。
    //
    // 返回结果:
    // 接收到的字节数。
    //
    // 异常:
    // System.ArgumentNullException:
    // buffer 为 null。
    //
    // System.Net.Sockets.SocketException:
    // 试图访问套接字时发生错误。 有关更多信息,请参见备注部分。
    //
    // System.ObjectDisposedException:
    // System.Net.Sockets.Socket 已关闭。
    //
    // System.Security.SecurityException:
    // 调用堆栈中的调用方没有所需的权限。
    public int Receive(byte[] buffer);

         上面代码介绍了Receive方法接收参数及返回值。

    复制代码
     1  private void ListenConn(object o)
     2         {
     3             //将参数o 转化为监听的socket
     4             Socket socketListener = o as Socket;
     5             //写入循环 每一个连接就创建一个通信用的socket
     6             while (true)
     7             {
     8                 //当有客户端连接成功 创建通信用的socket
     9                 Socket connSocket = socketListener.Accept();
    10                 string ip = connSocket.RemoteEndPoint.ToString();
    11                 ShowMsg(ip + " " + DateTime.Now.ToString() + " 连接成功");
    12                //创建一个新线程去接收消息
    13                 Thread th = new Thread(ReceiveMsg);
    14                 th.Start(connSocket);
    15 
    16             }
    17 
    18 }
    复制代码

    接收消息的代码:

    复制代码
     1   //接收客户端的消息
     2         private void ReceiveMsg(object o)
     3         {
     4             Socket connSocket = o as Socket;
     5 
     6             //通信用的socket连接成功 就可以接收消息了
     7             byte[] buffer = new byte[1024 * 1024 * 5];//5M缓存
     8             while (true)
     9             {
    10                 //count是当前接收的字节个数
    11                 int count = connSocket.Receive(buffer);
    12                 string ip = connSocket.RemoteEndPoint.ToString();
    13                 //判断接收到的字节个数 是0表示客户端关闭了
    14                 if (count > 0)
    15                 {
    16 
    17                     //将字节转换为字符串
    18                     string msg = Encoding.UTF8.GetString(buffer, 0, count);
    19                     ShowMsg(ip + " " + DateTime.Now.ToString() + "
    " + msg);
    20                 }
    21                 else
    22                 {
    23                     //socket没办法发送空消息 如果收到空消息 客户端关闭
    24                     ShowMsg(ip + ":" + "断开连接");
    25                     connSocket.Close();
    26                     break;
    27 
    28                 }
    29 
    30             }
    31 
    32         }
    复制代码

    测试:仍然用telnet命令来测试:telnet 127.0.0.1 50000

         测试结果:多对一,一对一,发送消息正常,关闭客户端,服务端正常显示哪个客户端断开连接。

         服务器端所有代码:

     View Code

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    namespace Wolfy.ChatServer
    {
    public partial class Server : Form
    {
    public Server()
    {
    InitializeComponent();
    //不让其检查跨线程的操作
    Control.CheckForIllegalCrossThreadCalls = false;
    }
    //存放endpoin和通信用的socket
    Dictionary<string, Socket> dic = new Dictionary<string, Socket>();
    private void btnSend_Click(object sender, EventArgs e)
    {
    ServerSendMsg(this.txtInputMsg.Text);
    }
    /// <summary>
    /// 服务器给客户端发送消息
    /// </summary>
    private void ServerSendMsg(string msg)
    {
    //服务器给客户端发消息
    string userkey = comboBoxEndpoint.Text;
    if (!string.IsNullOrEmpty(userkey))
    {
    ShowMsg(msg);
    byte[] buffer = Encoding.UTF8.GetBytes(msg);
    dic[userkey].Send(buffer);
    msg = "";
    }
    else
    {
    MessageBox.Show("请选择客户端");
    }
    }

    private void btnStartService_Click(object sender, EventArgs e)
    {
    //服务器ip地址
    IPAddress ip = IPAddress.Parse(txtIPAddress.Text);
    //ip地址和端口
    IPEndPoint endpoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
    //创建用于监听的socket
    Socket socketListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    //绑定ip和端口
    socketListener.Bind(endpoint);
    //开始监听 限制连接数 最多可以连接10个
    socketListener.Listen(10);
    ShowMsg("开始监听......");
    //创建线程 去监听连接
    Thread th = new Thread(ListenConn);
    //将线程变为后台线程
    th.IsBackground = true;
    th.Start(socketListener);
    }
    private void ListenConn(object o)
    {
    //将参数o 转化为监听的socket
    Socket socketListener = o as Socket;
    //写入循环 每一个连接就创建一个通信用的socket
    while (true)
    {
    //当有客户端连接成功 创建通信用的socket
    Socket connSocket = socketListener.Accept();
    string ip = connSocket.RemoteEndPoint.ToString();
    ShowMsg(ip + " " + DateTime.Now.ToString() + " 连接成功");
    //连接成功后加入字典
    dic.Add(ip, connSocket);
    comboBoxEndpoint.Items.Add(ip);
    //创建一个新线程去接收消息
    Thread th = new Thread(ReceiveMsg);
    th.Start(connSocket);
    }

    }
    //接收客户端的消息
    private void ReceiveMsg(object o)
    {
    Socket connSocket = o as Socket;

    //通信用的socket连接成功 就可以接收消息了
    byte[] buffer = new byte[1024 * 1024 * 5];//5M缓存
    while (true)
    {
    //count是当前接收的字节个数
    int count = connSocket.Receive(buffer);
    string ip = connSocket.RemoteEndPoint.ToString();
    //判断接收到的字节个数 是0表示客户端关闭了
    if (count > 0)
    {
    //将字节转换为字符串
    string msg = Encoding.UTF8.GetString(buffer, 0, count);
    ShowMsg(ip + " " + DateTime.Now.ToString() + " " + msg);
    }
    else
    {
    //socket没办法发送空消息 如果收到空消息 客户端关闭
    ShowMsg(ip + ":" + "断开连接");
    connSocket.Close();
    break;
    }

    }

    }
    /// <summary>
    /// 提示信息辅助方法
    /// </summary>
    /// <param name="msg"></param>
    private void ShowMsg(string msg)
    {
    this.txtMsgView.AppendText(msg + " ");
    }

    private void txtInputMsg_KeyUp(object sender, KeyEventArgs e)
    {
    //如果用户按下了Enter键
    if (e.KeyCode == Keys.Enter)
    {
    //则调用 服务器向客户端发送信息的方法
    ServerSendMsg(this.txtInputMsg.Text);
    }
    }
    }
    }

    客户端实现

         客户端创建的socket即负责连接又负责通信,所以这里和服务端不同。客户端连接、接收消息和发送消息代码如下:

     View Code

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Net.Sockets;
    using System.Net;
    using System.Threading;
    namespace Wolf.ChatClient
    {
    public partial class Client : Form
    {
    public Client()
    {
    InitializeComponent();
    //不让检查跨线程操作
    Control.CheckForIllegalCrossThreadCalls = false;
    }
    Socket socket;
    private void btnStartService_Click(object sender, EventArgs e)
    {
    //连接服务器的ip和端口
    IPAddress ip = IPAddress.Parse(txtIPAddress.Text);
    IPEndPoint endpoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
    //客户端的socket即负责连接又负责通信
    socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    //连接服务器
    socket.Connect(endpoint);
    ShowMsg("连接成功......");
    //和服务器连接成功后就可以接收服务端的消息了
    Thread th = new Thread(ReceiveMsg);
    th.IsBackground = true;
    th.Start();

    }
    private void ReceiveMsg()
    {
    byte[] buffer = new byte[1024 * 1024 * 5];
    while (true)
    {
    int count = socket.Receive(buffer);
    string msg = Encoding.UTF8.GetString(buffer, 0, count);
    ShowMsg(this.txtIPAddress.Text + ":" + this.txtPort.Text + " " + DateTime.Now.ToString() + " " + msg);
    }
    }
    private void ShowMsg(string msg)
    {
    txtMsgView.AppendText(msg + " ");
    }
    private void btnSend_Click(object sender, EventArgs e)
    {
    ClientSendMsg(this.txtInputMsg.Text);
    }

    /// <summary>
    /// 客户端向服务端发送消息
    /// </summary>
    /// <param name="msg"></param>
    private void ClientSendMsg(string msg)
    {
    //向服务端发送消息
    if (socket != null)
    {
    ShowMsg(msg);
    byte[] buffer = Encoding.UTF8.GetBytes(msg);
    socket.Send(buffer);
    msg = "";
    }
    else
    {
    ShowMsg("<<<<请先连接服务器>>>");
    }
    }

    private void txtInputMsg_KeyUp(object sender, KeyEventArgs e)
    {
    //如果用户按下了Enter键
    if (e.KeyCode == Keys.Enter)
    {
    //则调用 服务器向客户端发送信息的方法
    ClientSendMsg(this.txtInputMsg.Text);
    }
    }

    private void Client_FormClosing(object sender, FormClosingEventArgs e)
    {
    //客户端关闭 关闭socket
    socket.Shutdown(SocketShutdown.Both);
    }
    }
    }

    测试结果:


     结语:

           边学习,边动手,实现了两端通信,目前支持一对一,多对一通信功能,代码中针对关闭客户端的情况还有bug,有待进一步修改。

    Alternate Text 作者:Wolfy
    出处:http://www.cnblogs.com/wolf-sun/
    本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。愿与志同道合的朋友一起成长......
     
    分类: 网络编程
  • 相关阅读:
    深入浅出JSONP--解决ajax跨域问题
    Apache与Tomcat的区别
    项目终于接近尾声了
    交互设计[小插曲]--网站UI配色
    使用 Jasmine 进行测试驱动的 JavaScript 开发
    javascript单元测试
    MySQL查询当前数据库中所有记录不为空的表
    cannot be resolved to a type的错误
    oracle 表空数据导出dmp ,空表导出失败
    Iterable<E> Iterator<E>
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3332357.html
Copyright © 2020-2023  润新知