热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

基于c#用Socket做一个局域网聊天工具

目前基于Internet的即时聊天工具已经做的非常完美,本文介绍了基于c#用Socket做一个局域网聊天工具,有需要的朋友可以看一下。

程序设计成为简单的服务端和客户端之间的通信, 但通过一些方法可以将这两者进行统一起来, 让服务端也成为客户端, 让客户端也成为服务端, 使它们之间可以互相随时不间断的通信. 考虑到实现最原始的服务端和客户端之间的通信所需要的步骤对于写这样的程序是很有帮助的.

作为服务端, 要声明一个Socket A并绑定(Bind)某一个IP+这个IP指定的通信端口, 比如这个是127.0.0.1:9050, 然后开始监听(Listen), Listen可以监听来自多个IP传过来的连接请求, 具体可以同时连接几个客户端, Listen方法中可以设定一个参数. 如果Listen到某一个客户端发来连接请求了, 这时定义一个新的Socket B专门负责与这个客户端的通信, Socket B = A.Accept(). 这时可以获取这个客户端的IP和端口,  IPEndPoint C = (IPEndPoint)B.RemoteEndPoint, C.Address和C.Port分别表示客户端C的IP地址和端口. 这时通过B.Send()方法就可以给C发送消息了, B.Receive()可以接收客户端C发来的信息.

作为客户端, 也需要声明一个Socket D并绑定某一个IP+本机一个未被占用的端口, 定义IPEndPoint E表示要进行连接的服务端Socket, 要指明E的IP和端口, 这样才可以进行端口对端口之间的通信, 接下来就可以尝试D.Connect(E), 连接成功之后就可以发送和接收数据了, D.Send(), D.Receive.

发送消息时, 数据都是以字节或字节数组为单位进行传输的, 比如我客户端D要发送"Hello World"则要这样写: D.Send(Encoding.ASCII.GetBytes("Hello World")).  接受消息时, 也是以字节或字节数组, 比如服务端要接受D刚才发送的Hello World, 可以这样写: Byte[] data = new Byte[1024]; int receivedDataLength = B.Receive(data); string stringdata = Encoding.ASCII.GetString(data, 0, receivedDataLength); stringdata这时就是Hello World.

上面只是大概的阐述了服务端与客户端之间的通信过程, 在网上找到了具体的代码例子, 也贴过来参考参考. 这个例子没有将服务端与客户端统一起来, 他是分别写服务端和客户端的.

服务端代码

using System;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace tcpserver
{
   ///   
   /// Class1 的摘要说明。
   ///   
   class server
  {
     ///   
     /// 应用程序的主入口点。
     ///   
    [STAThread]
     static  void Main( string [] args)
    {
       // 
       // TODO: 在此处添加代码以启动应用程序
       //
      int recv; // 用于表示客户端发送的信息长度
      byte [] data;// = new  byte [ 1024 ]; // 用于缓存客户端所发送的信息,通过socket传递的信息必须为字节数组 
      IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050 ); // 本机预使用的IP和端口 
      Socket newsock = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
      newsock.Bind(ipep); // 绑定
      newsock.Listen( 10 ); // 监听
      Console.WriteLine( " waiting for a client " );
      Socket client = newsock.Accept(); //当有可用的客户端连接尝试时执行,并返回一个新的socket,用于与客户端之间的通信 
      IPEndPoint clientip = (IPEndPoint)client.RemoteEndPoint;
      Console.WriteLine( " connect with client: " + clientip.Address + " at port: " + clientip.Port);
      string welcome = " welcome here! " ;
      data = Encoding.ASCII.GetBytes(welcome);
      client.Send(data,data.Length,SocketFlags.None); // 发送信息 
      while ( true )
      { // 用死循环来不断的从客户端获取信息 
        data = new  byte [ 1024 ];
        recv = client.Receive(data);
        Console.WriteLine( " recv= " + recv);
        if (recv == 0 ) // 当信息长度为0,说明客户端连接断开 
           break ;
        Console.WriteLine(Encoding.ASCII.GetString(data, 0 ,recv));
        client.Send(data,recv,SocketFlags.None);
      }
      Console.WriteLine( " Disconnected from " + clientip.Address);
      client.Close();
      newsock.Close();
    }
  }
}

