当前位置: 首页 > 编程日记 > 正文

[转]C#网络编程(异步传输字符串) - Part.3

本文转自:http://www.tracefact.net/CSharp-Programming/Network-Programming-Part3.aspx

这篇文章我们将前进一大步,使用异步的方式来对服务端编程,以使它成为一个真正意义上的服务器:可以为多个客户端的多次请求服务。但是开始之前,我们需要解决上一节中遗留的一个问题。

消息发送时的问题

这个问题就是:客户端分两次向流中写入数据(比如字符串)时,我们主观上将这两次写入视为两次请求;然而服务端有可能将这两次合起来视为一条请求,这在两个请求间隔时间比较短的情况下尤其如此。同样,也有可能客户端发出一条请求,但是服务端将其视为两条请求处理。下面列出了可能的情况,假设我们在客户端连续发送两条“Welcome to Tracefact.net!”,则数据到达服务端时可能有这样三种情况:

NOTE:在这里我们假设采用ASCII编码方式,因为此时上面的一个方框正好代表一个字节,而字符串到达末尾后为持续的0(因为byte是值类型,且最小为0)。

上面的第一种情况是最理想的情况,此时两条消息被视为两个独立请求由服务端完整地接收。第二种情况的示意图如下,此时一条消息被当作两条消息接收了:

而对于第三种情况,则是两条消息被合并成了一条接收:

如果你下载了上一篇文章所附带的源码,那么将Client2.cs进行一下修改,不通过用户输入,而是使用一个for循环连续的发送三个请求过去,这样会使请求的间隔时间更短,下面是关键代码:

