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

[转载]分享WCF聊天程序--WCFChat

http://www.cnblogs.com/gaoweipeng/archive/2009/09/04/1560260.html

无意中在一个国外的站点下到了一个利用WCF实现聊天的程序,作者是:Nikola Paljetak。研究了一下,自己做了测试和部分修改,感觉还不错,分享给大家。
先来看下运行效果:
开启服务:

客户端程序:


程序分为客户端和服务器端:
------------服务器端:

IChatService.cs:

ContractedBlock.gifExpandedBlockStart.gifCode
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Collections;

namespace WCFChatService
{
    
// SessionMode.Required  允许Session会话。双工协定时的回调协定类型为IChatCallback接口
    [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IChatCallback))]
    
public interface IChatService
    {
        [OperationContract(IsOneWay 
= false, IsInitiating = true, IsTerminating = false)]//----->IsOneWay = false等待服务器完成对方法处理;IsInitiating = true启动Session会话,IsTerminating = false 设置服务器发送回复后不关闭会话
        string[] Join(string name);//用户加入

        [OperationContract(IsOneWay 
= true, IsInitiating = false, IsTerminating = false)]
        
void Say(string msg);//群聊信息

        [OperationContract(IsOneWay 
= true, IsInitiating = false, IsTerminating = false)]
        
void Whisper(string to, string msg);//私聊信息

        [OperationContract(IsOneWay 
= true, IsInitiating = false, IsTerminating = true)]
        
void Leave();//用户加入
    }
    
/// <summary>
    
/// 双向通信的回调接口
    
/// </summary>
    interface IChatCallback
    {
        [OperationContract(IsOneWay 
= true)]
        
void Receive(string senderName, string message);

        [OperationContract(IsOneWay 
= true)]
        
void ReceiveWhisper(string senderName, string message);

        [OperationContract(IsOneWay 
= true)]
        
void UserEnter(string name);

        [OperationContract(IsOneWay 
= true)]
        
void UserLeave(string name);
    }

    
/// <summary>
    
/// 设定消息的类型
    
/// </summary>
    public enum MessageType { Receive, UserEnter, UserLeave, ReceiveWhisper };
    
/// <summary>
    
/// 定义一个本例的事件消息类. 创建包含有关事件的其他有用的信息的变量,只要派生自EventArgs即可。
    
/// </summary>
    public class ChatEventArgs : EventArgs
    {
        
public MessageType msgType;
        
public string name;
        
public string message;
    }
}

ChatService.cs