客户端代码

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace tcpclient
{
  ///   
  /// Class1 的摘要说明。
  ///   
  class client
  {
    ///   
    /// 应用程序的主入口点。
    ///   
    [STAThread]
    static void Main(string[] args)
    {
      // 
      // TODO: 在此处添加代码以启动应用程序
      //
      byte[] data = new byte[1024];
      Socket newclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
      newclient.Bind(new IPEndPoint(IPAddress.Any, 905));
      Console.Write(" please input the server ip: ");
      string ipadd = Console.ReadLine();
      Console.WriteLine();
      Console.Write(" please input the server port: ");
      int port = Convert.ToInt32(Console.ReadLine());
      IPEndPoint ie = new IPEndPoint(IPAddress.Parse(ipadd), port); // 服务器的IP和端口
      try
      {
        // 因为客户端只是用来向特定的服务器发送信息,所以不需要绑定本机的IP和端口。不需要监听。 
        newclient.Connect(ie);
      }
      catch (SocketException e)
      {
        Console.WriteLine(" unable to connect to server ");
        Console.WriteLine(e.ToString());
        return;
      }
      int receivedDataLength = newclient.Receive(data);
      string stringdata = Encoding.ASCII.GetString(data, 0, receivedDataLength);
      Console.WriteLine(stringdata);
      while (true)
      {
        string input = Console.ReadLine();
        if (input == " exit ")
          break;
        newclient.Send(Encoding.ASCII.GetBytes(input));
        data = new byte[1024];
        receivedDataLength = newclient.Receive(data);
        stringdata = Encoding.ASCII.GetString(data, 0, receivedDataLength);
        Console.WriteLine(stringdata);
      }
      Console.WriteLine(" disconnect from sercer ");
      newclient.Shutdown(SocketShutdown.Both);
      newclient.Close();
    }
  }
}

上面的服务端和客户端都是控制台应用程序, 想办法做一个窗体类型的, 思路就是另起一个线程, 这个线程专门负责两端建立连接. 如果不采用另起线程的方法, 当等待连接而没有连接上, 或者主动连接, 服务端还没有相应时, 程序就会出现没有响应的假死状态.

当这个线程将两个端口连接成功后, 就让程序进入一个死循环, 这个死循环负责不断的接收是否有消息传来, 传来的话就在txtGetMsg中显示出来:

while (true)  // 用死循环来不断的获取信息
{
  data = new byte[1024];
  recv = newclient.Receive(data);
 
  uiContext.Send(new SendOrPostCallback(
  state =>
  {
    int txtGetMsgLength = txtGetMsg.Text.Length;
    string recMsg = "Friend:    " + System.DateTime.Now.ToString() + "\n  " +Encoding.Unicode.GetString(data, 0, recv) + "\n";
    txtGetMsg.AppendText(recMsg);
    txtGetMsg.Select(txtGetMsgLength, recMsg.Length - Encoding.Unicode.GetString(data, 0, recv).Length - 1);
    txtGetMsg.SelectiOnColor= Color.Red;
  }), null);
}

如果按下发送消息的按钮, 则发送txtSendMsg中的文本, 我写的是用Unicode编码, 所以可以发送中文字符.

private void btnSendMsg_Click(object sender, EventArgs e)
{
  string input = txtSendMsg.Text;
  if (input == "")
  {
    MessageBox.Show("消息不能为空!", "发送消息出错");
    txtSendMsg.Focus();
  }
  else
  {
    if (meIsClient)
    {
      newclient.Send(Encoding.Unicode.GetBytes(input));
      string showText = "Me:      " + System.DateTime.Now.ToString() + "\n  "
      + input + "\n";
      int txtGetMsgLength = txtGetMsg.Text.Length;
      txtGetMsg.AppendText(showText);
      txtGetMsg.Select(txtGetMsgLength, showText.Length - 1 - input.Length);
      txtGetMsg.SelectiOnColor= Color.Blue;
      txtSendMsg.Text = "";
    }
    else
    {
      client.Send(Encoding.Unicode.GetBytes(input));
      string showText = "Me    " + System.DateTime.Now.ToString() + "\n  "
      + input + "\n";
      int txtGetMsgLength = txtGetMsg.Text.Length;
      txtGetMsg.AppendText(showText);
      txtGetMsg.Select(txtGetMsgLength, showText.Length - 1 - input.Length);
      txtGetMsg.SelectiOnColor= Color.Blue;
      txtSendMsg.Text = "";
    }
  }
}

程序的运行效果:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 如何基于ggplot2构建相关系数矩阵热图以及一个友情故事
    本文介绍了如何在rstudio中安装ggplot2,并使用ggplot2构建相关系数矩阵热图。同时,通过一个友情故事,讲述了真爱难觅的故事背后的数据量化和皮尔逊相关系数的概念。故事中的小伙伴们在本科时参加各种考试,其中有些沉迷网络游戏,有些热爱体育,通过他们的故事,展示了不同兴趣和特长对学习和成绩的影响。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
author-avatar
Jacky-xu1992
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有