热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

同步传输字符串

同步传输字符串 接下来考虑着一种情况,完成一个简单的文本通信:(1).客户端将字符串发送到服务端,服务端接受字符串并显示(2).服务端将字符串由英文的小写转换为大写,然后发回给客户

同步传输字符串

 

接下来考虑着一种情况,完成一个简单的文本通信:

(1).客户端将字符串发送到服务端,服务端接受字符串并显示

(2).服务端将字符串由英文的小写转换为大写,然后发回给客户端,客户端接受并显示.

 

客户端发送,服务端接受并输出

 

1.服务端程序

 

可以在TcpClient上调用GetStream()方法来获得连接到远程计算机的网络流NetworkStream.当在客户端调用时,它获得连接服务端的流;当在服务端调用时,它获得连接客户端的流.

 

先看服务端的代码实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
using System.Net;
using System.Net.Sockets;
namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            const int BufferSize = 8192;//缓存大小,8192字节
 
            Console.WriteLine("Server is running...");
            IPAddress ip = new IPAddress(new byte[] { 192, 168, 3, 19 });
            TcpListener listener = new TcpListener(ip,1621);
 
            listener.Start();//开始监听
 
            Console.WriteLine("Start Listening...");
 
            //获取一个连接,中断方法
            TcpClient remoteClient = listener.AcceptTcpClient();
 
            //打印连接到的客户端信息
            Console.WriteLine("Client Connected ! Local:{0}<-- Client:{1}",remoteClient.Client.LocalEndPoint,remoteClient.Client.RemoteEndPoint);
 
            //获得流,并写入buffer中
 
            NetworkStream streamToClient = remoteClient.GetStream();
            byte[] buffer = new byte[BufferSize];
            int byteRead = streamToClient.Read(buffer,0,BufferSize);
 
            //获得请求的字符串
            string msg = Encoding.Unicode.GetString(buffer,0,byteRead);
            Console.WriteLine("Received: {0} [{1}bytes]",msg,byteRead);
 
            //按Q退出
            Console.WriteLine("\n\n按Q退出\n\n");
            ConsoleKey key;
            do
            {
                key = Console.ReadKey(true).Key;
            } while (key!=ConsoleKey.Q);
        }
    }
}


等一下,这里楼主有个问题需要问一下,在使用netstat命令查看端口时,你知道根据端口的状态判断哪些端口是可以用来监听的,那些端口不能用来监听吗?


这段程序的上半部分上一次说过了,remoteClient.GetStream()方法获取到了连接至客户端的流,然后从流中读出数据并保存在了buffer,随后使用Encoding.Unicode.GetString()方法,从缓存中获取到实际的字符串.最后将字符串显示在了控制台中.这段代码有个地方需要注意:如果能够读取的字符串的总字节数大于BufferSize,就会出现字符串截断现象,只能读取到不完整的字符串.这是因为缓存的字节数是有限的,在本例中是8192.如果传递的数据字节数比较大,例如图片,音频,文件,则必须采用”分次读取然后转存”的方式,代码如下:

            //获取字符串
            byte buffer = new byte[BufferSize];
            int bytesRead;
            MemoryStream ms = new MemoryStream();
            do
            {
                bytesRead = streamToClient.Read(buffer,0,BufferSize);
            } while (bytesRead>0);


咱们没有使用”分次读取转存”的方式,为啥呢?因为:楼主的水平有限,不想误人子弟.

 

说实话,8192已经很多了.当使用Unicode编码时,8192字节可以保存4096个汉字和英文字符.使用不同的编码方式,占用的字节数有很大的差异.

 

现在不对客户端在任何修改,先调试运行服务器,在运行客户端,会发现服务端在打印完”Client Connected ! Local:192.168.3.19:1621<-- Client:192.168.3.19:4044”之后,再次被阻塞了,没有继续运行,也没有输出”Reading data,{0} bytes...”等任何字符.可见,AcceptTcpClient()方法类似,Read()方法也是同步的,只有当客户端发送数据的时候,服务端才会读取数据,执行此方法,否则会一直等待下去.

 

2.客户端程序

 

