• C# socket编程服务端部署


    C# Socket网络编程之服务端部署

    前前后后弄了差不多一个星期,终于把这玩意儿给搞出来了,其实谈不上多复杂,但是从0开始还是要走很多的弯路,现在也没有博客或者视频教程教弄这个东西,所以写下这篇博客方便以后要用的时来看看。


    总的来说需要掌握的东西不多。

    • C# socket网络编程
    • Linux基本命令
    • TCP/IP协议

    以下是我的服务端代码(由于我是用的winform框架写的,代码有两部分)

    program.cs

    using System;
    using System.Collections.Generic;
    using System.Windows.Forms;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.IO;
    using System.Drawing;
    
    namespace ChatoServer
    {
        static class Program
        {
            //1.创建套接字
            static Socket serverSocket = null;
            static IPAddress ip = null;
            static IPEndPoint point = null;
    
            static Dictionary<string, Socket> allClientSockets = null;
    
            static MainForm form = null;
            /// <summary>
            /// 应用程序的主入口点。
            /// </summary>
            [STAThread]//开启单线程
            //表示这个Main程序被一个单线程套间包住,且Main的执行,一次只能被一个线程占用,这个线程未执行完,别的线程是没办法调用的。
            static void Main()
            {
                //在 C# 中,System.Threading.Thread 类用于线程的工作。它允许创建并访问多线程应用程序中的单个线程。进程中第一个被执行的线程称为主线程。
               // 当 C# 程序开始执行时,主线程自动创建。使用 Thread 类创建的线程被主线程的子线程调用。您可以使用 Thread 类的 CurrentThread 属性访问线程
                allClientSockets = new Dictionary<string, Socket>();
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                form = new MainForm(bListenClick, bSendClick);
                Application.Run(form);
    
            }
    
            static EventHandler bListenClick = SetListen;//启动服务器相当于
            static EventHandler bSendClick = SendMsg;
    
            static void SetListen(object sender, EventArgs e)
            {
                
                ip = IPAddress.Parse(form.GetIPText());//获得IP
                point = new IPEndPoint(ip, form.GetPort());//获得端口
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//设置套接字
                try {
                    serverSocket.Bind(point);//绑定
                    serverSocket.Listen(20);//监听,最多20个
                    form.Println($"服务器开始在 {point} 上监听。");
    
                    Thread thread = new Thread(Listen);
                    thread.SetApartmentState(ApartmentState.STA);//当前线程状态为单线程
                    //当初始化一个线程,把Thread.IsBackground=true的时候,指示该线程为后台线程。后台线程将会随着主线程的退出而退出。
                    thread.IsBackground = true;
                    thread.Start(serverSocket);//开始线程的的执行
                }
                catch (Exception ex) {
                    form.Println($"错误: {ex.Message}");
                }
            }
    
            static void Listen(object so)
            {
                Socket serverSocket = so as Socket;
                while (true)//不断接收来着客户端的请求
                {
                    try {
                        //等待连接并且创建一个负责通讯的socket
                        Socket clientSocket = serverSocket.Accept();
                        //获取链接的IP地址
                        string clientPoint = clientSocket.RemoteEndPoint.ToString();
                        form.Println($"{clientPoint} 上的客户端请求连接。");
                        allClientSockets.Add(clientPoint, clientSocket);//加入字典--一个IP对应一个套接字。。。。
                        form.ComboBoxAddItem(clientPoint);//把连接的客户端的IP加入下拉列表方便通讯。。。
                        string msg = "ip=" + clientPoint;
                        //设置缓冲区,并把IP:具体IP的字符串设置成UTF-8编码的字节数组。。。
                        byte[] sendee = Encoding.UTF8.GetBytes(msg);
                        //相当于Python的range。。。。
                        foreach (string ip in allClientSockets.Keys)
                            if (ip != clientPoint)
                            {
                                //ip为当前遍历到的IP,clientPoint为新加进来的IP
                                allClientSockets[ip].Send(sendee);  //向所有客户端发送新客户端连接的ip信息
                                byte[] sendeeIP = Encoding.UTF8.GetBytes("ip=" + ip);
                                allClientSockets[clientPoint].Send(sendeeIP);  //向新客户端发送所有已有客户端的ip信息
                            }
    
                        //开启一个新线程不停接收消息
                        Thread thread = new Thread(Receive);
                        thread.IsBackground = true;
                        thread.SetApartmentState(ApartmentState.STA);
                        thread.Start(clientSocket);
                    }
                    catch(Exception e) {
                        form.Println($"错误: {e.Message}");
                        break;
                    }
                }
            }
    
            static void Receive(object so)
            {
                //接收缓冲区
                byte[] buf = new byte[1024 * 1024 * 2];
                byte[] cacheBuf = new byte[1024 * 1024 * 2];
                Socket clientSocket = so as Socket;
                string clientPoint = clientSocket.RemoteEndPoint.ToString();
                while (true) {
                    try {
                        //获取发送过来的消息容器
                        //Socket.Receive返回收到的字节数。
                        int len = clientSocket.Receive(buf);
                        //有效字节为0则跳过
                        if (len == 0) break;
    
                        if (buf[0] == 1) //如果接收的字节数组的第一个字节是1,说明接收的是文件
                        {
                            form.Println("来自" + clientSocket.RemoteEndPoint + ": 的文件buf正在接收");
                            Buffer.BlockCopy(buf, 0, cacheBuf, 0, 1024 * 1024 * 2);
                            Thread thread = new Thread(Receive);
                            thread.IsBackground = true;
                            thread.SetApartmentState(ApartmentState.STA);
                            thread.Start(clientSocket);
                        }
                        else if(buf[0] == 2)
                        {
                            form.Println("来自" + clientSocket.RemoteEndPoint + ": 的图片buf正在接收");
                            //发送并显示图片
                            MemoryStream ms = new MemoryStream();
                            ms.Write(buf, 1, len - 1);
                            Image img = Image.FromStream(ms);
                            form.SetPic(img);
                            form.Println("来自" + clientSocket.RemoteEndPoint + ": 的图片接收成功");
                        }
                        else
                        {
                            string s = Encoding.UTF8.GetString(buf, 0, len);
                            form.Println($"{clientPoint}: {s}");
                            if (s.StartsWith("message="))//判断字符串是否以message=开头
                                foreach (String t in allClientSockets.Keys)
                                {
                                    if (clientPoint != t)
                                    {
                                        byte[] sendee = Encoding.UTF8.GetBytes($"{clientPoint}: {s}");
                                        allClientSockets[t].Send(sendee);
                                    }
                                }
                            else if (s.StartsWith("ip="))
                            {
                                string ip = Regex.Match(s, @"d{1,3}.d{1,3}.d{1,3}.d{1,3}:d{1,5}").Value;
                                byte[] sendee = Encoding.UTF8.GetBytes($"{clientPoint}: {s.Substring(3+ ip.Length)}");
                                allClientSockets[ip].Send(sendee);
                            }
                            if (s.StartsWith("TO="))
                            {
                                string ip = Regex.Match(s, @"d{1,3}.d{1,3}.d{1,3}.d{1,3}:d{1,5}").Value;
                                allClientSockets[ip].Send(cacheBuf);
                            }
                        }
    
                        //byte[] sendee = Encoding.UTF8.GetBytes("服务器返回信息");
                        //clientSocket.Send(sendee);
                    }
                    catch (SocketException e) {
                        allClientSockets.Remove(clientPoint);
                        form.ComboBoxRemoveItem(clientPoint);  //移除服务端相应用户列表项
    
                        string msg = "Removeip=" + clientPoint;  //给所有客户端发送移除消息
                        byte[] sendee = Encoding.UTF8.GetBytes(msg);
                        foreach (string ip in allClientSockets.Keys)
                                allClientSockets[ip].Send(sendee);
                        form.Println($"客户端 {clientSocket.RemoteEndPoint} 中断连接: {e.Message}");
                        clientSocket.Close();
                        break;
                    }
                    catch(Exception e) {
                        form.Println($"错误: {e.Message}");
                    }
                }
            }
    
            static void SendMsg(object sender, EventArgs e)
            {
                if(form.GetComboBoxItem() == null)
                {
                    string msg = form.GetMsgText();
                    if (msg == "") return;
                    byte[] sendee = Encoding.UTF8.GetBytes($"服务器:{msg}");
                    foreach (Socket s in allClientSockets.Values)
                        s.Send(sendee);
                    form.Println(msg);
                    form.ClearMsgText();
                }
                else
                {
                    string msg = form.GetMsgText();
                    if (msg == "") return;
                    byte[] sendee = Encoding.UTF8.GetBytes($"服务器:{msg}");
                    Socket socketSend = allClientSockets[form.GetComboBoxItem()];
                    socketSend.Send(sendee);
                    form.Println(msg);
                    form.ClearMsgText();
                }
            }
    
        }
    }
    
    

    form.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    
    namespace ChatoServer
    {
        public partial class MainForm : Form
        {
            public MainForm(EventHandler bListenClick, EventHandler bSendClick)
            {
                InitializeComponent();
                //这样设计方便代码分离。。。。
                
                this.buttonListen.Click += bListenClick;
                this.buttonSend.Click += bSendClick;
                
            }
    
            public string GetIPText()
            {
                //获取IP地址
                return this.textBoxIP.Text;
            }
            
            public int GetPort()
            {
                //获取端口地址
                return (int)this.numericUpDownPort.Value;
            }
    
            public string GetMsgText()
            {
                //获取输入框信息
                return this.textBoxSendee.Text.Trim();
            }
    
            public void SetPic(Image img)
            {
                //写入图片文件
                this.pictureBox1.Image = img;
            }
    
            public void ClearMsgText()
            {
                //清除文本框信息
                this.textBoxSendee.Clear();
            }
            //C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针
            delegate void VoidString(string s);
            //C#中禁止跨线程直接访问控件
            //InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它。
            public void Println(string s)
            {
                if (this.textBoxMsg.InvokeRequired) {
                    //如果InvokeRequired==true表示其它线程需要访问控件,那么调用invoke来转给控件owner处理。
                    VoidString println = Println;//此时的println和Println等同
                    //使用委托跨线程访问,这个事情必须自己接受其他委托做,不能其他线程直接做。
                    this.textBoxMsg.Invoke(println, s);
                }
                else {
                    this.textBoxMsg.AppendText(s + Environment.NewLine);
                    //this.textBoxMsg1.
                }
            }
    
            public delegate DialogResult InvokeDelegate(Form parent);
            public DialogResult XShowDialog(Form parent)
            {
                if (parent.InvokeRequired)
                {
                    InvokeDelegate xShow = new InvokeDelegate(XShowDialog);
                    parent.Invoke(xShow, new object[] { parent });
                    return DialogResult;
                }
                return this.ShowDialog(parent);
            }
    
            public void ComboBoxAddItem(string s)
            {
                //comboBoxAllClients下拉列表框的集合
                if (this.comboBoxAllClients.InvokeRequired) {
                    VoidString cbAddItem = ComboBoxAddItem;
                    this.textBoxMsg.Invoke(cbAddItem, s);
                }
                else {
                    this.comboBoxAllClients.Items.Add(s);
                }
            }
            public void ComboBoxRemoveItem(string s)
            {
                if (this.comboBoxAllClients.InvokeRequired) {
                    VoidString cbRmItem = ComboBoxRemoveItem;
                    this.textBoxMsg.Invoke(cbRmItem, s);
                }
                else {
                    this.comboBoxAllClients.Items.Remove(s);
                }
            }
    
            public string GetComboBoxItem()
            {
                if (this.comboBoxAllClients.SelectedItem == null)
                    return null;
                else
                    return this.comboBoxAllClients.SelectedItem.ToString();
            }
    
            private void MainForm_Load(object sender, EventArgs e)
            {
                this.buttonListen.PerformClick();
            }
        }
    }
    
    

    其实一看Program.cs就能明白了。这里编码都不太难。
    下面就是部署到服务器了。把写好的代码编译成.exe文件,放到服务器上。因为.exe只能在Windows上运行,所以为了在Linux环境下运行,需要在Linux上下载mono.下载好mono之后就直接 mono xxx.exe这样服务端就在服务器上运行起来了,再在本机打开客户端就可以通信啦。

  • 相关阅读:
    CodeForces Round #678(Div2) E.Complicated Computations Mex性质,权值线段树
    P6075 子集选取 思维
    HDU-4747 Mex 线段树应用 Mex性质
    P1273 有线电视网 树形DP 树上背包
    P6786 GCDs & LCMs 数学推导
    [CTSC1997] 选课 树上背包
    Gym-101915K Poor Ramzi 区间DP
    [MdOI R2] Odyssey 拓扑排序上DP
    CodeForces Div3.F
    二分-B
  • 原文地址:https://www.cnblogs.com/simple5960/p/13039923.html
Copyright © 2020-2023  润新知