IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> C#利用套接字实现数据发送 -> 正文阅读

[网络协议]C#利用套接字实现数据发送

一、TCP/UDP

Socket 套接字是支持TCP/IP协议的网络通信的基本操作单元。可以将套接字看作不同主机间的进程进行双向通信的端点,它构成了单个主机内及整个网络间的编程界面。
套接字的工作原理:
通过互联网进行通信,至少需要一对套接字,其中一个运行于客户机端,称之为ClientSocket,另一个运行于服务器端,称之为ServerSocket。
套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。

1.1 TCP

TCP协议提供的是端到端服务。TCP协议所提供的端到端的服务是保证信息一定能够到达目的地址。它是一种面向连接的协议。

TCP编程的服务器端一般步骤:

  1. 创建一个socket,用函数socket()
  2. 绑定IP地址、端口等信息到socket上,用函数bind()
  3. 开启监听,用函数listen()
  4. 接收客户端上来的连接,用函数accept()
  5. 收发数据,用函数send()和recv(),或者read()和write()
  6. 关闭网络连接
  7. 关闭监听

客户端一般步骤:

  1. 创建一个socket,用函数socket()
  2. 设置要连接的对方的IP地址和端口等属性
  3. 连接服务器,用函数connect()
  4. 收发数据,用函数send()和recv(),或者read()和write()
  5. 关闭网络连接

1.2 UDP

UDP协议提供了一种不同于TCP协议的端到端服务。UDP协议所提供的端到端传输服务是尽力而为(best-effort)的,即UDP套接字将尽可能地传送信息,但并不保证信息一定能成功到达目的地址,而且信息到达的顺序与其发送顺序不一定一致。

服务器端:

  1. 创建一个socket,用函数socket()
  2. 绑定IP地址、端口等信息到socket上,用函数bind()
  3. 循环接收数据,用函数recvfrom()
  4. 关闭网络连接

客户端:

  1. 创建一个socket,用函数socket()
  2. 设置对方的IP地址和端口等属性
  3. 发送数据,用函数sendto()
  4. 关闭网络连接

二、控制台程序使用 UDP 通信

编译工具:visual studio 2019

2.1 创建控制台程序

在屏幕上连续输出50行“hello cqjtu!重交物联2019级”

1.创建新项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建成功,如下图所示:
在这里插入图片描述

2.代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 50; i++)
            {
                Console.WriteLine("hello cqjtu!重交物联2019级", (i + 1));
            }
            System.Console.ReadKey();
        }
    }
}

3.编译输出结果
在这里插入图片描述
在这里插入图片描述

2.2 UDP通信

打开一个网络UDP 套接字,向另一台室友电脑发送这50行消息

我的电脑运行客户端程序,室友电脑运行服务器端程序,从而实现简单的通信功能

使用网络协议需要引入头文件.Net和.Net.Sockets,要实现两台电脑需在同一个网络下

服务端:

步骤:
1.实例化并设置socket实例对象
创建IP地址和端口

IPEndPoint ip = new IPEndPoint(IPAddress.Any,8001);

绑定监听地址:

socket.Bind(ip);

2.连接

IPEndPoint send = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(send);

3.接收客户端发送过来的消息

receive = socket.ReceiveFrom(data, ref Remote);

4.完整代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            int receive;
            byte[] data = new byte[1024];

            //得到本机IP
            IPEndPoint ip = new IPEndPoint(IPAddress.Any,8080);

            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp);
            //绑定网络地址
            socket.Bind(ip);

            Console.WriteLine("This is a Server, host name is {0}", Dns.GetHostName());

            //等待客户机连接
            Console.WriteLine("正在连接客户机...");

            //得到客户机的IP
            IPEndPoint send = new IPEndPoint(IPAddress.Any, 0);
            EndPoint Remote = (EndPoint)(send);
            receive = socket.ReceiveFrom(data, ref Remote);
            Console.WriteLine("Message received from {0}: ", Remote.ToString());
            Console.WriteLine(Encoding.UTF8.GetString(data, 0, receive));

            //与客户机连接成功,返回连接成功信息
            string message = "你好!客户端";

            //字符串与字节数组相互转换
            data = Encoding.UTF8.GetBytes(message);

            //发送信息
            socket.SendTo(data, data.Length, SocketFlags.None, Remote);
            while(true)
            {
                data = new byte[1024];
                //接收信息
                receive = socket.ReceiveFrom(data, ref Remote);
                Console.WriteLine(Encoding.UTF8.GetString(data, 0, receive));
            }
        }
    }
}