接下来编写客户端想服务端发送字符串的代码,与服务端类似,先获取连接到服务端的流,将字符串保存到buffer,再将缓存写入流.写入流这一过程,相当于将消息发往服务端.

        static void Main(string[] args)
        {
            #region MyRegion
            /*
 
            Console.WriteLine("Client is running...");
            TcpClient client;
            for (int i = 0; i <= 2; i++)
            {
                try
                {
                    client = new TcpClient();
                    //与服务器建立连接
                    client.Connect(IPAddress.Parse("192.168.3.19"), 1621);
 
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    return;
                }
                //打印连接到的服务端信息
                Console.WriteLine("Server Connected ! Local:{0} -->Server:{1}", client.Client.LocalEndPoint, client.Client.RemoteEndPoint);
            }
            //按Q退出
            Console.WriteLine("\n\n输入\"Q\"键退出. ");
            ConsoleKey key;
            do
            {
                key = Console.ReadKey(true).Key;
            } while (key != ConsoleKey.Q);*/
            #endregion
            Console.WriteLine("Client is running...");
            TcpClient client;
            try
            {
                client = new TcpClient();
                //与服务器建立连接
                client.Connect(IPAddress.Parse("192.168.1.120"),1621);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
 
            //打印连接到的服务端信息
            Console.WriteLine("Server Connected! Local:{0}-->Server:{1}",client.Client.LocalEndPoint,client.Client.RemoteEndPoint);
 
            string msg = "Hello,readers!";
 
            NetworkStream streamToServer = client.GetStream();
 
            byte[] buffer = Encoding.Unicode.GetBytes(msg);//获得缓存
            streamToServer.Write(buffer,0,buffer.Length);
 
            Console.WriteLine("Sent: {0}",msg);
 
            //按Q退出
            Console.WriteLine("\n\n按Q退出");
            ConsoleKey key;
            do
            {
                key = Console.ReadKey(true).Key;
            } while (key!=ConsoleKey.Q);
 
        }


现在再次运行程序,得到的输出为:

//服务端:
Server is running...
Start Listening...
Client Connected ! Local:192.168.1.120:1621<-- Client:192.168.1.120:5465
Received: Hello,readers! [28bytes]
//客户端:
Client is running...
Server Connected! Local:192.168.1.120:5465-->Server:192.168.1.120:1621
Sent: Hello,readers!


 

可以看到,已经成功的发送和接受了一串字符串,是不是有点成就感了?QQ不过如此了了的事.但不要高兴的太早,对于即时通信程序来说,在客户端和服务端之间,应该是可以不间断的接受和发送消息的.但是上面的代码只能接受客户端发送的一条消息,因为代码已经运行完毕,控制外也已经输出了”按Q退出”.无法再继续执行任何的后续动作.此时如果在开启一个客户端,那么出现的情况是:客户端可以与服务器建立连接,也就是”netstat -a”显示为ESTABLISHED,这是操作系统所知道的;但是由于服务端的程序已经执行到了最后一步,只能输入Q退出,无法再执行任何动作.

 

回想一下,前面说过,当一个服务端需要接受多个客户端连接时,所采用的处理办法是:AcceptTcpClient()方法放在一个while循环中.类似的,当服务端需要同一个客户端的多次请求进行处理时,可以将Read()方法也放入到一个do/while循环中.

 

综合起来就只有四种情况:

 

第一种:如果不使用do/while循环,服务端只有一个listener.AcceptTcpClient()方法和一个TcpClient.GetStream().Read()方法,则服务端只能处理来自一个客户端的一条请求.

 

第二种:如果使用一个do/while循环,并将listener.AcceptTcpClient(0方法和TcpClient.GetStream().Read()方法放在循环中,那么服务端将可以处理多个客户端的一条请求.

 

第三种:使用一个do/while循环,并将listener.AcceptTcpClient()方法放在循环内,TcpClient.GetStream().Read()方法放在循环外,那么可以处理一个客户端的多条请求.

 

第四种:使用两个do/while循环,对它们分别进行嵌套,那么结果是啥?你肯定会说,可以处理多个客户端的多个请求.事实上不是这样的.因为内层的do/while循环总是在为一个客户端服务,它会中断在TcpClient.GetStream().Read()方法的位置,而无法执行完毕.即时可以通过某种方式让内层循环退出,例如,当客户端向服务端发送”exit”字符串时,服务端也只能挨个对客户端提供服务.如果服务端想并行的对多个客户端的多个请求进行服务,那么服务端就需要采用多线程.主线程,即执行外层的do/while循环的线程,它在AcceptTcpClient()获取到一个TcpClient之后,必须将内层的do/while循环交给其他的线程去处理,然后主线程快速的重新回到listener.AcceptTcpClient()的位置,来响应其他的客户端?明白了吗?是不是有点晕,楼主也有点晕,没关系.咱们一个一个讲解.

 

咱们先来看第二种和第三种情况.

对于第二种情况,按照上面描述的那些对代码做一些改动,服务端代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
using System.Net;
using System.Net.Sockets;
using System.IO;
 
namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            const int BufferSize = 8192;//缓存大小,8192字节
 
            Console.WriteLine("Server is running...");
            IPAddress ip = new IPAddress(new byte[] { 192, 168, 1, 120 });
            TcpListener listener = new TcpListener(ip,1621);
 
            listener.Start();//开始监听
 
            Console.WriteLine("Start Listening...");
            do
            {
                //获取一个连接,中断方法
                TcpClient remoteClient = listener.AcceptTcpClient();
                //打印连接到的客户端信息
                Console.WriteLine("Client Connected ! Local:{0}<-- Client:{1}", remoteClient.Client.LocalEndPoint, remoteClient.Client.RemoteEndPoint);
 
                //获得流,并写入buffer中
                NetworkStream streamToClient = remoteClient.GetStream();
                byte[] buffer = new byte[BufferSize];
                int bytesRead = streamToClient.Read(buffer,0,BufferSize);
 
                //获得请求的字符串
                string msg = Encoding.Unicode.GetString(buffer,0,bytesRead);
                Console.WriteLine("Received: {0} [{1}bytes]",msg,bytesRead);
 
            } while (true);
            /*
            //获取一个连接,中断方法
            TcpClient remoteClient = listener.AcceptTcpClient();
 
            //打印连接到的客户端信息
            Console.WriteLine("Client Connected ! Local:{0}<-- Client:{1}",remoteClient.Client.LocalEndPoint,remoteClient.Client.RemoteEndPoint);
 
            //获得流,并写入buffer中
 
            NetworkStream streamToClient = remoteClient.GetStream();
            byte[] buffer = new byte[BufferSize];
            int byteRead = streamToClient.Read(buffer,0,BufferSize);
 
            //获得请求的字符串
            string msg = Encoding.Unicode.GetString(buffer,0,byteRead);
            Console.WriteLine("Received: {0} [{1}bytes]",msg,byteRead);
 
            //按Q退出
            Console.WriteLine("\n\n按Q退出\n\n");
            ConsoleKey key;
            do
            {
                key = Console.ReadKey(true).Key;
            } while (key!=ConsoleKey.Q);
            */
           /* //获取字符串
            byte buffer = new byte[BufferSize];
            int bytesRead;
            MemoryStream ms = new MemoryStream();
            do
            {
                bytesRead = streamToClient.Read(buffer,0,BufferSize);
            } while (bytesRead>0);*/
        }
    }
}


