服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 基于c#用Socket做一个局域网聊天工具

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

2021-12-08 13:14Create Chen C#

目前基于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.

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

服务端代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using system;
using system;
using system.net;
using system.net.sockets;
using system.text;
namespace tcpserver
{
   ///  <summary>
   /// class1 的摘要说明。
   ///  </summary>
   class server
  {
     ///  <summary>
     /// 应用程序的主入口点。
     ///  </summary>
    [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();
    }
  }
}

客户端代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using system;
using system.net;
using system.net.sockets;
using system.text;
namespace tcpclient
{
  ///  <summary>
  /// class1 的摘要说明。
  ///  </summary>
  class client
  {
    ///  <summary>
    /// 应用程序的主入口点。
    ///  </summary>
    [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中显示出来:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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编码, 所以可以发送中文字符.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 = "";
    }
  }
}

程序的运行效果:

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

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

延伸 · 阅读

精彩推荐
  • C#C#直线的最小二乘法线性回归运算实例

    C#直线的最小二乘法线性回归运算实例

    这篇文章主要介绍了C#直线的最小二乘法线性回归运算方法,实例分析了给定一组点,用最小二乘法进行线性回归运算的实现技巧,具有一定参考借鉴价值,需要...

    北风其凉8912021-10-18
  • C#聊一聊C#接口问题 新手速来围观

    聊一聊C#接口问题 新手速来围观

    聊一聊C#接口问题,新手速来围观,一个通俗易懂的例子帮助大家更好的理解C#接口问题,感兴趣的小伙伴们可以参考一下...

    zenkey7072021-12-03
  • C#C# 后台处理图片的几种方法

    C# 后台处理图片的几种方法

    本篇文章主要介绍了C# 后台处理图片的几种方法,非常具有实用价值,需要的朋友可以参考下。...

    IT小伙儿10162021-12-08
  • C#c#学习之30分钟学会XAML

    c#学习之30分钟学会XAML

    一个界面程序的核心,无疑就是界面和后台代码,而xaml就是微软为构建应用程序界面而创建的一种描述性语言,也就是说,这东西是搞界面的...

    C#教程网8812021-12-10
  • C#浅谈C# winForm 窗体闪烁的问题

    浅谈C# winForm 窗体闪烁的问题

    下面小编就为大家带来一篇浅谈C# winForm 窗体闪烁的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网7962021-12-21
  • C#C#基础之泛型

    C#基础之泛型

    泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能。接下来通过本文给大家介绍c#基础之泛型,感兴趣的朋友一起学习吧...

    方小白7732021-12-03
  • C#Unity3D UGUI实现缩放循环拖动卡牌展示效果

    Unity3D UGUI实现缩放循环拖动卡牌展示效果

    这篇文章主要为大家详细介绍了Unity3D UGUI实现缩放循环拖动展示卡牌效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参...

    诗远3662022-03-11
  • C#C#实现的文件操作封装类完整实例【删除,移动,复制,重命名】

    C#实现的文件操作封装类完整实例【删除,移动,复制,重命名】

    这篇文章主要介绍了C#实现的文件操作封装类,结合完整实例形式分析了C#封装文件的删除,移动,复制,重命名等操作相关实现技巧,需要的朋友可以参考下...

    Rising_Sun3892021-12-28