ContractedBlock.gifExpandedBlockStart.gifCode
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WCFChatService
{
    
// InstanceContextMode.PerSession 服务器为每个客户会话创建一个新的上下文对象。ConcurrencyMode.Multiple 异步的多线程实例
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
    
public class ChatService : IChatService
    {
        
private static Object syncObj = new Object();////定义一个静态对象用于线程部份代码块的锁定,用于lock操作
        IChatCallback callback = null;

        
public delegate void ChatEventHandler(object sender, ChatEventArgs e);//定义用于把处理程序赋予给事件的委托。
        public static event ChatEventHandler ChatEvent;//定义事件
        static Dictionary<string, ChatEventHandler> chatters = new Dictionary<string, ChatEventHandler>();//创建一个静态Dictionary(表示键和值)集合(字典),用于记录在线成员,Dictionary<(Of <(TKey, TValue>)>) 泛型类

        
private string name;
        
private ChatEventHandler myEventHandler = null;


        
public string[] Join(string name)
        {
            
bool userAdded = false;
            myEventHandler 
= new ChatEventHandler(MyEventHandler);//将MyEventHandler方法作为参数传递给委托

            
lock (syncObj)//线程的同步性,同步访问多个线程的任何变量,利用lock(独占锁),确保数据访问的唯一性。
            {
                
if (!chatters.ContainsKey(name) && name != "" && name != null)
                {
                    
this.name = name;
                    chatters.Add(name, MyEventHandler);
                    userAdded 
= true;
                }
            }

            
if (userAdded)
            {
                callback 
= OperationContext.Current.GetCallbackChannel<IChatCallback>();//获取当前操作客户端实例的通道给IChatCallback接口的实例callback,此通道是一个定义为IChatCallback类型的泛类型,通道的类型是事先服务契约协定好的双工机制。
                ChatEventArgs e = new ChatEventArgs();//实例化事件消息类ChatEventArgs
                e.msgType = MessageType.UserEnter;
                e.name 
= name;
                BroadcastMessage(e);
                ChatEvent 
+= myEventHandler;
                
string[] list = new string[chatters.Count]; //以下代码返回当前进入聊天室成员的称列表
                lock (syncObj)
                {
                    chatters.Keys.CopyTo(list, 
0);//将字典中记录的用户信息复制到数组中返回。
                }
                
return list;
            }
            
else
            {
                
return null;
            }
        }

        
public void Say(string msg)
        {
            ChatEventArgs e 
= new ChatEventArgs();
            e.msgType 
= MessageType.Receive;
            e.name 
= this.name;
            e.message 
= msg;
            BroadcastMessage(e);
        }

        
public void Whisper(string to, string msg)
        {
            ChatEventArgs e 
= new ChatEventArgs();
            e.msgType 
= MessageType.ReceiveWhisper;
            e.name 
= this.name;
            e.message 
= msg;
            
try
            {
                ChatEventHandler chatterTo;
//创建一个临时委托实例
                lock (syncObj)
                {
                    chatterTo 
= chatters[to]; //查找成员字典中,找到要接收者的委托调用
                }
                chatterTo.BeginInvoke(
this, e, new AsyncCallback(EndAsync), null);//异步方式调用接收者的委托调用
            }
            
catch (KeyNotFoundException)
            {
            }
        }

        
public void Leave()
        {
            
if (this.name == null)
                
return;

            
lock (syncObj)
            {
                chatters.Remove(
this.name);
            }
            ChatEvent 
-= myEventHandler;
            ChatEventArgs e 
= new ChatEventArgs();
            e.msgType 
= MessageType.UserLeave;
            e.name 
= this.name;
            
this.name = null;
            BroadcastMessage(e);
        }

        
//回调,根据客户端动作通知对应客户端执行对应的操作
        private void MyEventHandler(object sender, ChatEventArgs e)
        {
            
try
            {
                
switch (e.msgType)
                {
                    
case MessageType.Receive:
                        callback.Receive(e.name, e.message);
                        
break;
                    
case MessageType.ReceiveWhisper:
                        callback.ReceiveWhisper(e.name, e.message);
                        
break;
                    
case MessageType.UserEnter:
                        callback.UserEnter(e.name);
                        
break;
                    
case MessageType.UserLeave:
                        callback.UserLeave(e.name);
                        
break;
                }
            }
            
catch
            {
                Leave();
            }
        }

        
private void BroadcastMessage(ChatEventArgs e)
        {

            ChatEventHandler temp 
= ChatEvent;

            
if (temp != null)
            {
                
//循环将在线的用户广播信息
                foreach (ChatEventHandler handler in temp.GetInvocationList())
                {
                    
//异步方式调用多路广播委托的调用列表中的ChatEventHandler 
                    handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
                }
            }
        }
        
//广播中线程调用完成的回调方法功能:清除异常多路广播委托的调用列表中异常对象(空对象)
        private void EndAsync(IAsyncResult ar)
        {
            ChatEventHandler d 
= null;

            
try
            {
                
//封装异步委托上的异步操作结果
                System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
                d 
= ((ChatEventHandler)asres.AsyncDelegate);
                d.EndInvoke(ar);
            }
            
catch
            {
                ChatEvent 
-= d;
            }
        }
    }
}

------------客户端:

ContractedBlock.gifExpandedBlockStart.gifCode
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.ServiceModel;

