前言

最近在学习C# Socket相关的知识,学习之余,动手做了一个简单的局域网聊天器。有萌生做这个的想法,主要是由于之前家里两台电脑之间想要传输文件十分麻烦,需要借助QQ,微信或者其他第三方应用,基本都要登录,而且可能传输的文件还有大小限制,压缩问题。所以本聊天器的首要目标就是解决这两个问题,做到使用方便(双击启动即用),传文件无限制。

废话不多说,先上图。S-Chat是服务端,C-Chat是客户端,两者除了客户端首次启动后需要设置一下连接的IP地址外,无其他区别。操作与界面都完全相同,对于用户来说,基本不用在意谁是服务端谁是客户端。

编码

服务端监听接口

服务端主要负责开启监听线程,等待客户端接入

  1. public void StartListen()
  2. {
  3. // 创建Socket对象 new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
  4. Socket socket = GetSocket();
  5. // 将套接字与IPEndPoint绑定
  6. socket.Bind(this.GetIPEndPoint());
  7. // 开启监听 仅支持一个连接
  8. socket.Listen(1);
  9. // 开启线程等待客户端接入,避免堵塞
  10. Thread acceptThread = new Thread(new ThreadStart(TryAccept));
  11. acceptThread.IsBackground = true;
  12. acceptThread.Start();
  13. }
  14. public void TryAccept()
  15. {
  16. Socket socket = GetSocket();
  17. while (true)
  18. {
  19. try
  20. {
  21. Socket connectedSocket = socket.Accept()
  22. this.ConnectedSocket = connectedSocket;
  23. OnConnect(); // 连接成功回调
  24. this.StartReceive(); // 开始接收线程
  25. break;
  26. }
  27. catch (Exception e)
  28. {
  29. }
  30. }
  31. }

客户端连接接口

客户端主要负责开启连接线程,每隔2秒,自动尝试连接服务端

  1. public void StartConnect()
  2. {
  3. Thread connectThread = new Thread(new ThreadStart(TryConnect));
  4. connectThread.IsBackground = true;
  5. connectThread.Start();
  6. }
  7. public void TryConnect()
  8. {
  9. Socket socket = GetSocket();
  10. while (true)
  11. {
  12. try
  13. {
  14. socket.Connect(this.GetIPEndPoint());
  15. this.ConnectedSocket = socket;
  16. OnConnect(); // 连接成功回调
  17. this.StartReceive();
  18. break;
  19. }
  20. catch (Exception e)
  21. {
  22. Thread.Sleep(TryConnectInterval); // 指定间隔后重新尝试连接
  23. }
  24. }
  25. }

文字发送,文件发送,接收文字,接收文件等通用接口主要实现在ChatBase类中,是服务端与客户端的共同父类。

文字发送接口

发送数据的第一位表示发送信息的类型,0表示字符串文字,1表示文件

然后获取待发送字符串的长度,使用long类型表示,占用8个字节

共发送的字节数据可以表示为头部(类型 + 字符串字节长度,共9个字节)+ 实际字符串字节数据

  1. public bool Send(string msg)
  2. {
  3. if (ConnectedSocket != null && ConnectedSocket.Connected)
  4. {
  5. byte[] buffer = UTF8.GetBytes(msg);
  6. byte[] len = BitConverter.GetBytes((long)buffer.Length);
  7. byte[] content = new byte[1 + len.Length + buffer.Length];
  8. content[0] = (byte)ChatType.Str; // 发送信息类型,字符串
  9. Array.Copy(len, 0, content, 1, len.Length); // 字符串字节长度
  10. Array.Copy(buffer, 0, content, 1 + len.Length, buffer.Length); // 实际字符串字节数据
  11. try
  12. {
  13. ConnectedSocket.Send(content);
  14. return true;
  15. }
  16. catch (Exception e)
  17. {
  18. }
  19. }
  20. return false;
  21. }

文件发送接口

与字符串发送相同的头部可以表示为(类型 + 文件长度,共9个字节)

还需要再加上待发送的文件名的长度,与文件名字节数据

