SOCKET 网络编程:服务器和客户端

SOCKET

scoket: 程序间的通讯方式,就像人们用【电话】通讯,程序用【SCOKET】通讯。
SOCKET俗称【套接字】,是用来描述【IP地址】和【端口】,是一个通讯链的句柄。
IP地址: 用于查找计算机在网络中的位置
端口号: 用于查找程序在计算机的位置
协议: 计算机之间联系的语言,需要统一的语言方式,计算机才能有效沟通。
UDP协议: 效率高,但不稳定,容易数据丢失
在这里插入图片描述
TCP协议: 安全,稳定,但效率较低
在这里插入图片描述

协议概念

客户端与服务器之间的【传输规则】与【解析规则】,博主写的服务器和客户端也存在着协议,不然服务器和客户端就解析不了接收到的消息。
【TCP协议】和【UDP协议】是【国际通用】的传输规则和解析规则,采用这套协议我们可以在国际上与其他【使用相同协议】的服务器、客户端进行交互。

创建Scoket服务器

在这里插入图片描述

知识点:
  1. 创建一个负责监听IP地址和端口号的SOCKET:
    Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  2. 获取本地IP地址:IPAddress ip = IPAddress.Any;
  3. 创建客户端链接IP和端口地址的对象:IPEndPoint point = new IPEndPoint(ip,Convert.ToInt32(textBox_Port.Text));
  4. SOCKET监听客户端:socketWatch.Bind(point);
  5. 等待客户的连接,并且创建与之通信的SOCKET(等待状态程序会假死,所以安排线程来执行):
    socketSend = socketWatch.Accept();
  6. 接受客户端发送的消息,返回值为有效字节数(用线程循环接受消息):int r = socketSend.Receive(buffer);
  7. 向客户端发送消息:dic[ip](链接客户端的SOCKET).Send(newBuffer);
  8. 将多个【链接客户端的SOCKET】存放在键值对中。
  9. 多用try{ }catch{ },可以减少程序使用时出现的异常报错,影响用户体验。(当然调试阶段少于try-catch)
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 _03_demo
{
    public partial class Form1 : Form
    {
        Dictionary<string, Socket> dic = new Dictionary<string, Socket>();
        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        /// 开启客户端连接的监听
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Monitor_Click(object sender, EventArgs e)
        {
            //创建一个负责监听IP地址和端口号的SOCKET
            Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Any;
            //创建端口号对象
            IPEndPoint point = new IPEndPoint(ip,Convert.ToInt32(textBox_Port.Text));
            //监听
            socketWatch.Bind(point);
            ShowMsg("监听成功!");
            //同时监听人数
            socketWatch.Listen(10);
            //创建新线程执行监听客户端的连接,避免socket等待客户连接是造成假死
            Thread th = new Thread(Listen);
            th.IsBackground = true;
            th.Start(socketWatch);
        }
        /// <summary>
        /// 等待客户的连接,并且创建与之通信的SOCKET
        /// </summary>
        /// 
        Socket socketSend;
        void Listen(object o)
        {
            Socket socketWatch = o as Socket;
            //不断地等待与客户的连接
            while (true)
            {
                //等待客户的连接,并且创建与之通信的SOCKET
                socketSend = socketWatch.Accept();
                //把客户端地址加入键值对
                dic.Add(socketSend.RemoteEndPoint.ToString(),socketSend);
                //把客户端的地址加入下拉框
                comboBox_IP.Items.Add(socketSend.RemoteEndPoint.ToString());
                ShowMsg(socketSend.RemoteEndPoint.ToString() + ":连接成功!");
                //新建线程执行接收客户发来的消息
                Thread th = new Thread(Receive);
                th.IsBackground = true;
                th.Start(socketSend);
            }
        }
        /// <summary>
        /// 接收客户端发来的消息,并输出到文本框
        /// </summary>
        /// <param name="o"></param>
        void Receive(object o)
        {
            while (true)
            {
                try
                {
                    Socket socketSend = o as Socket;
                    //客户端连接成功后,服务器应该接收客户端发来的消息
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    //返回值为实际接收的有效字节
                    int r = socketSend.Receive(buffer);
                    //判断客户端是否停止运行,如果客户端停止运行,就退出循环
                    if (r == 0)
                    {
                        break;
                    }
                    //将字节数据转成字符串
                    string s = Encoding.UTF8.GetString(buffer, 0, r);
                    ShowMsg(socketSend.RemoteEndPoint + ":" + s);
                }
                catch
                {

                }
            }
            
        }
        /// <summary>
        /// 打印文本在日志框中
        /// </summary>
        /// <param name="str"></param>
        void ShowMsg(string str)
        {
            textBox_Log.AppendText(str +"\r\n");
        }
        /// <summary>
        /// 关闭跨线程操作的检查
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            Control.CheckForIllegalCrossThreadCalls = false;
        }
        /// <summary>
        /// 发送消息按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button4_Click(object sender, EventArgs e)
        {
            Thread th = new Thread(Send);
            th.IsBackground = true;
            th.Start();
        }
        /// <summary>
        /// 获取文本框内容,转成字符发送给客户端
        /// </summary>
        void Send()
        {
            string s = textBox_Input.Text.Trim();
            byte[] buffer = Encoding.UTF8.GetBytes(s);
            List<byte> list = new List<byte>();
            //第一个字节为0,表示传送的数据是文本
            list.Add(0);
            list.AddRange(buffer);
            byte[] newBuffer = list.ToArray();
            string ip = comboBox_IP.SelectedItem.ToString();
            dic[ip].Send(newBuffer);
        }
        /// <summary>
        /// 选择文件,并把文件地址输入文本框内
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button2_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.InitialDirectory = @"C:\Users\shen\Desktop";
            ofd.Filter = "所有文件|*.*";
            ofd.ShowDialog();

            textBox_File.Text = ofd.FileName;
        }
        /// <summary>
        /// 发送文件给客户端
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button3_Click(object sender, EventArgs e)
        {
            string path = textBox_File.Text;
            using(FileStream fsr = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                byte[] buffer = new byte[1024 * 1024 * 5];
                int r = fsr.Read(buffer, 0, buffer.Length);
                List<byte> list = new List<byte>();
                //第一个字节为0,表示传送的是文件
                list.Add(1);
                list.AddRange(buffer);
                byte[] newBuffer = list.ToArray();
                string ip = comboBox_IP.SelectedItem.ToString();
                dic[ip].Send(newBuffer,0,r+1,SocketFlags.None);
            }
        }
        /// <summary>
        /// 发送震动按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button5_Click(object sender, EventArgs e)
        {
            byte[] buffer = new byte[1];
            //第一个字节为0,表示传送的是震动指令
            buffer[0] = 2;
            string ip = comboBox_IP.SelectedItem.ToString();
            dic[ip].Send(buffer);
        }
    }
}