namespace WCFChatClient
{
    
public partial class ChatForm : Form, IChatServiceCallback
    {
        
/// <summary>
        
/// 该函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。 
        
/// </summary>
        
/// <param name="hWnd">其窗口程序将接收消息的窗口的句柄</param>
        
/// <param name="msg">指定被发送的消息</param>
        
/// <param name="wParam">指定附加的消息指定信息</param>
        
/// <param name="lParam">指定附加的消息指定信息</param>
        [DllImport("user32.dll")]
        
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
        
//当一个窗口标准垂直滚动条产生一个滚动事件时发送此消息给那个窗口,也发送给拥有它的控件
        private const int WM_VSCROLL = 0x115;
        
private const int SB_BOTTOM = 7;
        
private int lastSelectedIndex = -1;

        
private ChatServiceClient proxy;
        
private string userName;

        
private WaitForm wfDlg = new WaitForm();
        
private delegate void HandleDelegate(string[] list);
        
private delegate void HandleErrorDelegate();

        
public ChatForm()
        {
            InitializeComponent();
            ShowInterChatMenuItem(
true);
        }

        
/// <summary>
        
/// 连接服务器
        
/// </summary>
        private void InterChatMenuItem_Click(object sender, EventArgs e)
        {
            lbOnlineUsers.Items.Clear();
            LoginForm loginDlg 
= new LoginForm();
            
if (loginDlg.ShowDialog() == DialogResult.OK)
            {
                userName 
= loginDlg.txtUserName.Text;
                loginDlg.Close();
            }

            txtChatContent.Focus();
            Application.DoEvents();
            InstanceContext site 
= new InstanceContext(this);//为实现服务实例的对象进行初始化
            proxy = new ChatServiceClient(site);
            IAsyncResult iar 
= proxy.BeginJoin(userName, new AsyncCallback(OnEndJoin), null);
            wfDlg.ShowDialog();
        }

        
private void OnEndJoin(IAsyncResult iar)
        {
            
try
            {
                
string[] list = proxy.EndJoin(iar);
                HandleEndJoin(list);

            }
            
catch (Exception e)
            {
                HandleEndJoinError();
            }

        }
        
/// <summary>
        
/// 错误提示
        
/// </summary>
        private void HandleEndJoinError()
        {
            
if (wfDlg.InvokeRequired)
                wfDlg.Invoke(
new HandleErrorDelegate(HandleEndJoinError));
            
else
            {
                wfDlg.ShowError(
"无法连接聊天室!");
                ExitChatSession();
            }
        }
        
/// <summary>
        
/// 登录结束后的处理
        
/// </summary>
        
/// <param name="list"></param>
        private void HandleEndJoin(string[] list)
        {
            
if (wfDlg.InvokeRequired)
                wfDlg.Invoke(
new HandleDelegate(HandleEndJoin), new object[] { list });
            
else
            {
                wfDlg.Visible 
= false;
                ShowInterChatMenuItem(
false);
                
foreach (string name in list)
                {
                    lbOnlineUsers.Items.Add(name);
                }
                AppendText(
" 用户: " + userName + "--------登录---------" + DateTime.Now.ToString()+ Environment.NewLine);
            }
        }
        
/// <summary>
        
/// 退出聊天室
        
/// </summary>
        private void OutInterChatMenuItem_Click(object sender, EventArgs e)
        {
            ExitChatSession();
            Application.Exit();
        }
        
/// <summary>
        
/// 群聊
        
/// </summary>
        private void btnChat_Click(object sender, EventArgs e)
        {
            SayAndClear(
"", txtChatContent.Text, false);
            txtChatContent.Focus();
        }
        
/// <summary>
        
/// 发送消息
        
/// </summary>
        private void SayAndClear(string to, string msg, bool pvt)
        {
            
if (msg != "")
            {
                
try
                {
                    CommunicationState cs 
= proxy.State;
                    
//pvt 公聊还是私聊
                    if (!pvt)
                    {
                        proxy.Say(msg);
                    }
                    
else
                    {
                        proxy.Whisper(to, msg);
                    }

                    txtChatContent.Text 
= "";
                }
                
catch
                {
                    AbortProxyAndUpdateUI();
                    AppendText(
"失去连接: " + DateTime.Now.ToString() + Environment.NewLine);
                    ExitChatSession();
                }
            }
        }
        
private void txtChatContent_KeyPress(object sender, KeyPressEventArgs e)
        {
            
if (e.KeyChar == 13)
            {
                e.Handled 
= true;
                btnChat.PerformClick();
            }
        }
        
/// <summary>
        
/// 只有选择一个用户时,私聊按钮才可用
        
/// </summary>
        private void lbOnlineUsers_SelectedIndexChanged(object sender, EventArgs e)
        {
            AdjustWhisperButton();
        }
        
/// <summary>
        
/// 私聊
        
/// </summary>       
        private void btnWhisper_Click(object sender, EventArgs e)
        {
            
if (txtChatDetails.Text == "")
            {
                
return;
            }
            
object to = lbOnlineUsers.SelectedItem;
            
if (to != null)
            {
                
string receiverName = (string)to;
                AppendText(
"私下对" + receiverName + "说: " + txtChatContent.Text);//+ Environment.NewLine
                SayAndClear(receiverName, txtChatContent.Text, true);
                txtChatContent.Focus();
            }
        }
        
/// <summary>
        
/// 连接聊天室
        
/// </summary>
        private void ShowInterChatMenuItem(bool show)
        {
            InterChatMenuItem.Enabled 
= show;
            OutInterChatMenuItem.Enabled 
= this.btnChat.Enabled = !show;
        }
        
private void AppendText(string text)
        {
            txtChatDetails.Text 
+= text;
            SendMessage(txtChatDetails.Handle, WM_VSCROLL, SB_BOTTOM, 
new IntPtr(0));
        }
        
/// <summary>
        
/// 退出应用程序时,释放使用资源
        
/// </summary>
        private void ExitChatSession()
        {
            
try
            {
                proxy.Leave();
            }
            
catch { }
            
finally
            {
                AbortProxyAndUpdateUI();
            }
        }
        
/// <summary>
        
/// 释放使用资源
        
/// </summary>
        private void AbortProxyAndUpdateUI()
        {
            
if (proxy != null)
            {
                proxy.Abort();
                proxy.Close();
                proxy 
= null;
            }
            ShowInterChatMenuItem(
true);
        }
        
/// <summary>
        
/// 接收消息
        
/// </summary>
        public void Receive(string senderName, string message)
        {
            AppendText(senderName 
+ "说: " + message + Environment.NewLine);
        }
        
/// <summary>
        
/// 接收私聊消息
        
/// </summary>
        public void ReceiveWhisper(string senderName, string message)
        {
            AppendText(senderName 
+ " 私下说: " + message + Environment.NewLine);
        }
        
/// <summary>
        
/// 新用户登录
        
/// </summary>
        public void UserEnter(string name)
        {
            AppendText(
"用户 " + name + " --------登录---------" + DateTime.Now.ToString() + Environment.NewLine);
            lbOnlineUsers.Items.Add(name);
        }
        
/// <summary>
        
/// 用户离开
        
/// </summary>
        public void UserLeave(string name)
        {
            AppendText(
"用户 " + name + " --------离开---------" + DateTime.Now.ToString() + Environment.NewLine);
            lbOnlineUsers.Items.Remove(name);
            AdjustWhisperButton();
        }
        
/// <summary>
        
/// 控制私聊按钮的可用性,只有选择了用户时按钮才可用
        
/// </summary>
        private void AdjustWhisperButton()
        {
            
if (lbOnlineUsers.SelectedIndex == lastSelectedIndex)
            {
                lbOnlineUsers.SelectedIndex 
= -1;
                lastSelectedIndex 
= -1;
                btnWhisper.Enabled 
= false;
            }
            
else
            {
                btnWhisper.Enabled 
= true;
                lastSelectedIndex 
= lbOnlineUsers.SelectedIndex;
            }

            txtChatContent.Focus();
        }
        
/// <summary>
        
/// 窗体关闭时,释放使用资源
        
/// </summary>
        private void ChatForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            AbortProxyAndUpdateUI();
            Application.Exit();
        }
    }
}

