首页 > 学院 > 开发设计 > 正文

C#-Socket监听消息处理

2019-11-10 18:18:34
字体:
来源:转载
供稿:网友

搬运整合三个使用C#实现Socket编程的例子,包含服务器端和客户端。

原文链接:

C# socket监听

C#-Socket监听消息处理

基于C#的socket编程的TCP同步实现

按照链接顺序贴上原文。

例子一:

网络通讯流程如上

服务器:

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.IO;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 _06Server{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        PRivate void btnStart_Click(object sender, EventArgs e)        {            try            {                //当点击开始监听的时候 在服务器端创建一个负责监ip地址跟端口号的Socket                Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);                IPAddress ip = IPAddress.Any;//IPAddress.Parse(txtServer.Text);                //创建端口号对象                IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));                //监听                socketWatch.Bind(point);                ShowMsg("监听成功");                socketWatch.Listen(10);                Thread th = new Thread(Listen);                th.IsBackground = true;                th.Start(socketWatch);            }            catch            {                           }        }        /// <summary>        /// 等待客户端的连接 并且创建与之通信用的Socket        /// </summary>        ///         Socket socketSend;        void Listen(object o)        {            Socket socketWatch = o as Socket;            //等待客户端的连接 并且创建一个负责通信的Socket            while (true)            {                try                {                    //负责跟客户端通信的Socket                    socketSend = socketWatch.Accept();                    //将远程连接的客户端的IP地址和Socket存入集合中                    dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);                    //将远程连接的客户端的IP地址和端口号存储下拉框中                    cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString());                    //192.168.11.78:连接成功                    ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + "连接成功");                    //开启 一个新线程不停的接受客户端发送过来的消息                    Thread th = new Thread(Recive);                    th.IsBackground = true;                    th.Start(socketSend);                }                catch                { }            }        }        //将远程连接的客户端的IP地址和Socket存入集合中        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();        /// <summary>        /// 服务器端不停的接受客户端发送过来的消息        /// </summary>        /// <param name="o"></param>        void Recive(object o)        {            Socket socketSend = o as Socket;            while (true)            {                try                {                    //客户端连接成功后,服务器应该接受客户端发来的消息                    byte[] buffer = new byte[1024 * 1024 * 2];                    //实际接受到的有效字节数                    int r = socketSend.Receive(buffer);                    if (r == 0)                    {                        break;                    }                    string str = Encoding.UTF8.GetString(buffer, 0, r);                    ShowMsg(socketSend.RemoteEndPoint + ":" + str);                }                catch                { }            }        }        void ShowMsg(string str)        {            txtLog.AppendText(str + "/r/n");        }        private void Form1_Load(object sender, EventArgs e)        {            Control.CheckForIllegalCrossThreadCalls = false;        }        /// <summary>        /// 服务器给客户端发送消息        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void btnSend_Click(object sender, EventArgs e)        {            string str = txtMsg.Text;            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);            List<byte> list = new List<byte>();            list.Add(0);            list.AddRange(buffer);            //将泛型集合转换为数组            byte[] newBuffer = list.ToArray();            //buffer = list.ToArray();不可能            //获得用户在下拉框中选中的IP地址            string ip = cboUsers.SelectedItem.ToString();            dicSocket[ip].Send(newBuffer);            //     socketSend.Send(buffer);        }        /// <summary>        /// 选择要发送的文件        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void btnSelect_Click(object sender, EventArgs e)        {            OpenFileDialog ofd = new OpenFileDialog();            ofd.InitialDirectory = @"C:/Users/SpringRain/Desktop";            ofd.Title = "请选择要发送的文件";            ofd.Filter = "所有文件|*.*";            ofd.ShowDialog();            txtPath.Text = ofd.FileName;        }        private void btnSendFile_Click(object sender, EventArgs e)        {            //获得要发送文件的路径            string path = txtPath.Text;            using (FileStream fsRead = new FileStream(path, FileMode.Open, Fileaccess.Read))            {                byte[] buffer = new byte[1024 * 1024 * 5];                int r = fsRead.Read(buffer, 0, buffer.Length);                List<byte> list = new List<byte>();                list.Add(1);                list.AddRange(buffer);                byte[] newBuffer = list.ToArray();                dicSocket[cboUsers.SelectedItem.ToString()].Send(newBuffer, 0, r+1, SocketFlags.None);            }        }        /// <summary>        /// 发送震动        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void btnZD_Click(object sender, EventArgs e)        {            byte[] buffer = new byte[1];            buffer[0] = 2;            dicSocket[cboUsers.SelectedItem.ToString()].Send(buffer);        }    }}客户端:

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.IO;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 _07Client{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        Socket socketSend;        private void btnStart_Click(object sender, EventArgs e)        {            try            {                //创建负责通信的Socket                socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);                IPAddress ip = IPAddress.Parse(txtServer.Text);                IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));                //获得要连接的远程服务器应用程序的IP地址和端口号                socketSend.Connect(point);                ShowMsg("连接成功");                //开启一个新的线程不停的接收服务端发来的消息                Thread th = new Thread(Recive);                th.IsBackground = true;                th.Start();            }            catch            { }                    }        /// <summary>        /// 不停的接受服务器发来的消息        /// </summary>        void Recive()        {            while (true)            {                try                {                    byte[] buffer = new byte[1024 * 1024 * 3];                    int r = socketSend.Receive(buffer);                    //实际接收到的有效字节数                    if (r == 0)                    {                        break;                    }                    //表示发送的文字消息                    if (buffer[0] == 0)                    {                        string s = Encoding.UTF8.GetString(buffer, 1, r-1);                        ShowMsg(socketSend.RemoteEndPoint + ":" + s);                    }                    else if (buffer[0] == 1)                    {                        SaveFileDialog sfd = new SaveFileDialog();                        sfd.InitialDirectory = @"C:/Users/SpringRain/Desktop";                        sfd.Title = "请选择要保存的文件";                        sfd.Filter = "所有文件|*.*";                        sfd.ShowDialog(this);                        string path = sfd.FileName;                        using (FileStream fsWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))                        {                            fsWrite.Write(buffer, 1, r - 1);                        }                        MessageBox.Show("保存成功");                    }                    else if (buffer[0] == 2)                    {                        ZD();                    }                                }                catch { }            }        }        /// <summary>        /// 震动        /// </summary>        void ZD()        {            for (int i = 0; i < 500; i++)            {                this.Location = new Point(200, 200);                this.Location = new Point(280, 280);            }        }        void ShowMsg(string str)        {            txtLog.AppendText(str + "/r/n");        }        /// <summary>        /// 客户端给服务器发送消息        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void btnSend_Click(object sender, EventArgs e)        {            string str = txtMsg.Text.Trim();            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);            socketSend.Send(buffer);        }        private void Form1_Load(object sender, EventArgs e)        {            Control.CheckForIllegalCrossThreadCalls = false;        }        private void txtServer_TextChanged(object sender, EventArgs e)        {        }    }}例子二:

TCP/IP:Transmission Control Protocol/Internet Protocol,传输控制协议/因特网互联协议,又名网络通讯协议。简单来说:TCP控制传输数据,负责发现传输的问题,一旦有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地,而IP是负责给因特网中的每一台电脑定义一个地址,以便传输。TCP协议在许多分布式应用程序中进行消息命令传递是必不可少的部分。

TCP通信的三次握手:三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。

第一次握手:客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。第二次握手:服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时,将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即X+1。第三次握手:客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1

先看下服务端Socket监听代码:

using System;using System.Collections.Generic;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading;namespace SocketDome{    /// <summary>    /// 处理Socket监听逻辑    /// </summary>    public class SocketProvider    {        private static Socket serviceSocketListener; //Socke监听处理请求        /// <summary>        /// 开启Socket监听        /// </summary>        public static void Init()        {            serviceSocketListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            serviceSocketListener.Bind(new IPEndPoint(IPAddress.Parse("10.0.0.217"), 20000)); //IP和端口应该是可配置            serviceSocketListener.Listen(1024);            Thread handleSocket = new Thread(new ThreadStart(HandleSocket));            handleSocket.Start();        }        /// <summary>        /// 监听链接        /// </summary>        private static void HandleSocket()        {            while (true)            {                try                {                    Socket currSocket = serviceSocketListener.Accept();  //为新建连接创建新的 System.Net.Sockets.Socket                    Thread processThread = new Thread(new ParameterizedThreadStart(ProcessSocket));                    processThread.Start(currSocket);                }                catch { }            }        }        /// <summary>        /// 处理Socket信息        /// </summary>        /// <param name="obj">新建连接创建新Socket对象</param>        private static void ProcessSocket(object obj)        {            Socket currSocket = (Socket)obj;            try            {                byte[] recvBytess = new byte[1048576];                int recbytes;                recbytes = currSocket.Receive(recvBytess, recvBytess.Length, 0);                if (recbytes > 0)                {                    var contentStr = Encoding.UTF8.GetString(recvBytess, 0, recbytes);                    var _order = contentStr.Split('~');                    byte[] sendPass = Encoding.UTF8.GetBytes(_order[0].ToUpper() + "#SUCCESS"); //先相应对话,然后去异步处理                    currSocket.Send(sendPass, sendPass.Length, SocketFlags.None);                    switch (_order[0].ToUpper())                    {                         case"ADDCACHE":                            Console.WriteLine("添加缓存消息" + _order[1]);                           //处理ADDCACHE逻辑                           Console.WriteLine("写Log日志");                            break;                        default :                            Console.WriteLine("命令错误");                            Console.WriteLine("写Log日志");                            break;                    }                }            }            catch (Exception ex)            {                Console.WriteLine("写Error日志" + ex.Message);            }        }    }}

这个服务端,监听着客户端发来的命令,格式定义为:命令~参数,在服务端接受到客户端的命令消息后立即回传接到命令并开始处理,进行异步处理避免客户端等待。

下面看下客户端的Socket客户端主动请求服务端代码:

using System;using System.Collections.Generic;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;namespace Consoleapplication7{    /// <summary>    /// Socket Helper    /// </summary>    public class SocketHelper    {        private string ip;        private IPEndPoint ex;        private Socket socket;        public SocketHelper(string ip, int port)        {            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            this.ip = ip;            this.ex = new IPEndPoint(IPAddress.Parse(ip), port);        }        /// <summary>        /// Socket 进行连接        /// </summary>        /// <returns>连接失败OR成功</returns>        public bool Socketlink()        {            try            {                socket.Connect(ex);                return true;            }            catch (Exception ex)            {                return false;            }        }        /// <summary>        /// Socket 发送消息        /// </summary>        /// <param name="strmsg">消息</param>        public void SendVarMessage(string strmsg)        {            try            {                byte[] msg = System.Text.Encoding.UTF8.GetBytes(strmsg);                this.socket.Send(msg);            }            catch (Exception ex)            {                this.socket.Close();            }        }        /// <summary>        /// Socket 消息回传        /// </summary>        /// <returns></returns>        public string ReceiveMessage()        {            try            {                byte[] msg = new byte[1048576];                int recv = socket.Receive(msg);                this.socket.Close();                return System.Text.Encoding.UTF8.GetString(msg, 0, recv);            }            catch (Exception ex)            {                this.socket.Close();                return "ERROR";            }        }    }}
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace ConsoleApplication7{    class Program    {        static void Main(string[] args)        {            SocketHelper socket = new SocketHelper("10.0.0.217",20000);            if(socket.Socketlink())            {                Console.WriteLine("连接成功");                socket.SendVarMessage("ADDCACHE~张三");                string strReposon = socket.ReceiveMessage();                Console.WriteLine(strReposon);            }            Console.Read();        }    }}首先以管理园身份开启服务端查询,然后客户端主动请求服务端进行消息请求。

例子三:

一、摘要

  总结一下基于C#的TCP传输协议的涉及到的常用方法及同步实现。

二、实验平台

  Visual Studio 2010

三、socket编程的一些常用方法(同步实现)

3.1 命名空间

  需要添加的命名空间

using System.Net;using System.Net.Socket;

3.2 构造新的socket对象

socket原型:
public socket (AddressFamily addressFamily,SocketType sockettype,ProtocolType protocolType)

(1) AddressFamily 用来指定socket解析地址的寻址方案,Inte.Network标示需要ip版本4的地址,Inte.NetworkV6需要ip版本6的地址;

(2) SocketType 参数指定socket类型,Raw支持基础传输协议访问,Stream支持可靠,双向,基于连接的数据流;

(3) ProtocolType 表示socket支持的网络协议,如常用的TCP和UDP协议。

3.3 定义主机对象(1) IPEndPoint类

原型:

a)  

public IPEndPoint(IPAddress address,int port)  

参数address可以直接填写主机的IP,如"192.168.2.1";

b)

public IPEndPoint(long address,int port)

参数address整型int64如123456,参数port端口int32,如6655。

(2) 利用DNS服务器解析主机,使用Dns.Resolve方法

原型:

public static IPHostEntry Resolve(string hostname)

参数:待解析的主机名称,返回IPHostEntry类值,IPHostEntry为Inte.Net主机地址信息提供容器,该容器提供存有IP地址列表,主机名称等。

(3) Dns.GetHostByName获取本地主机名称

原型:

public static IPHostEntry GetHostByName(string hostname)

(4) GetHostByAddress

原型:

a)

public static IPHostEntry GetHostByAddress(IPAddress address)

参数:IP地址。

b)

public static IPHostEntry GetHostByAddress(string address)

参数:IP地址格式化字符串。

3.4 端口绑定和监听

  同步套接字服务器主机的绑定和端口监听,Socket类的Bind(绑定主机),Listen(监听端口),Accept(接收客户端的连接请求)。

(1) Bind

原型:

public void Bind(EndPoint LocalEP)

参数为主机对象 IPEndPoint

(2) Listen

原型:

public void Listen(int backlog)

参数整型数值,挂起队列最大值

(3) accept

原型:

public socket accept()

返回为套接字对象

3.5 socket的发送和接收方法

(1) 发送数据

a)socket类的send方法

原型一:

public int Send(byte[] buffer)

参数:待发送的字节数组;

原型二:

public int Send(byte[],SocketFlags)

SocketFlags成员列表:

DontRoute不使用路由表发送,

MaxIOVectorLength为发送和接收数据的wsabuf结构数量提供标准值,

None 不对次调用使用标志,

OutOfBand消息的部分发送或接收,

Partial消息的部分发送或接收,

Peek查看传入的消息。

原型三:

public int Send(byte[],int,SocketFlags)

参数二要发送的字节数

原型四:

public int Send(byte[],int,int,SocketFlags)

参数二为Byte[]中开始发送的位置

b) NetWordStream类的Write方法

原型:

public override void write(byte[] buffer,int offset,int size)

参数分别为:字节数组,开始字节位置,总字节数。

(2) 接收数据

a) Socket类Receive方法

原型一:

public int Receive(byte[] buffer) 原型二:

public int Receive(byte[],SocketFlags)原型三:

public int Receive(byte[],int,SocketFlags)  

原型四:

public int Receive(byte[],int,int,SocketFlags)

Socket类Receive方法的相关参数可参看Socket类Send方法中的参数。

b) NetworkStream类的Read方法