共发送的字节数据可以表示为头部(类型 + 文件长度,共9个字节)+ 文件名头部(文件名长度 + 文件名字节数据)+ 实际文件数据

  1. public bool SendFile(string path)
  2. {
  3. if (ConnectedSocket != null && ConnectedSocket.Connected)
  4. {
  5. try
  6. {
  7. FileInfo fi = new FileInfo(path);
  8. byte[] len = BitConverter.GetBytes(fi.Length);
  9. byte[] name = UTF8.GetBytes(fi.Name);
  10. byte[] nameLen = BitConverter.GetBytes(name.Length);
  11. byte[] head = new byte[1 + len.Length + nameLen.Length + name.Length];
  12. head[0] = (byte)ChatType.File; // 加上信息发送类型
  13. Array.Copy(len, 0, head, 1, len.Length); // 加上文件长度
  14. Array.Copy(nameLen, 0, head, 1 + len.Length, nameLen.Length); // 加上文件名长度
  15. Array.Copy(name, 0, head, 1 + len.Length + nameLen.Length, name.Length); // 加上文件名字节数据
  16. ConnectedSocket.SendFile(
  17. path,
  18. head,
  19. null,
  20. TransmitFileOptions.UseDefaultWorkerThread
  21. );
  22. return true;
  23. }
  24. catch(Exception e)
  25. {
  26. }
  27. }
  28. return false;
  29. }

信息接收接口(文字与文件)

主要是解析接收到的字节数据,根据字符串或文件的类型进行处理

  1. public void Receive()
  2. {
  3. if (ConnectedSocket != null)
  4. {
  5. while (true)
  6. {
  7. try
  8. {
  9. // 读取公共头部
  10. byte[] head = new byte[9];
  11. ConnectedSocket.Receive(head, head.Length, SocketFlags.None);
  12. int len = BitConverter.ToInt32(head, 1);
  13. if (head[0] == (byte) ChatType.Str)
  14. {
  15. // 接收字符串
  16. byte[] buffer = new byte[len];
  17. ConnectedSocket.Receive(buffer, len, SocketFlags.None);
  18. OnReceive(ChatType.Str, UTF8.GetString(buffer));
  19. }
  20. else if(head[0] == (byte)ChatType.File)
  21. {
  22. // 接收文件
  23. if (!Directory.Exists(dirName))
  24. {
  25. Directory.CreateDirectory(dirName);
  26. }
  27. // 读取文件名信息
  28. byte[] nameLen = new byte[4];
  29. ConnectedSocket.Receive(nameLen, nameLen.Length, SocketFlags.None);
  30. byte[] name = new byte[BitConverter.ToInt32(nameLen, 0)];
  31. ConnectedSocket.Receive(name, name.Length, SocketFlags.None);
  32. string fileName = UTF8.GetString(name);
  33. // 读取文件内容并写入
  34. int readByte = 0;
  35. int count = 0;
  36. byte[] buffer = new byte[1024 * 8];
  37. string filePath = Path.Combine(dirName, fileName);
  38. if (File.Exists(filePath))
  39. {
  40. File.Delete(filePath);
  41. }
  42. using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
  43. {
  44. while (count != len)
  45. {
  46. int readLength = buffer.Length;
  47. if(len - count < readLength)
  48. {
  49. readLength = len - count;
  50. }
  51. readByte = ConnectedSocket.Receive(buffer, readLength, SocketFlags.None);
  52. fs.Write(buffer, 0, readByte);
  53. count += readByte;
  54. }
  55. }
  56. OnReceive(ChatType.File, fileName);
  57. }
  58. else
  59. {
  60. // 未知类型
  61. }
  62. }
  63. catch (Exception e)
  64. {
  65. }
  66. }
  67. }
  68. }

使用

  • 第一次使用,客户端需要设置待连接的IP地址。之后再启动会自动连接

    1. 双击服务端exe启动,点击设置,查看IP地址项

    2. 双击客户端exe启动,点击设置,在IP地址项,输入服务端查看到的IP地址

  • 设置成功后,等待大约一两秒,应用cion变成绿色,即表示连接成功,可以正常发送文字和文件了

  • 可以点击选择文件(支持选择多个文件),发送文件

  • 支持直接拖拽文件到输入框,发送文件

  • 支持Ctrl+Enter快捷键发送

  • 接收到的文件自动存放在exe所在目录的ChatFiles文件夹下

注意事项

  • 客户端服务端需要在同一个局域网下才能实现连接
  • 服务端IP地址是不支持修改的,自动读取本机的IP地址

源码

  • 完整代码放在GitHub上,点击查看
  • 预编译好的可运行exe程序,在仓库的Release目录,也可以直接通过百度云下载,提取码v4pe