客户端:

客户端程序与服务器程序非常类似。由于客户机不需要在指定的UDP端口等待流入的数据,所以不使用Bind()方法来绑定监听地址。而是在数据发送时使用系统随机指定的一个UDP端口。

完整代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace test1
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] data = new byte[1024];

            //构建TCP 服务器
            Console.WriteLine("This is a Client, host name is {0}", Dns.GetHostName());

            //设置服务IP(这个IP地址是服务器的IP),设置端口号
            IPEndPoint ip = new IPEndPoint(IPAddress.Parse("192.168.0.102"), 8080);

            //定义网络类型,数据连接类型和网络协议UDP
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            //与服务器端连接成功,服务器端接收到消息
            string welcome = "你好! 服务器端";
            data = Encoding.UTF8.GetBytes(welcome);
            server.SendTo(data, data.Length, SocketFlags.None, ip);
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint Remote = (EndPoint)sender;

            data = new byte[1024];
            //对于不存在的IP地址,加入此行代码后,可以在指定时间内解除阻塞模式限制
            int recv = server.ReceiveFrom(data, ref Remote);
            Console.WriteLine("Message received from {0}: ", Remote.ToString());
            Console.WriteLine(Encoding.UTF8.GetString(data, 0, recv));

            Console.WriteLine("按下任意按键开始发送...");
            Console.ReadKey();
            int i = 0;
            while (i<=50)
            {
                string s = "hello cqjtu!重交物联2019级" + i;
                Console.WriteLine(s);
                server.SendTo(Encoding.UTF8.GetBytes(s), Remote);
                i++;
            }
            Console.WriteLine("发送完毕!");
            server.Close();
            Console.WriteLine("按下任意按键退出发送...");
            Console.ReadKey();
        }

    }
}

注意:要先运行服务器端代码,在运行客户端代码

2.3 结果

服务器端:
在这里插入图片描述
客户端:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.4 抓包分析数据

在方框内输入 “ UDP ” 过滤包,然后就可以看到下面的信息,这些就是室友电脑发给我的 50 条数据,下面开始分析这些数据,只选择其中一条分析。
当室友电脑给我发送消息的时候,就能抓取包,貌似自己电脑给自己发消息抓不到包,建议两台电脑进行发送数据抓包

客户端抓包:

源地址是192.168.43.112,目的地址是192.168.43.236,使用协议为UDP,端口号为8080,长度为33字节

在这里插入图片描述
在这里插入图片描述

服务器端抓包:
在这里插入图片描述

三、Form窗口程序使用 TCP 通信

3.1 创建项目

在这里插入图片描述

创建成功如下图所示:
在这里插入图片描述

3.2 设计界面

在这里插入图片描述

我们这里需要3个控件,2 个 TextBox 控件和 1 个 Button 控件
在这里插入图片描述

1.设计消息显示界面

  • 点击需要设计的控件,可以看到右下角控件的属性
    在这里插入图片描述

  • 点击三角形,勾选MultiLine(多行文本框),这样textbox就可以竖着拉长

在这里插入图片描述

  • 添加垂直滚动条:右下角找到 ScrollBars 属性,设置参数为 Vertical
    在这里插入图片描述

  • 设置边界样式:找到 BorderStyle ,参数设置为 FixedSingle
    在这里插入图片描述

  • 控件name为textBox1

注意:所有控件的name属性不能重复,也不能有中文