string msg = "Welcome to TraceFact.Net!";for (int i = 0; i <= 2; i++) {byte[] buffer = Encoding.Unicode.GetBytes(msg);     // 获得缓存try {streamToServer.Write(buffer, 0, buffer.Length); // 发往服务器Console.WriteLine("Sent: {0}", msg);} catch (Exception ex) {Console.WriteLine(ex.Message);break;}}

运行服务端,然后再运行这个客户端,你可能会看到这样的结果:

可以看到,尽管上面将消息分成了三条单独发送,但是服务端却将后两条合并成了一条。对于这些情况,我们可以这样处理:就好像HTTP协议一样,在实际的请求和应答内容之前包含了HTTP头,其中是一些与请求相关的信息。我们也可以订立自己的协议,来解决这个问题,比如说,对于上面的情况,我们就可以定义这样一个协议:

[length=XXX]:其中xxx是实际发送的字符串长度(注意不是字节数组buffer的长度),那么对于上面的请求,则我们发送的数据为:“[length=25]Welcome to TraceFact.Net!”。而服务端接收字符串之后,首先读取这个“元数据”的内容,然后再根据“元数据”内容来读取实际的数据,它可能有下面这样两种情况:

NOTE:我觉得这里借用“元数据”这个术语还算比较恰当,因为“元数据”就是用来描述数据的数据。

  • “[“”]”中括号是完整的,可以读取到length的字节数。然后根据这个数值与后面的字符串长度相比,如果相等,则说明发来了一条完整信息;如果多了,那么说明接收的字节数多了,取出合适的长度,并将剩余的进行缓存;如果少了,说明接收的不够,那么将收到的进行一个缓存,等待下次请求,然后将两条合并。
  • “[”“]”中括号本身就不完整,此时读不到length的值,因为中括号里的内容被截断了,那么将读到的数据进行缓存,等待读取下次发送来的数据,然后将两次合并之后再按上面的方式进行处理。

接下来我们来看下如何来进行实际的操作,实际上,这个问题已经不属于C#网络编程的内容了,而完全是对字符串的处理。所以我们不再编写服务端/客户端代码,直接编写处理这几种情况的方法:

public class RequestHandler {private string temp = string.Empty;public string[] GetActualString(string input) {return GetActualString(input, null);}private string[] GetActualString(string input, List<string> outputList) {if (outputList == null)outputList = new List<string>();if (!String.IsNullOrEmpty(temp))input = temp + input;string output = "";string pattern = @"(?<=^\[length=)(\d+)(?=\])";int length;if (Regex.IsMatch(input, pattern)) {Match m = Regex.Match(input, pattern);// 获取消息字符串实际应有的长度length = Convert.ToInt32(m.Groups[0].Value);// 获取需要进行截取的位置int startIndex = input.IndexOf(']') + 1;// 获取从此位置开始后所有字符的长度output = input.Substring(startIndex);if (output.Length == length) {// 如果output的长度与消息字符串的应有长度相等// 说明刚好是完整的一条信息
                outputList.Add(output);temp = "";} else if (output.Length < length) {// 如果之后的长度小于应有的长度,// 说明没有发完整,则应将整条信息,包括元数据,全部缓存// 与下一条数据合并起来再进行处理temp = input;// 此时程序应该退出,因为需要等待下一条数据到来才能继续处理
} else if (output.Length > length) {// 如果之后的长度大于应有的长度,// 说明消息发完整了,但是有多余的数据// 多余的数据可能是截断消息,也可能是多条完整消息// 截取字符串output = output.Substring(0, length);outputList.Add(output);temp = "";// 缩短input的长度input = input.Substring(startIndex + length);// 递归调用
                GetActualString(input, outputList);}} else {    // 说明“[”,“]”就不完整temp = input;}return outputList.ToArray();}}

这个方法接收一个满足协议格式要求的输入字符串,然后返回一个数组,这是因为如果出现多次请求合并成一个发送过来的情况,那么就将它们全部返回。随后简单起见,我在这个类中添加了一个静态的Test()方法和PrintOutput()帮助方法,进行了一个简单的测试,注意我直接输入了length=13,这个是我提前计算好的。

public static void Test() {RequestHandler handler = new RequestHandler();string input;// 第一种情况测试 - 一条消息完整发送input = "[length=13]明天中秋,祝大家节日快乐!";handler.PrintOutput(input);// 第二种情况测试 - 两条完整消息一次发送input = "明天中秋,祝大家节日快乐!";input = String.Format("[length=13]{0}[length=13]{0}", input);handler.PrintOutput(input);// 第三种情况测试A - 两条消息不完整发送input = "[length=13]明天中秋,祝大家节日快乐![length=13]明天中秋";handler.PrintOutput(input);input = ",祝大家节日快乐!";handler.PrintOutput(input);// 第三种情况测试B - 两条消息不完整发送input = "[length=13]明天中秋,祝大家";handler.PrintOutput(input);input = "节日快乐![length=13]明天中秋,祝大家节日快乐!";handler.PrintOutput(input);// 第四种情况测试 - 元数据不完整input = "[leng";handler.PrintOutput(input);     // 不会有输出
input = "th=13]明天中秋,祝大家节日快乐!";handler.PrintOutput(input);}// 用于测试输出
private void PrintOutput(string input) {Console.WriteLine(input);string[] outputArray = GetActualString(input);foreach (string output in outputArray) {Console.WriteLine(output);}Console.WriteLine();}

运行上面的程序,可以得到如下的输出:

OK,从上面的输出可以看到,这个方法能够满足我们的要求。对于这篇文章最开始提出的问题,可以很轻松地通过加入这个方法来解决,这里就不再演示了,但在本文所附带的源代码含有修改过的程序。在这里花费了很长的时间,接下来让我们回到正题,看下如何使用异步方式完成上一篇中的程序吧。

异步传输字符串

在上一篇中,我们由简到繁,提到了服务端的四种方式:服务一个客户端的一个请求、服务一个客户端的多个请求、服务多个客户端的一个请求、服务多个客户端的多个请求。我们说到可以将里层的while循环交给一个新建的线程去让它来完成。除了这种方式以外,我们还可以使用一种更好的方式――使用线程池中的线程来完成。我们可以使用BeginRead()、BeginWrite()等异步方法,同时让这BeginRead()方法和它的回调方法形成一个类似于while的无限循环:首先在第一层循环中,接收到一个客户端后,调用BeginRead(),然后为该方法提供一个读取完成后的回调方法,然后在回调方法中对收到的字符进行处理,随后在回调方法中接着调用BeginRead()方法,并传入回调方法本身。

由于程序实现功能和上一篇完全相同,我就不再细述了。而关于异步调用方法更多详细内容,可以参见 C#中的委托和事件(续)。

1.服务端的实现

public class RemoteClient {private TcpClient client;private NetworkStream streamToClient;private const int BufferSize = 8192;private byte[] buffer;private RequestHandler handler;public RemoteClient(TcpClient client) {this.client = client;// 打印连接到的客户端信息Console.WriteLine("\nClient Connected!{0} <-- {1}",client.Client.LocalEndPoint, client.Client.RemoteEndPoint);// 获得流streamToClient = client.GetStream();buffer = new byte[BufferSize];// 设置RequestHandlerhandler = new RequestHandler();// 在构造函数中就开始准备读取AsyncCallback callBack = new AsyncCallback(ReadComplete);streamToClient.BeginRead(buffer, 0, BufferSize, callBack, null);}// 再读取完成时进行回调private void ReadComplete(IAsyncResult ar) {int bytesRead = 0;try {lock (streamToClient) {bytesRead = streamToClient.EndRead(ar);Console.WriteLine("Reading data, {0} bytes ...", bytesRead);}if (bytesRead == 0) throw new Exception("读取到0字节");string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);Array.Clear(buffer,0,buffer.Length);        // 清空缓存,避免脏读string[] msgArray = handler.GetActualString(msg);   // 获取实际的字符串// 遍历获得到的字符串foreach (string m in msgArray) {Console.WriteLine("Received: {0}", m);string back = m.ToUpper();// 将得到的字符串改为大写并重新发送byte[] temp = Encoding.Unicode.GetBytes(back);streamToClient.Write(temp, 0, temp.Length);streamToClient.Flush();Console.WriteLine("Sent: {0}", back);}               // 再次调用BeginRead(),完成时调用自身,形成无限循环lock (streamToClient) {AsyncCallback callBack = new AsyncCallback(ReadComplete);streamToClient.BeginRead(buffer, 0, BufferSize, callBack, null);}} catch(Exception ex) {if(streamToClient!=null)streamToClient.Dispose();client.Close();Console.WriteLine(ex.Message);      // 捕获异常时退出程序              
        }}} 随后,我们在主程序中仅仅创建TcpListener类型实例,由于RemoteClient类在构造函数中已经完成了初始化的工作,所以我们在下面的while循环中我们甚至不需要调用任何方法:class Server {static void Main(string[] args) {Console.WriteLine("Server is running ... ");IPAddress ip = new IPAddress(new byte[] { 127, 0, 0, 1 });TcpListener listener = new TcpListener(ip, 8500);listener.Start();           // 开始侦听Console.WriteLine("Start Listening ...");while (true) {// 获取一个连接,同步方法,在此处中断TcpClient client = listener.AcceptTcpClient();              RemoteClient wapper = new RemoteClient(client);}}}好了,服务端的实现现在就完成了,接下来我们再看一下客户端的实现:

2.客户端的实现

与服务端类似,我们首先对TcpClient进行一个简单的包装,使它的使用更加方便一些,因为它是服务端的客户,所以我们将类的名称命名为ServerClient:public class ServerClient {private const int BufferSize = 8192;private byte[] buffer;private TcpClient client;private NetworkStream streamToServer;private string msg = "Welcome to TraceFact.Net!";public ServerClient() {try {client = new TcpClient();client.Connect("localhost", 8500);      // 与服务器连接} catch (Exception ex) {Console.WriteLine(ex.Message);return;}buffer = new byte[BufferSize];// 打印连接到的服务端信息Console.WriteLine("Server Connected!{0} --> {1}",client.Client.LocalEndPoint, client.Client.RemoteEndPoint);streamToServer = client.GetStream();}// 连续发送三条消息到服务端public void SendMessage(string msg) {msg = String.Format("[length={0}]{1}", msg.Length, msg);for (int i = 0; i <= 2; i++) {byte[] temp = Encoding.Unicode.GetBytes(msg);   // 获得缓存try {streamToServer.Write(temp, 0, temp.Length); // 发往服务器Console.WriteLine("Sent: {0}", msg);} catch (Exception ex) {Console.WriteLine(ex.Message);break;}}lock (streamToServer) {AsyncCallback callBack = new AsyncCallback(ReadComplete);streamToServer.BeginRead(buffer, 0, BufferSize, callBack, null);}}public void SendMessage() {SendMessage(this.msg);}// 读取完成时的回调方法private void ReadComplete(IAsyncResult ar) {int bytesRead;try {lock (streamToServer) {bytesRead = streamToServer.EndRead(ar);}if (bytesRead == 0) throw new Exception("读取到0字节");string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);Console.WriteLine("Received: {0}", msg);Array.Clear(buffer, 0, buffer.Length);      // 清空缓存,避免脏读lock (streamToServer) {AsyncCallback callBack = new AsyncCallback(ReadComplete);streamToServer.BeginRead(buffer, 0, BufferSize, callBack, null);}} catch (Exception ex) {if(streamToServer!=null)streamToServer.Dispose();client.Close();Console.WriteLine(ex.Message);}}}在上面的SendMessage()方法中,我们让它连续发送了三条同样的消息,这么仅仅是为了测试,因为异步操作同样会出现上面说过的:服务器将客户端的请求拆开了的情况。最后我们在Main()方法中创建这个类型的实例,然后调用SendMessage()方法进行测试:class Client {static void Main(string[] args) {ConsoleKey key;ServerClient client = new ServerClient();client.SendMessage();Console.WriteLine("\n\n输入\"Q\"键退出。");do {key = Console.ReadKey(true).Key;} while (key != ConsoleKey.Q);}}是不是感觉很清爽?因为良好的代码重构,使得程序在复杂程度提高的情况下依然可以在一定程度上保持良好的阅读性。

3.程序测试

最后一步,我们先运行服务端,接着连续运行两个客户端,看看它们的输出分别是什么:

大家可以看到,在服务端,我们可以连接多个客户端,同时为它们服务;除此以外,由接收的字节数发现,两个客户端均有两个请求被服务端合并成了一条请求,因为我们在其中加入了特殊的协议,所以在服务端可以对这种情况进行良好的处理。

在客户端,我们没有采取类似的处理,所以当客户端收到应答时,仍然会发生请求合并的情况。对于这种情况,我想大家已经知道该如何处理了,就不再多费口舌了。

使用这种定义协议的方式有它的优点,但缺点也很明显,如果客户知道了这个协议,有意地输入[length=xxx],但是后面的长度却不匹配,此时程序就会出错。可选的解决办法是对“[”和“]”进行编码,当客户端有意输入这两个字符时,我们将它替换成“\[”和“\]”或者别的字符,在读取后再将它还原。

关于这个范例就到此结束了,剩下的两个范例都将采用异步传输的方式,并且会加入更多的协议内容。下一篇我们将介绍如何向服务端发送或接收文件。

相关文章:

【百度地图API】——如何用label制作简易的房产标签

摘要&#xff1a; 最近&#xff0c;API爱好者们纷纷说&#xff0c;自定义marker太复杂了&#xff01;不仅定义复杂&#xff0c;连所有的dom事件都要自己重新定义。有没有快速简易创建房产标签的方法呢&#xff1f; 答案当然是有的啦~ 我们可以利用label嘛&#xff01; --------…

李洪根关于[SQL]对于分页存储过程

作者&#xff1a;李洪根 微软MVP1。我个人认为最好的分页方法是: Selecttop10*fromtablewhereid>200写成存储过程,上面的语句要拼一下sql语句,要获得最后大于的哪一个ID号2。那个用游标的方式,只适合于小数据量的表,如果表在一万行以上,就差劲了你的存储过程还比不上NOT IN…

避不开的算法,如何吃透?

作者 | Alekya Ragipally译者 | 弯月&#xff0c;编辑 | 屠敏题图 | 自东方 IC出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;当你使用搜索引擎&#xff08;例如Google Chrome、Mozilla Firefox等&#xff09;的时候&#xff0c;后台发生了什么&#xff…

CentOS 6.4下PXE+Kickstart无人值守安装操作系统

一、简介1.1 什么是PXEPXE(Pre-boot Execution Environment&#xff0c;预启动执行环境)是由Intel公司开发的最新技术&#xff0c;工作于Client/Server的网络模式&#xff0c;支持工作站通过网络从远端服务器下载映像&#xff0c;并由此支持通过网络启动操作系统&#xff0c;在…

Asp.NET中常用的一些优化性能的方法

ASP.NET 的缓存机制相比ASP有很大的改进&#xff0c;本文档除对常用优化方法进行总结介绍外&#xff0c;强调了如何使用ASP.NET的缓存来获得最佳性能。1&#xff1a;不要使用不必要的session 和ASP中一样&#xff0c;在不必要的时候不要使用Session。可以针对整个应用程序或者页…

不信你看!这次Python和AI真的玩儿大了!!

这是一个很难让人心平气和的年代。不少人都想学 AI&#xff0c;总担心自己学不会&#xff0c;学不懂&#xff0c;或者学的课不是只教Python&#xff0c;就是缺少项目实战。最终都是浅尝辄止&#xff0c;不了了之&#xff01;我每天在公众号后台收到上千条类似的留言&#xff1a…

【引用】在Eclipse中将java Project转换成Dynamic Web Project

编辑工程的.project文件&#xff1a; 添加 <nature>org.eclipse.wst.common.project.facet.core.nature</nature> <nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature> <nature>org.eclipse.jem.workbench.JavaEMFNature<…

mysql之字符编码问题

mysql编码分为服务端编码和客户端编码两大类字段编码, 表编码, 数据库编码这些编码都属于服务端编码,服务端编码决定你可以存哪些字符以及这些字符要哪种规则排序.字段编码优先级最高. 你插入用什么码属于客户端编码, 你用什么客户端编码都无所谓,只要插入前加个命令set names …

关于GCN,我有三种写法

作者 | 阿泽来源 | 阿泽的学习笔记&#xff08;ID: aze_learning&#xff09;本篇文章主要基于 DGL 框架用三种不同的方式来实现图卷积神经网络。DGL简介DGL&#xff08;Deep Graph Library&#xff09;框架是由纽约大学和 AWS 工程师共同开发的开源框架&#xff0c;旨在为大家…

CentOS5快速搭建vsftp服务

既然强调快速, 我们就马上开始&#xff0c;环境是centos5安装vsftpd&#xff0c;用了半天做了测试与修改&#xff0c;终于完成。 第一步&#xff1a;安装vsftpd&#xff0c;在终端允许 # yum -y install vsftpd 没什么问题就直接安装好啦 第二步&#xff1a;编辑vsftpd的配置…

我和freelancer不得不说的故事5 --- 心理落差

我和freelancer不得不说的故事5 --- 心理落差 我下海之前所在的外企&#xff0c;是一家顶级知名IT企业&#xff0c;其SAP咨询服务业务规模和影响都很大&#xff0c;是SAP咨询界五大咨询公司之一。我从07年加入这家公司&#xff0c;到辞职下海&#xff0c;在这家公司工作8年半。…

一起谈.NET技术,asp.net控件开发基础(18)

本篇继续上篇的讨论&#xff0c;可能大家已经在使用asp.net2.0了,DataSource属性不再使用,而是跟数据源控件搭配使用.现在讨论的绑定技术都是基于1.1版本,先熟悉一下,本质上是一样的,这样一步步的学习.对以后绝对有帮助.因为当你使用数据源控件,只需要设置一个DataSourceID,方便…

使用sqlserver来存放和取得session

asp.net 提供了三种存放 session的方式。 1 InProc 2 State Server 3 SQL Server 第一种是我们经常用的&#xff0c;第2中就是使用一个名为 state server 的机器用它的内存来存放其他机器的session 状态&#xff0c;其实&#xff0c;我们还可以在 sql server 里面来存放和取…

五项挑战获四项第一,地平线霸榜Waymo自动驾驶算法挑战赛

美国当地时间6月15日&#xff0c;Alphabet&#xff08;Google母公司&#xff09;旗下的自动驾驶公司Waymo在CVPR 2020自动驾驶Workshop上揭晓Waymo开放数据集挑战赛的结果&#xff0c;边缘AI芯片企业地平线斩获5项挑战中的4项全球第一。 本次挑战赛&#xff0c;Waymo开放了其自…

SSO单点登录基于CAS架构封装 Memcached 实例

2019独角兽企业重金招聘Python工程师标准>>> SSO认证中心是CAS整个应用架构的一个极其重要的关键点&#xff0c;必须满足如下两点要求&#xff1a; 1.高可用&#xff0c;不允许程序发生故障。如果认证中心发生故障&#xff0c;整个应用群将无法登录&#xff0c;导致…

HTMLButton控件下的Confirm()

作者&#xff1a;未知 请作者速与本人联系一、前言在ASP.NET中大部分如删除等一些动作为了友好都为添加confirm()来弹出消息框进行提示&#xff0c;但是HTML控件和WEB控件是否使用的方法是一样的呢?二、方法A. System.Web.UI.WebControls.Button控件现在一般都是这样在Page_…

Python 还能实现哪些 AI 游戏?附上代码一起来一把!

作者 | 李秋键责编 | Carol头图 | CSDN 付费下载自视觉中国人工智能作为当前热门在我们生活中得到了广泛应用&#xff0c;尤其是在智能游戏方面&#xff0c;有的已经达到了可以和职业选手匹敌的效果。而DQN算法作为智能游戏的经典选择算法&#xff0c;其主要是通过奖励惩罚机制…

一起谈.NET技术,专访微软MVP衣明志:走进ASP.NET MVC 2框架开发

日前微软已经发布ASP.NET MVC 2框架RC版&#xff0c;究竟这次RC版本的发布对于WEB开发者带来怎样的改变&#xff1f;以及未来ASP.NET MVC 2正式版还会有哪些改进&#xff1f;带着这样的问题&#xff0c;我们51CTO记者彭凡专门采访了微软MVP衣明志老师。ASP.NET MVC是微软官方提…

Entity Framework:Code-First Tutorial开篇

这个系列文章是关于Entity Framework Code-First的英文系列文章&#xff0c;内容不错&#xff0c;每篇一个主题知识点介绍&#xff0c;特转载过来 原文地址&#xff1a;http://www.entityframeworktutorial.net/code-first/entity-framework-code-first.aspx转载于:https://www…

Android开发者指南(22) —— Accessing Resources

前言   本章内容为Android开发者指南的Framework Topics/Application Resources/Accessing Resources章节&#xff0c;译为"资源调用"&#xff0c;版本为Android 3.2 r1&#xff0c;翻译来自&#xff1a;"CodeGuy"&#xff0c;欢迎访问他的博客&#xff…

如何快速实现HTML编辑器.NET组件

作者&#xff1a;未知 请作者速与本人联系得到“素材”首先我们需要得到一个HTML编辑器的原始代码&#xff0c;网上有不少这类的编辑器&#xff0c;如大名鼎鼎的RichTextBox&#xff0c;为了避免版权纠纷&#xff0c;以我所做得为例&#xff08;暂名&#xff1a;UltraTextBox…

罗永浩力荐,丁磊豪送的学习神器:手机查词真不如这支AI词典笔?

销量确实称得上火爆。尽管999元的直播优惠价价格并不低&#xff0c;但这支有道词典笔专业版在快手直播间还是经历了返场&#xff0c;最终20000多台一抢而空。 为这款产品站台的正是网易CEO丁磊&#xff0c;6月11日是他网上卖货的首秀&#xff0c;不过更重要的是&#xff0c;那天…

Thinking in java中关于Exception的一道面试题.

今天看到Thinking in Java中一个关于Exception的例子:最后看到有一篇总结的比较好的文章, 这里拿来记录下, 文章地址是:http://blog.csdn.net/salerzhang/article/details/46581457 感谢原作者. 1 class Annoyance extends Exception {}2 class Sneeze extends Annoyance {}3 …

使用 .NET 框架轻松开发完美的 Web 窗体控件

作者&#xff1a;David S. Platt 出自&#xff1a;微软 本文假定您熟悉 Visual Basic .NET、C# 和 HTML 下载本文的代码&#xff1a; WebC.exe (274KB) 摘要 预建的自定义控件可以简化和加快应用程序的设计&#xff0c;并使您能够维护 UI 的一致性。但是&#xff0c;预先打…

史上最强女游戏程序员

也许你听说过John Carmack 和Tim Sweeney等大牛的名字&#xff0c;而向来游戏工业都是阳盛阴衰&#xff0c;适逢国际妇女节&#xff0c;今天我为大家介绍游戏业界一位史上最强女游戏程序员&#xff1a;Corrinne Yu。 简历 以下是她在游戏业界内的简历 微软Halo团队首席引擎架构…

重磅日程公布!与百名大咖在线交流技术,2天20个AI论坛不可错过

当全球都在面向 AI 变革时&#xff0c;AI 不再是触不可及&#xff0c;它需要产业化落地&#xff0c;为社会创造价值。在这一轮技术革命、技术浪潮中&#xff0c;开发者们成为构建任何一家AI企业的核心竞争力。不过&#xff0c;不同于此前只懂开发语言、数据结构便可轻松躲过新技…

Python取出列表相应值的位置(表处理)

#需求在一个列表中&#xff0c;取出相应值的位置方法1&#xff1a;#脚本示例[rootlocalhost opt]# cat list.py #!/usr/bin/env python #_*_ coding:utf-8 _*_ name[!,#,*,Eric,wsyht,jack,jack,a,b,c,d,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,2332,4,2,6,2] first_pos 0 for …

rhel5.5安装xwindow

rhel5.5安装xwindow 1安装xwindow yum groupinstall "X Window System" 2、安装GNOME桌面环境 yum groupinstall "GNOME Desktop Environment" 3、卸载GNOME桌面环境 yum groupremove "GNOME Desktop Environment"转载于:https://blog.51cto…

使用 ASP.NET 加密口令

作者&#xff1a;未知 请作者速与本人联系当我们在网站上建立数据库时&#xff0c;保护用户的信息安全是非常必要的。多数用户不愿意让别人知道自己的信息&#xff0c;同时网管也不想因为安全问题而丢失网站的信誉。无论对于谁&#xff0c;安全问题都是非常重要的。为了解决这…

算法鼻祖高德纳,82 岁仍在写《计算机程序设计的艺术》

作者 | 年素清编辑 | 伍杏玲出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;高德纳&#xff08;Donald Ervin Knuth&#xff09;被誉为现代计算机科学的鼻祖&#xff0c;毕生致力于编译程序、属性文法和运算法则等领域的前沿研究&#xff0c;共出版专著17部&#x…