代码中我做了详细的讲解,相信园友们完全可以看懂。代码中的一些使用的方法还是值得大家参考学习的。这里涉及到了WCF的使用方法,需要注意的是:如果想利用工具生成代理类,需要加上下面的代码:

ContractedBlock.gifExpandedBlockStart.gifCode
if (host.Description.Behaviors.Find<System.ServiceModel.Description.ServiceMetadataBehavior>() == null)
            {
                BindingElement metaElement 
= new TcpTransportBindingElement();
                CustomBinding metaBind 
= new CustomBinding(metaElement);
                host.Description.Behaviors.Add(
new System.ServiceModel.Description.ServiceMetadataBehavior());
                host.AddServiceEndpoint(
typeof(System.ServiceModel.Description.IMetadataExchange), metaBind, "MEX");
            }

否则在生成代理类的时候会报错如下的错误:

源码下载:
/Files/gaoweipeng/WCFChat.rar


转载于:https://www.cnblogs.com/fx2008/archive/2011/11/08/2241317.html

相关文章:

【C++】C++11 STL算法(五):设置操作(Set operations)、堆操作(Heap operations)

目录设置操作(Set operations)一、includes1、原型&#xff1a;2、说明&#xff1a;3、官方demo二、set_difference1、原型&#xff1a;2、说明&#xff1a;3、官方demo三、set_intersection1、原型&#xff1a;2、说明&#xff1a;3、官方demo四、set_symmetric_difference1、…