在这里插入图片描述

  • 设置消息显示界面的 TextBox 不可编辑:找到 Enabled 属性,参数设为 False 。
    在这里插入图片描述

2.设计消息发送框

  • name为textBox2
    在这里插入图片描述

  • 设置文本样式,点击Font属性
    在这里插入图片描述
    根据喜好,自行选择
    在这里插入图片描述

3.设计Button按钮

  • name为button1
    在这里插入图片描述

  • 修改Text属性
    在这里插入图片描述

4.设计窗体

  • name属性:Form1
    在这里插入图片描述

  • text属性:客户端
    在这里插入图片描述

  • AcceptButton属性:设置为button控件的name属性(当我们点击电脑回车键的时候,就相当于点击发送按钮)
    在这里插入图片描述
    在这里插入图片描述

3.3 代码编写

红色方框后就是我们双击button控件后,自动生成的函数体
在这里插入图片描述

注意头文件.Net和.Net.Sockets

客户端:

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;
using System.Net.Sockets;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                string str;
                //做连接准备,ip地址为服务器ip地址
                IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse("192.168.0.102"), 8080);
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                //开始连接
                str = "连接服务器中...";
                textBox1.AppendText(str + Environment.NewLine);
                socket.Connect(iPEndPoint);//连接到服务器

                //发送消息
                string sendmessage = textBox2.Text;
                str = "消息内容: " + sendmessage;
                textBox1.AppendText(str + Environment.NewLine);
                byte[] bs = Encoding.UTF8.GetBytes(sendmessage);
                str = "正在发送给服务器...";
                textBox1.AppendText(str + Environment.NewLine);
                socket.Send(bs, bs.Length, 0);//发送信息

                //接收服务器端反馈信息
                string recvStr = "";
                byte[] recvBytes = new byte[1024];
                int bytes;
                bytes = socket.Receive(recvBytes, recvBytes.Length, 0);//接收服务器端返回的信息
                recvStr += Encoding.UTF8.GetString(recvBytes, 0, bytes);
                str = "服务器返回信息:" + recvStr;
                textBox1.AppendText(str + Environment.NewLine);

                //关闭socket
                socket.Close();
            }
            //异常处理
            catch (ArgumentNullException f)
            {
                string str = "ArgumentNullException: " + f.ToString();
                textBox1.AppendText(str + Environment.NewLine);
            }
            catch (SocketException f)
            {
                string str = "ArgumentNullException: " + f.ToString();
                textBox1.AppendText(str + Environment.NewLine);
            }
            textBox1.AppendText("" + Environment.NewLine);
            textBox2.Text = "";
        }
    }
}

服务器端:

创建一个控制台应用,像前面UDP通信

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            //连接准备
            IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Any, 8080);
            //创建端口对象
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Bind(iPEndPoint);//绑定端口

            //循环监听并处理消息
            while (true)
            {
                i++;
                try
                {
                    Console.WriteLine("Perform operations {0} :", i);
                    Console.WriteLine("************************************");
                    socket.Listen(0);//开始监听
                    Console.WriteLine("等待连接...");

                    //实例化一个新的端口
                    Socket temp = socket.Accept();//为新建连接创建新的Socket
                    Console.WriteLine("连接成功!");

                    //接收客户端发来的消息
                    string recvStr = "";
                    byte[] recvBytes = new byte[1024];
                    int bytes;
                    bytes = temp.Receive(recvBytes, recvBytes.Length, 0);//接收客户端信息
                    recvStr += Encoding.UTF8.GetString(recvBytes, 0, bytes);
                    Console.WriteLine("消息内容:{0}", recvStr);//显示客户端传来的信息

                    //返回给客户端连接成功的信息
                    string sendmessage = "成功发送消息,客户端!";
                    byte[] bs = Encoding.UTF8.GetBytes(sendmessage);
                    temp.Send(bs, bs.Length, 0);

                    //关闭端口
                    temp.Close();
                    Console.WriteLine("Completed...");
                    Console.WriteLine("-----------------------------------");
                }
                catch (ArgumentNullException e)
                {
                    Console.WriteLine("ArgumentNullException: {0}", e);
                }
                catch (SocketException e)
                {
                    Console.WriteLine("SocketException: {0}", e);
                }
            }
        }
    }
}