创建Socket客户端

在这里插入图片描述

知识点:
  1. 运行完服务器后,右键客服端项目,【调试-启动新实例】,可以打开多个客户端
    在这里插入图片描述
  2. CMD控制台输入ipconfig,可以查询本机IP地址
  3. 创建负责通讯的SOCKET:socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  4. 将字符串ip地址转成ip地址实例:IPAddress ip = IPAddress.Parse(textBox_IP.Text);
  5. 创建服务器IP和端口地址的对象:IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(textBox_Port.Text));
  6. 连接服务器:socketSend.Connect(point);
  7. 发送消息给服务器:socketSend.Send(buffer);
  8. 接受服务器发送的消息,返回值为有效字节数(用线程循环接受消息):int r = socketSend.Receive(buffer);
  9. 在当前窗体打开【保存文件对话框】:sfd.ShowDialog(this);
  10. 获取当前窗体的坐标:int X = this.Location.X; int Y = this.Location.Y;
  11. 重新赋值当前窗体的新坐标:this.Location = new Point(this.Location.X - 10, this.Location.Y - 10);
  12. 多用try{ }catch{ },可以减少程序使用时出现的异常报错,影响用户体验。(当然调试阶段少于try-catch)
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 _04_demo
{
    public partial class Form1 : Form
    {
        Socket socketSend;

        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        /// 客户端给服务器发消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button2_Click(object sender, EventArgs e)
        {
            //获取输入框的字符串
            string s = textBox_input.Text.Trim();
            //把字符串转成字节数组
            byte[] buffer = Encoding.UTF8.GetBytes(s);
            //发送消息给服务器
            socketSend.Send(buffer);
        }
        /// <summary>
        /// 把文本输出到日志框
        /// </summary>
        /// <param name="s"></param>
        void ShowMsg(string s)
        {
            textBox_Log.AppendText(s + "\n");
        }
        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Link_Click(object sender, EventArgs e)
        {
            //创建负责通讯的SOCKET
            socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //将字符串ip地址转成ip地址实例
            IPAddress ip = IPAddress.Parse(textBox_IP.Text);
            //获取IP地址和端口号
            IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(textBox_Port.Text));
            //连接服务器
            socketSend.Connect(point);
            ShowMsg("连接成功!");
            //创建线程,执行接收服务器消息
            Thread th = new Thread(Receive);
            th.IsBackground = true;
            th.Start();
        }
        /// <summary>
        /// 不断接收服务器的消息
        /// </summary>
        void Receive()
        {
            while (true)
            {


                //创建字节数组,接收服务器的消息
                byte[] buffer = new byte[1024 * 1024 * 2];
                //接收服务器的消息,返回值为有效字节数
                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\shen\Desktop";
                    sfd.Filter = "所有文件|*.*";
                    sfd.Title = "保存文件";
                    sfd.ShowDialog(this);
                    string path = sfd.FileName;
                    using(FileStream fsw = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
                    {
                        fsw.Write(buffer, 1, r - 1);
                    }
                    MessageBox.Show("保存成功!");
                }
                else if (buffer[0] == 2)
                {
                    Shake();

                }
                

            }
        }
        /// <summary>
        /// 关闭跨线程操作的检查
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            Control.CheckForIllegalCrossThreadCalls = false;
        }
        /// <summary>
        /// 窗体震动效果
        /// </summary>
        void Shake()
        {
            for(int i = 0; i < 500; i++)
            {
                this.Location = new Point(this.Location.X - 10, this.Location.Y - 10);
                this.Location = new Point(this.Location.X + 20, this.Location.Y + 20);
                this.Location = new Point(this.Location.X - 10, this.Location.Y - 10);
            }
        }
    }
}
  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值