然后启动多个客户端程序,在服务端可以看到这样的情况:

Server is running...
Start Listening...
Client Connected ! Local:192.168.1.120:1621<-- Client:192.168.1.120:6737
Received: Hello,readers! [28bytes]
Client Connected ! Local:192.168.1.120:1621<-- Client:192.168.1.120:6742
Received: Hello,readers! [28bytes]
Client Connected ! Local:192.168.1.120:1621<-- Client:192.168.1.120:6754
Received: Hello,readers! [28bytes]


 

现在将第二种情况变为第三种情况,只需要将do向下挪动几行就可以了:

            //获取一个连接,中断方法
            TcpClient remoteClient = listener.AcceptTcpClient();
            //打印连接到的客户端信息
            Console.WriteLine("Client Connected ! Local:{0}<-- Client:{1}", remoteClient.Client.LocalEndPoint, remoteClient.Client.RemoteEndPoint);
            //获得流,并写入buffer中
            NetworkStream streamToClient = remoteClient.GetStream();
            do
            {                
                byte[] buffer = new byte[BufferSize];
                int bytesRead = streamToClient.Read(buffer, 0, BufferSize);
 
                //获得请求的字符串
                string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);
                Console.WriteLine("Received: {0} [{1}bytes]", msg, bytesRead);
 
            } while (true);