C# Socket编程实现简单的局域网聊天器的更多相关文章

  1. socket编程,简单多线程服务端测试程序

    socket编程,简单多线程服务端测试程序 前些天重温了MSDN关于socket编程的WSAStartup.WSACleanup.socket.closesocket.bind.listen.acce ...

  2. Socket编程的简单实现

    关于socket编程的简单实现,主要分成客户端.服务端两个部分.实现如下: 1.服务端代码如下,注意:server端要优先于client端启动 2.client端代码,以及启动后客户端和服务端之间的简 ...

  3. [JavaWeb基础] 024.Socket编程之简单的聊天程序

    1.Socket的简介 1)什么是Socket 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket通常用来实现客户方和服务方的连接.Socket ...

  4. java socket编程开发简单例子 与 nio非阻塞通道

    基本socket编程 1.以下只是简单例子,没有用多线程处理,只能一发一收(由于scan.nextLine()线程会进入等待状态),使用时可以根据具体项目功能进行优化处理 2.以下代码使用了1.8新特 ...

  5. [转] 3个学习Socket编程的简单例子:TCP Server/Client, Select

    以前都是采用ACE的编写网络应用,最近由于工作需要,需要直接只用socket接口编写CS的代码,重新学习这方面的知识,给出自己所用到的3个简单例子,都是拷贝别人的程序.如果你能完全理解这3个例子,估计 ...

  6. socket编程——一个简单的样例

    从一个简单的使用TCP样例開始socket编程,其基本过程例如以下: server                                                  client ++ ...

  7. socket编程——一个简单的例子

    从一个简单的使用TCP例子开始socket编程,其基本步骤如下: server                                                  client ++++ ...

  8. 5.1 socket编程、简单并发服务器

    什么是socket? socket可以看成是用户进程与内核网络协议栈的编程接口.是一套api函数. socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机间的进程间通信. 工业上使用的为t ...

  9. python socket编程 实现简单p2p聊天程序

    目标是写一个python的p2p聊天的项目,这里先说一下python socket的基础课程 一.Python Socket 基础课程 Socket就是套接字,作为BSD UNIX的进程通信机制,取后 ...

随机推荐

  1. vscode中的live-server配置https?

    json文件中使用绝对路径添加证书 "liveServer.settings.https": { "enable": true, "cert" ...

  2. labview程序结构

    三种范式:面向过程.面向事件,面向对象:六种模式:标准状态机.消息队列.生产者/消费者(事件).生产者/消费者(数据).用户界面事件处理.主/从 事件结构相当于是一个while语句里边嵌套了一个条件结 ...

  3. 吴裕雄--天生自然python学习笔记:Python3 标准库概览

    操作系统接口 os模块提供了不少与操作系统相关联的函数. >>> import os >>> os.getcwd() # 返回当前的工作目录 'C:\\Python ...

  4. JAVA9中文API百度网盘免费下载

    JAVA9中文API百度网盘免费下载: https://pan.baidu.com/s/1tvHYQA8yyAS4xUFxwWrx_Q 提取码: 6e5h

  5. iOS多线程开发之GCD(中级篇)

    前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...

  6. Mybatis调用存储过程报错

    Mybatis调用存储过程 贴码 123456 Error querying database. Cause: java.sql.SQLException: User does not have ac ...

  7. 关于RN的热更新

    写点关于RN的热更新和RN版本升级后的强制更新.以及优化白屏问题 在APPDelegate中加载RN,一般的加载方式是:RCTRootView *rootView= [[RCTRootView all ...

  8. 【简单版】hexo博客搭建流程梳理

    前言 本文章会为你梳理一个搭建hexo博客的流程 相关网址: Docs: https://hexo.io/docs/ Themes: https://hexo.io/themes/ 安装hexo 准备 ...

  9. GoLand2019.03破解与汉化

    1.准备工作 (请认真的做好准备工作,否则中途可能会操作失败.) GoLand是JetBrains公司发布的商业版的GO语言编辑器(收费的),本屌目前还没钱购买正版,所以本次教程是以Windows平台 ...

  10. 《SDN期末作业——实现负载均衡》

    队名:取个队名真难 一.网络拓扑(场景二) 二.负载均衡程序 1.建立拓扑的代码 拓扑 2.下发组表流表的代码 下发组表流表 三.演示视频 1.目的 服务器h2,h3,h4上各自有不同的服务,h1是客 ...