3.4 结果展示

客户端:
在这里插入图片描述

服务器端:

在这里插入图片描述

3.5 抓包分析数据

在这里插入图片描述

看一下 UDP 包和 TCP 包的区别:
UDP:
Internet Protocl Version 4:IPv4协议
User Datagram Protocol:UDP协议
TCP:
Internet Protocl Version 4:IPv4协议
Transmission Control Protocol:TCP协议
在这里插入图片描述
可以看到都是IPv4协议,区别在于第四行,其余都差不多

3.6 端口扫描程序

单线程

单线程方式去进行会非常慢,会非常卡,只能通过停止调试按钮去关闭

在这里插入图片描述

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.Threading;


namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        //主机地址
        private string hostAddress;
        //起始端口
        private int start;
        //终止端口
        private int end;
        //端口号
        private int port;
        //定义线程对象
        //private Thread scanThread;
        public Form1()
        {
            InitializeComponent();
        }

        private void label2_Click(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
               
                //初始化
                textBox4.Clear();
                label5.Text = "0%";
                //获取ip地址和始末端口号
                hostAddress = textBox1.Text;
                start = Int32.Parse(textBox2.Text);
                end = Int32.Parse(textBox3.Text);
                if (decideAddress())
                {
                    //让输入的textbox只读,无法改变
                    textBox1.ReadOnly = true;
                    textBox2.ReadOnly = true;
                    textBox3.ReadOnly = true;
                    //设置进度条的范围
                    progressBar1.Minimum = start;
                    progressBar1.Maximum = end;
                    //显示框显示
                    textBox4.AppendText("端口扫描器 v1.0.0" + Environment.NewLine + Environment.NewLine);
                    //调用端口扫描函数
                    PortScan();
                }
                else
                {
                    //若端口号不合理,弹窗报错
                    MessageBox.Show("输入错误,端口范围为[0-65536]!");
                }
            }
            catch
            {
                //若输入的端口号为非整型,则弹窗报错
                MessageBox.Show("输入错误,端口范围为[0-65536]!");
            }
        }

        private bool decideAddress()
        {
            //判断端口号是否合理
            if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
                return true;
            else
                return false;
        }

        private void PortScan()
        {
            double x;
            string xian;
            //显示扫描状态
            textBox4.AppendText("开始扫描...(可能需要请您等待几分钟)" + Environment.NewLine + Environment.NewLine);
            //循环抛出线程扫描端口
            for (int i = start; i <= end; i++)
            {
                x = (double)(i - start + 1) / (end - start + 1);
                xian = x.ToString("0%");
                port = i;
                //调用端口i的扫描操作
                Scan();
                //进度条值改变
                label5.Text = xian;
                label5.Refresh();
                progressBar1.Value = i;
            }
            textBox4.AppendText(Environment.NewLine + "扫描结束!" + Environment.NewLine);
            //输入框textbox只读属性取消
            textBox1.ReadOnly = false;
            textBox2.ReadOnly = false;
            textBox3.ReadOnly = false;
        }

        private void Scan()
        {
            int portnow = port;
            //创建TcpClient对象,TcpClient用于为TCP网络服务提供客户端连接
            TcpClient objTCP = null;
            try
            {
                //用于TcpClient对象扫描端口
                objTCP = new TcpClient(hostAddress, portnow);
                //扫描到则显示到显示框
                textBox4.AppendText("端口 " + port + " 开放!" + Environment.NewLine);
            }
            catch
            {
                //未扫描到,则会抛出错误
            }
        }
    }
}

结果:
在这里插入图片描述

多线程