public override int Read(int byte[] buffer,int offset,int size)

参数可参看NetworkStream类的Write方法。

四、TCP传输协议的同步实现

 4.1 服务器端编程的步骤:

 (1) 创建套接字;

 (2) 绑定套接字到一个IP地址和一个端口上(bind());

 (3)将套接字设置为监听模式等待连接请求(listen());

 (4)请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());

 (5)用返回的套接字和客户端进行通信(send()/recv());

 (6)返回,等待另一连接请求;

 (7)关闭套接字。

服务器端代码:

using System;using System.Net;using System.Net.Sockets;using System.Collections.Generic;using System.Text;namespace net{    class Program    {        static void Main(string[] args)        {            //定义接收数据长度变量            int recv;            //定义接收数据的缓存            byte[] data = new byte[1024];            //定义侦听端口            IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, 5566);            //定义套接字类型            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            //连接            socket.Bind(ipEnd);            //开始侦听            socket.Listen(10);            //控制台输出侦听状态            Console.Write("Waiting for a client");            //一旦接受连接,创建一个客户端            Socket client = socket.Accept();            //获取客户端的IP和端口            IPEndPoint ipEndClient = (IPEndPoint)client.RemoteEndPoint;            //输出客户端的IP和端口            Console.Write("Connect with {0} at port {1}", ipEndClient.Address, ipEndClient.Port);            //定义待发送字符            string welcome = "Welcome to my server";            //数据类型转换            data = Encoding.ASCII.GetBytes(welcome);            //发送            client.Send(data, data.Length, SocketFlags.None);            while (true)            {                //对data清零                data = new byte[1024];                //获取收到的数据的长度                recv = client.Receive(data);                //如果收到的数据长度为0,则退出                if (recv == 0)                    break;                //输出接收到的数据                Console.Write(Encoding.ASCII.GetString(data, 0, recv));                //将接收到的数据再发送出去                client.Send(data, recv, SocketFlags.None);            }            Console.Write("Disconnect form{0}", ipEndClient.Address);            client.Close();            socket.Close();        }    }}

4.2 客户端编程的步骤:

(1) 创建套接字;

(2) 向服务器发出连接请求(connect());

(3) 和服务器端进行通信(send()/recv());

(4) 关闭套接字。

客户端代码:

using System;using System.Net;using System.Net.Sockets;using System.Collections.Generic;using System.Text;namespace client{    class Program    {        static void Main(string[] args)        {            //定义发送数据缓存            byte[] data = new byte[1024];            //定义字符串,用于控制台输出或输入            string input, stringData;            //定义主机的IP及端口            IPAddress ip = IPAddress.Parse("127.0.0.1");            IPEndPoint ipEnd = new IPEndPoint(ip, 5566);            //定义套接字类型            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            //尝试连接            try            {                socket.Connect(ipEnd);            }            //异常处理            catch (SocketException e)            {                Console.Write("Fail to connect server");                Console.Write(e.ToString());                return;            }            //定义接收数据的长度            int recv = socket.Receive(data);            //将接收的数据转换成字符串            stringData = Encoding.ASCII.GetString(data, 0, recv);            //控制台输出接收到的数据            Console.Write(stringData);            //定义从键盘接收到的字符串            input = Console.ReadLine();            //将从键盘获取的字符串转换成整型数据并存储在数组中                data = Encoding.ASCII.GetBytes(input);            //发送该数组            socket.Send(data, data.Length, SocketFlags.None);            while (true)            {                //                //如果字符串是"exit",退出while循环                if (input == "exit")                {                    break;                }                //对data清零                data = new byte[1024];                //定义接收到的数据的长度                recv = socket.Receive(data);                //将接收到的数据转换为字符串                stringData = Encoding.ASCII.GetString(data, 0, recv);                //控制台输出字符串                Console.Write(stringData);                //发送收到的数据                socket.Send(data, recv, 0);            }            Console.Write("disconnect from server");            socket.Shutdown(SocketShutdown.Both);            socket.Close();        }    }}上述代码实现了,当连接建立之后,客户端向服务器端发送键盘输入的字符,服务器端收到字符后,显示在控制台并发送给客户端,客户端收到字符后,显示在控制台并再次发送给服务器端,如此循环。

五、实验结果

  先后运行服务器端程序和客户端程序,控制台界面如下:

图1 服务器端控制台

  当连接建立后,服务器端控制台显示等待客户端的状态"Waiting for a client",并打印出连接信息。

图2 客户端控制台

  当连接建立后,客户端收到来自服务器端发送的字符串"Welcome to my server"。

  之后,客户端通过键盘发送数据,二者循环接收并发送,控制台分别如下:

图3 服务器控制台

图4 客户端控制台

六、几点说明

6.1 传输速度

  (1) 增大发送和接收的数组可提升传输速度,即增加一次实际发送数据的数量可以提高传输速度,但数组中数据的个数也不能一味的增大。需要说明的,由于地层MIT的限制,底层具体实现的时候每次发送的数据仍是不超过1510个的。

  (2) 将控制台界面最小化后,速度也会有翻倍的提升。

6.2 MFC的转换

  为了使传输协议更有可观性和使用性,通常做成MFC的样式,具体的使用已在基于TCP协议的网络摄像头的设计与实现应用。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表