63万张!旷视发布最大物体检测数据集Objects365 | 技术头条

编辑 | 琥珀来源 | AI科技大本营&#xff08;id&#xff1a;rgznai100&#xff09;昨日&#xff0c;在旷视科技联合北京智源人工智能研究院举办的发布会上&#xff0c;旷视研究院发布了物体检测数据集 Objects365&#xff0c;包含 63 万张图像数量&#xff0c;365 个类别数量&a…

(一)Android Studio 安装部署 华丽躲坑

叨叨两句先 小宇之前一直做前后端开发&#xff0c;只是略懂JS&#xff0c;未接触过Java和Android 近期工作任务也是兴趣使然&#xff0c;开始琢磨DJI二次开发 DJI是我最服气的无人机厂商&#xff0c;无人机稳定性极强&#xff0c;性价比狂高&#xff0c;还给了极度丰富的二次开…

linux 环境配置 安装jdk

一. 下载jdk5.0 for linux 到sun的主页 http://java.sun.com/j2se/1.5.0/download.jsp 下载jdk安装文件jdk-1_5_0_05-linux-i586.bin 二. 解压安装jdk 在shell终端下进入jdk-1_5_0_05-linux-i586.bin文件所在目录&#xff0c;执行命令 ./jdk-1_5_0_05-linux-i586.bin 这时会出现…

【C++】C++11 STL算法(六):最小/最大操作(Minimum/maximum operations)、比较运算(Comparison operations)

目录最小/最大操作(Minimum/maximum operations)一、max1、原型&#xff1a;2、说明&#xff1a;3、官方demo二、max_element1、原型&#xff1a;2、说明&#xff1a;3、官方demo三、min1、原型&#xff1a;2、说明&#xff1a;3、官方demo四、min_element1、原型&#xff1a;2…

springboot之定时任务

定时线程 说到定时任务&#xff0c;通常会想到JDK自带的定时线程来执行&#xff0c;定时任务。 回顾一下定时线程池。 public static ScheduledExecutorService newScheduledThreadPool(int var0) {return new ScheduledThreadPoolExecutor(var0);}public static ScheduledExec…

10只机器狗拉卡车!井然有序,毫不费力 | 极客头条

整理 | 琥珀出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;看来&#xff0c;这家娱乐网友多年的机器人公司终于要开始实现商用化了&#xff01;最先备受期待的是它的网红机器狗 SpotMini。今日凌晨&#xff0c;据多家外媒报道&#xff0c;波士顿动力 (Boston Dynami…

linux下查看nginx,apache,mysql,php的编译参数

有时候nginx&#xff0c;apache&#xff0c;mysql&#xff0c;php编译完了想看看编译参数可以用以下方法 nginx编译参数&#xff1a; #/usr/local/nginx/sbin/nginx -V nginx version: nginx/0.6.32 built by gcc 4.1.2 20071124 (Red Hat 4.1.2-42) configure arguments: --us…

【C++】C++11 STL算法(七):排列操作(Permutation operations)、数值操作(Numeric operations)

排列操作&#xff08;Permutation operations&#xff09; 一、is_permutation 1、原型&#xff1a; template< class ForwardIt1, class ForwardIt2 > bool is_permutation( ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2 );template< class ForwardIt…

码书:入门中文NLP必备干货:5分钟看懂“结巴”分词(Jieba)

导读&#xff1a;近年来&#xff0c;随着NLP技术的日益成熟&#xff0c;开源实现的分词工具越来越多&#xff0c;如Ansj、盘古分词等。在本文中&#xff0c;我们选取了Jieba进行介绍和案例展示&#xff0c;主要基于以下考虑&#xff1a;社区活跃。截止本文发布前&#xff0c;Ji…