相比于单线程,多线程快很多
注意:
构造函数里的:
CheckForIllegalCrossThreadCalls = false;
可以直接跳过跨线程检查,如果程序不当,会造成死循环,在本程序中不直接跳过会出现问题;建议使用委托 delegate

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.Threading;


namespace WindowsFormsApp3
{
    public partial class Form1 : Form
    {
        //主机地址
        private string hostAddress;
        //起始端口
        private int start;
        //终止端口
        private int end;
        //端口号
        private int port;
        //定义线程对象
        private Thread scanThread;
        //定义端口状态数据(开放则为true,否则为false)
        private bool[] done = new bool[65526];
        private bool OK;

        public Form1()
        {
            InitializeComponent();
            //不进行跨线程检查
            CheckForIllegalCrossThreadCalls = false;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                //初始化
                textBox4.Clear();
                label5.Text = "0%";
                //获取ip地址和始末端口号
                hostAddress = textBox1.Text;
                start = Int32.Parse(textBox2.Text);
                end = Int32.Parse(textBox3.Text);
                if (decideAddress())
                {
                    textBox1.ReadOnly = true;
                    textBox2.ReadOnly = true;
                    textBox3.ReadOnly = true;
                    //创建线程,并创建ThreadStart委托对象
                    Thread process = new Thread(new ThreadStart(PortScan));
                    process.Start();
                    //设置进度条的范围
                    progressBar1.Minimum = start;
                    progressBar1.Maximum = end;
                    //显示框显示
                    textBox4.AppendText("端口扫描器 v1.0.0" + Environment.NewLine + Environment.NewLine);
                }
                else
                {
                    //若端口号不合理,弹窗报错
                    MessageBox.Show("输入错误,端口范围为[0-65536]!");
                }
            }
            catch
            {
                //若输入的端口号为非整型,则弹窗报错
                MessageBox.Show("输入错误,端口范围为[0-65536]!");
            }
        }

        private bool decideAddress()
        {
            //判断端口号是否合理
            if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
                return true;
            else
                return false;
        }

        private void PortScan()
        {
            double x;
            string xian;
            //显示扫描状态
            textBox4.AppendText("开始扫描...(可能需要请您等待几分钟)" + Environment.NewLine + Environment.NewLine);
            //循环抛出线程扫描端口
            for (int i = start; i <= end; i++)
            {
                x = (double)(i - start + 1) / (end - start + 1);
                xian = x.ToString("0%");
                port = i;
                //使用该端口的扫描线程
                scanThread = new Thread(new ThreadStart(Scan));
                scanThread.Start();
                //使线程睡眠
                System.Threading.Thread.Sleep(100);
                //进度条值改变
                label5.Text = xian;
                progressBar1.Value = i;
            }
            while (!OK)
            {
                OK = true;
                for (int i = start; i <= end; i++)
                {
                    if (!done[i])
                    {
                        OK = false;
                        break;
                    }
                }
                System.Threading.Thread.Sleep(1000);
            }
            textBox4.AppendText(Environment.NewLine + "扫描结束!" + Environment.NewLine);
            textBox1.ReadOnly = false;
            textBox2.ReadOnly = false;
            textBox3.ReadOnly = false;
        }

        private void Scan()
        {
            int portnow = port;
            //创建线程变量
            Thread Threadnow = scanThread;
            //扫描端口,成功则写入信息
            done[portnow] = true;
            //创建TcpClient对象,TcpClient用于为TCP网络服务提供客户端连接
            TcpClient objTCP = null;
            try
            {
                //用于TcpClient对象扫描端口
                objTCP = new TcpClient(hostAddress, portnow);
                //扫描到则显示到显示框
                textBox4.AppendText("端口 " + port + " 开放!" + Environment.NewLine);
            }
            catch
            {
                //未扫描到,则会抛出错误
            }
        }
    }
}

结果:
在这里插入图片描述

四、参考文献

C#使用TCP/UDP协议通信并用Wireshark抓包分析数据
C#利用套接字实现数据发送
编写端口扫描器程序

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-11-25 08:28:41  更:2021-11-25 08:30:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:32:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码