然后再改动一下客户端,让它可以连续发送多个字符串到到服务器.当按下回车的时候发送字符串,输入”Q”的时候,跳出循环:

            Console.WriteLine("Client is running...");
            TcpClient client;
            try
            {
                client = new TcpClient();
                //与服务器建立连接
                client.Connect(IPAddress.Parse("192.168.1.120"), 1621);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
 
            //打印连接到的服务端信息
            Console.WriteLine("Server Connected! Local:{0}-->Server:{1}", client.Client.LocalEndPoint, client.Client.RemoteEndPoint);
 
 
 
            NetworkStream streamToServer = client.GetStream();
 
            string msg;
            do
            {
                Console.Write("Sent:");
                msg = Console.ReadLine();
                if (!String.IsNullOrEmpty(msg) && msg != "Q")
                {
                    byte[] buffer = Encoding.Unicode.GetBytes(msg);
                    try
                    {
                        //发往服务器
                        streamToServer.Write(buffer, 0, buffer.Length);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        return;
                    }
                }
            } while (msg != "Q");


接下来先运行服务端,再运行客户端,输入以下字符串来进行测试,应该能够看到预期的结果.

 

注意:如果再开启一个客户端,该客户端虽然可以成功的连接服务端,也可以发送字符串,但是服务端不会做任何的处理和显示.

 

这里有一点需要注意:当客户端在TcpClient实例上调用Close()方法,或者在流上调用Didpose()方法时,服务端的streamToClient.Read()方法会持续返回0,但是不抛出异常,所以会产生一个无限循环.服务端不断的刷新显示”Received: [0 bytes]”,因此,服务端在调用streamToClient.Read()方法后,应加上一个如下判断:

                int bytesRead = streamToClient.Read(buffer, 0, BufferSize);
                if (bytesRead==0)
                {
                    Console.WriteLine("Client offline");
                    break;
                }


如果直接关闭掉客户端,或者客户端执行完毕但没有调用stream.Dispose()或者TcpClient.Close(),切服务端此时仍阻塞在Read()方法处,则会在服务端抛出异常:未经处理的异常:  System.IO.IOException: 无法从传输连接中读取数据:远程主机强迫关闭了一个现有的连接。

 

因此,服务端的streamToClient.Read()方法需要写在一个try/catch.下面是改进的服务端代码:

            do
            {
                try
                {
                    byte[] buffer = new byte[BufferSize];
                    int bytesRead = streamToClient.Read(buffer, 0, BufferSize);
                    if (bytesRead == 0)
                    {
                        Console.WriteLine("Client offline");
                        break;
                    }
                    //获得请求的字符串
                    string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);
                    Console.WriteLine("Received: {0} [{1}bytes]", msg, bytesRead);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    break;
                }
            } while (true);


同样的,如果服务端在连接到客户端之后调用remoteClient.Close(),则客户端在调用streamToServer.Write()时也会抛出异常.因此,他们的读写操作都必须放入try/catch块中.

 

 

服务端发送,客户端接受并显示

 

1.服务端程序

 

到现在为止,客户端已经能发送字符串到服务端,服务端能接受并显示.接下来,再进行进一步处理,使服务端将字符串有英文小写改为英文大写,然后发回给客户端,客户端接受并显示.此时服务端和客户端的角色和上面进行了一下对调:对于服务端来说,就好像刚才的客户端一样,将字符串写入到流中,而客户端则同服务端一样,接受并显示.

 

服务端:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
using System.Net;
using System.Net.Sockets;
using System.IO;
 
namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            const int BufferSize = 8192;//缓存大小,8192字节
 
            Console.WriteLine("Server is running...");
            IPAddress ip = new IPAddress(new byte[] { 192,168,1,120});
 
            TcpListener listener = new TcpListener(ip, 1621);
            //开始监听
            listener.Start();
            Console.WriteLine("Start Listening...");
 
            //获取一个连接,中断方法
            TcpClient remoteClient = listener.AcceptTcpClient();
 
            //打印连接到的客户端信息
            Console.WriteLine("Client Connected! Local:{0}<--Client:{1}",remoteClient.Client.LocalEndPoint,remoteClient.Client.RemoteEndPoint);
 
            //获得流
            NetworkStream streamToClient = remoteClient.GetStream();
 
            do
            {
                try
                {
                    byte[] buffer = new byte[BufferSize];
                    int bytesRead = streamToClient.Read(buffer,0,BufferSize);
                    if (bytesRead==0)
                    {
                        Console.WriteLine("Client offline");
                        break;
                    }
                    //获得请求的字符串
                    string msg = Encoding.Unicode.GetString(buffer,0,bytesRead);
                    Console.WriteLine("Received: {0} [{1} bytes]",msg,bytesRead);
 
                    //转换为大写
 
                    msg = msg.ToUpper();
                    buffer = Encoding.Unicode.GetBytes(msg);
                    streamToClient.Write(buffer,0,buffer.Length);
 
                    Console.WriteLine("Sent: {0}",msg);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    break;
                }
            } while (true);
            streamToClient.Dispose();
            remoteClient.Close();
 
            //按Q退出
            Console.WriteLine("\n\n按Q退出");
            ConsoleKey key;
            do
            {
                key = Console.ReadKey(true).Key;
            } while (key!=ConsoleKey.Q);
        }
    }
}


上面的代码大家应该很熟悉了,主要的变化是转换字母大小写,并写入到流中的操作.

 

2.客户端代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
 
namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("CLient is running...");
            TcpClient client;
            const int BufferSize = 8192;
 
            try
            {
                client = new TcpClient();
                //与服务器建立连接
                client.Connect(IPAddress.Parse("192.168.3.19"), 9322);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }           
            //打印连接到的服务端信息
            Console.WriteLine("Server Connected! Local: {0} --> Server: {1}",client.Client.LocalEndPoint,client.Client.RemoteEndPoint);
 
            NetworkStream streamToServer = client.GetStream();
 
            string msg;
 
            do
            {
                Console.Write("Sent:");
                msg = Console.ReadLine();
 
                if (!string.IsNullOrEmpty(msg)&&msg!="Q")
                {
                    byte[] buffer = Encoding.Unicode.GetBytes(msg);
                    try
                    {
                        //发往服务器
                        streamToServer.Write(buffer,0,buffer.Length);
                        int bytesRead;
                        buffer = new byte[BufferSize];
 
                        //接受并显示服务器回传的字符串
                        bytesRead = streamToServer.Read(buffer,0,BufferSize);
 
                        if (bytesRead==0)
                        {
                            Console.WriteLine("Server offline");
                            break;
                        }
                        msg = Encoding.Unicode.GetString(buffer,0,bytesRead);
                        Console.WriteLine("Received: {0} [{1}bytes]",msg,bytesRead);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        break;
                    }
                }
            } while (msg!="Q");
            streamToServer.Dispose();
            client.Close();
        }
    }
}
 


先运行一下服务端,然后运行客户端,就会看到相应的输出.

 

 

这样大家应该会对C#的网络编程有了一定得了解,当然了,这是只是网络编程中的皮毛.因为到目前为止,咱们的操作都是同步操作,上面的代码只能作为入门使用.在实际中,一个服务端只能为一个客户端提供服务的情况几乎不存在.

 

下面咱们要看异步的网络编程之前,先学习一下在不同的编码方式中英文的大小,以及TCP缓存导致的文本边界问题.


同步传输字符串


推荐阅读
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 在project.properties添加#Projecttarget.targetandroid-19android.library.reference.1..Sliding ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文介绍了一种解析GRE报文长度的方法,通过分析GRE报文头中的标志位来计算报文长度。具体实现步骤包括获取GRE报文头指针、提取标志位、计算报文长度等。该方法可以帮助用户准确地获取GRE报文的长度信息。 ... [详细]
author-avatar
epa4316380
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有