《你必须掌握的Entity Framework 6.x与Core 2.0》正式出版感想

前言 借书正式出版之际&#xff0c;完整回顾下从写博客到写书整个历程&#xff0c;也算是对自己近三年在技术上的一个总结&#xff0c;整个历程可通过三个万万没想到来概括&#xff0c;请耐心阅读。 写博、写书完整历程回顾 从2013年12月注册博客园账号&#xff0c;注册博客园账…

JSF实现“Hello World!”

我们编写一个在页面上显示是“Hello World! ”&#xff0c;我们至少需要编写一个Page对象和一个对应模板文件&#xff08;tml&#xff09;。 第一步&#xff0c;Page对象编写 在Tapestry5中Page是与一个页面对应的POJO对象&#xff0c;它不需要继承Tapestry框架的任何基类或实现…

《权力的游戏》最终季上线!谁是你最喜爱的演员?这里有一份Python教程 | 附源码...

译者 | 刘畅编辑 | 琥珀出品 | AI科技大本营&#xff08;id&#xff1a;rgznai100&#xff09;《权力的游戏》最终季已于近日开播&#xff0c;对于全世界翘首以待的粉丝们来说&#xff0c;其最大的魅力就在于“无法预知的人物命运”。那些在魔幻时代的洪流中不断沉浮的人们&…

【C++】C++11 STL算法(八):对未初始化内存的操作(Operations on uninitialized memory)、C库(C library)

对未初始化内存的操作&#xff08;Operations on uninitialized memory&#xff09; 一、uninitialized_copy 1、原型&#xff1a; template< class InputIt, class ForwardIt > ForwardIt uninitialized_copy( InputIt first, InputIt last, ForwardIt d_first );2、…

OSPF高级设置实现全网互通

OSPF(开放式最短路径优先)是对链路状态路由协议的一种实现&#xff0c;隶属内部网关协议&#xff08;IGP&#xff09;&#xff0c;故运作于自治系统内部(AS)。采用戴克斯特拉算法&#xff08;Dijkstras algorithm&#xff09;被用来计算最短路径树。“Cost”作为路由度量值。链…

学习PHP ?

学PHP的决定真的是好的吗&#xff1f; 不怕又再错一次了吗&#xff1f; 已经是最后的一年半上学时间了.... 真的不愿再走之前那条失败的路&#xff0c;不愿&#xff0c;真的不愿&#xff1b; 这年半无论如何都要把一样技术搞精了 一年半的时间&#xff0c;对我来讲够了....只看…

【数据库】sqlite中的限制:数据库大小、表数、列数、行数、参数个数、连接数等

目录一、参考网址二、详解1、查看、设置sqlite限制命令.limit2、SQLite中的限制汇总1&#xff09;字符串或BLOB的最大长度2&#xff09;最大列数3&#xff09;SQL语句的最大长度4&#xff09;联接中的最大表数5&#xff09;表达式树的最大深度6&#xff09;函数的最大参数个数7…

flutter中的生命周期

前言 和其他的视图框架比如android的Activity一样&#xff0c;flutter中的视图Widget也存在生命周期&#xff0c;生命周期的回调函数提现在了State上面。理解flutter的生命周期&#xff0c;对我们写出一个合理的控件至关重要。组件State的生命周期整理如下图所示&#xff1a; 大…

小鱼易连获腾讯数亿C轮投资,云视频布局产业互联网

4 月 18 日&#xff0c;小鱼易连在北京举行 “鱼腾视界 产业互联” 战略合作暨融资发布会上&#xff0c;正式宣布获得 C 轮融资&#xff0c;由腾讯领投。融得的资金将全面用于小鱼易连云视频系统在产业互联网领域的落地&#xff0c;打通企业、政府、个人三者之间的柔性生态全产…

异步IO一定更好吗?

http://cnodejs.org/blog/?p1015续&#xff1a;异步IO一定更好吗&#xff1f;我之前的一篇文章《异步IO一定更好吗&#xff1f;》中举了一个很变态的例子&#xff0c;用以说明在单碟机械式硬盘上异步IO反而可能降低性能的问题&#xff0c;大家的讨论很热烈。前天的NodeParty杭…

谈谈Python那些不为人知的冷知识(二)

本文转载自Python的编程时光&#xff08;ID:Python-Time&#xff09;小明在日常Code中遇到一些好玩&#xff0c;冷门的事情&#xff0c;通常都会记录下来。从上一篇的分享来看&#xff0c;仍然有不少 Pythoner 对这些冷知识存在盲区&#xff0c;所以今天迎来第二篇。如果上篇你…

前端每日实战:45# 视频演示如何用纯 CSS 创作一个菱形 loader 动画

效果预览 按下右侧的“点击预览”按钮可以在当前页面预览&#xff0c;点击链接可以全屏预览。 https://codepen.io/comehope/pen/eKzjqK 可交互视频教程 此视频是可以交互的&#xff0c;你可以随时暂停视频&#xff0c;编辑视频中的代码。 请用 chrome, safari, edge 打开观看。…

【数据库】SQLite和MySQL之间的对比和选择

目录1、各自特定2、使用场景3、选择哪个1、各自特定 SQLite &#xff1a;独立、简单&#xff08;零配置&#xff09;&#xff1b;适用于为单个应用程序和设备提供本地数据存储。 MySQL&#xff1a;可伸缩、高并发性&#xff1b;适用于客户端/服务器模式企业数据的共享数据存储…

MySql中管理百万级要注意些什么东西(转载)

一、我们可以且应该优化什么&#xff1f; 硬件 操作系统/软件库 SQL服务器(设置和查询) 应 用编程接口(API) 应用程序 二、优化硬件 如果你需要庞大的数据库表 (>2G)&#xff0c;你应该考虑使用64位的硬件结构&#xff0c;像Alpha、Sparc或即将推出的IA64。因为MySQL内部使用…

【数据库】sqlite3数据库备份、导出方法汇总

【数据库】sqlite3常用命令及SQL语句 目录1、直接拷贝数据库2、使用.backup .clone1&#xff09;交互式2&#xff09;脚本3、导出到csv文件中&#xff08;其它格式类似&#xff09;1&#xff09;交互式2&#xff09;脚本3&#xff09;导出成其它格式汇总a> .mode asciib>…

高通与苹果宣布“复合”,英特尔黯然退场 | 极客头条

作者 | 郭芮转载自公众号CSDN&#xff08;ID:CSDNnews&#xff09;为期两年的苹果高通“诉讼之争”经历了各种推波助澜愈演愈烈&#xff0c;俨然到了最为关键的白热化阶段&#xff0c;没成想&#xff0c;在刚刚正式进入美国司法庭审环节的两天后却被强势叫停了&#xff01;4 月…

MQTT 协议 Client ID 长度不能超过23个字符

今天遇到一个MQTT的问题&#xff0c;MqttException: MQIsdp ClientId > 23 bytes ClientId的长度大于23时&#xff0c;无法链接MQTT服务器。 经过查看协议发现&#xff1a;客户端标识符(Client ID)是介于1和23个字符长度,客户端到服务器的唯一标识。它必须在搜有客户端连接到…

【数据库】适用于SQLite的SQL语句(一)

目录一、统计函数二、表TABLE1、创建表CREATE TABLE2、更改表ALTER TABLE3、删除表DROP TABLE三、分析表ANALYZE四、附加数据库 ATTACH DATABASE五、事务六、核心函数七、索引INDEX1、创建索引&#xff1a;CREATE INDEX2、查看索引&#xff1a;3、使用索引 INDEXED BY4、删除索…

谷歌大神Jeff Dean点赞网红博士论文:改进分布式共识机制 | 技术头条

作者 | Heidi Howard编译 | 刘静本文转载自公众号图灵TOPIA&#xff08;ID:turingtopia&#xff09;本文作者Heidi Howard&#xff0c;是剑桥大学计算机科学与技术系系统研究小组的分布式系统研究员。Heidi的研究领域一直围绕分布式系统中的一致性&#xff0c;容错性和性能并且…

使用Nginx做前端服务器时让Apache得到真实IP的方法

一&#xff1a;nginx.conf proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 其实这个proxy.conf里面默认都有&#xff0c;在nginx.conf使用include proxy.conf就可以 二&#xff1a;apa…