加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

C# 网络编程之简易聊天示例

发布时间:2020-12-16 01:13:23 所属栏目:百科 来源:网络整理
导读:还记得刚刚开始接触编程开发时,傻傻的将网站开发和网络编程混为一谈,常常因分不清楚而引为笑柄。后来勉强分清楚,又因为各种各样的协议端口之类的名词而倍感神秘,所以为了揭开网络编程的神秘面纱,本文尝试以一个简单的小例子,简述在网络编程开发中涉及

还记得刚刚开始接触编程开发时,傻傻的将网站开发和网络编程混为一谈,常常因分不清楚而引为笑柄。后来勉强分清楚,又因为各种各样的协议端口之类的名词而倍感神秘,所以为了揭开网络编程的神秘面纱,本文尝试以一个简单的小例子,简述在网络编程开发中涉及到的相关知识点,仅供学习分享使用,如有不足之处,还请指正。

概述

在TCP/IP协议族中,传输层主要包括TCP和UDP两种通信协议,它们以不同的方式实现两台主机中的不同应用程序之间的数据传输,即数据的端到端传输。由于它们的实现方式不同,因此各有一套属于自己的端口号,且相互独立。采用五元组(协议,信源机IP地址,信源应用进程端口,信宿机IP地址,信宿应用进程端口)来描述两个应用进程之间的通信关联,这也是进行网络程序设计最基本的概念。传输控制协议(Transmission Control Protocol,TCP)提供一种面向连接的、可靠的数据传输服务,保证了端到端数据传输的可靠性。

涉及知识点

本例中涉及知识点如下所示:

  1. TcpClient : TcpClient类为TCP网络服务提供客户端连接,它构建于Socket类之上,以提供较高级别的TCP服务,提供了通过网络连接、发送和接收数据的简单方法。
  2. TcpListener:构建于Socket之上,提供了更高抽象级别的TCP服务,使得程序员能更方便地编写服务器端应用程序。通常情况下,服务器端应用程序在启动时将首先绑定本地网络接口的IP地址和端口号,然后进入侦听客户请求的状态,以便于客户端应用程序提出显式请求。
  3. NetworkStream:提供网络访问的基础数据流。一旦侦听到有客户端应用程序请求连接侦听端口,服务器端应用将接受请求,并建立一个负责与客户端应用程序通信的信道。

网络聊天示意图

如下图所示:看似两个在不同网络上的人聊天,实际上都是通过服务端进行接收转发的。

TCP网络通信示意图

如下图所示:首先是服务端进行监听,当有客户端进行连接时,则建立通讯通道进行通信。

示例截图

服务端截图,如下所示:

客户端截图,如下所示:开启两个客户端,开始美猴王和二师兄的对话。

核心代码

发送信息类,如下所示:

 1 using System;
 2  System.Collections.Generic;
 3  System.Linq;
 4  System.Text;
 5  System.Threading.Tasks;
 6 
 7 namespace Common
 8 {
 9     /// <summary>
10     /// 定义一个类,所有要发送的内容,都按照这个来
11     </summary>
12     public class ChatMessage
13     {
14         15          头部信息
16         17         public ChatHeader header { get; set; }
18 
19         20          信息类型,默认为文本
21         22         public ChatType chatType { 23 
24         25          内容信息
26         27         string info { 28 
29     }
30 
31     32     33     34      ChatHeader
35 36         37          id唯一标识
38         39         string id { 40 
41         42          源:发送方
43         44         string source { 45 
46         47          目标:接收方
48         49         string dest { 50 
51 52 
53     54      内容标识
55     56     enum ChatMark
57 58         BEGIN  = 0x0000,59         END = 0xFFFF
60 61 
62      ChatType {
63         TEXT=064         IMAGE=1
65 66 }
View Code

打包帮助类,如下所示:所有需要发送的信息,都要进行封装,打包,编码成固定格式,方便解析。

 包帮助类
 PackHelper
 获取待发送的信息
17         <param name="text"></param>
18         <returns></returns>
19         static byte[] GetSendMsgBytes(string text,string source,1)">string dest)
20         {
21             ChatHeader header = new ChatHeader()
22             {
23                 source = source,1)">24                 dest = dest,1)">25                 id = Guid.NewGuid().ToString()
26             };
27             ChatMessage msg =  ChatMessage()
28 29                 chatType = ChatType.TEXT,1)">30                 header = header,1)">31                 info = text
32 33             string msg01 = GeneratePack<ChatMessage>(msg);
34             byte[] buffer = Encoding.UTF8.GetBytes(msg01);
35             return buffer;
36         }
37 
39          生成要发送的包
40         <typeparam name="T"></typeparam>
<param name="t"></param>
string GeneratePack<T>(T t) {
45             string send = SerializerHelper.JsonSerialize<T>(t);
46             string res = string.Format("{0}|{1}|{2}",ChatMark.BEGIN.ToString(X").PadLeft(4,'0'),send,ChatMark.END.ToString('));
47             int length = res.Length;
48 
49             return {0}|{1}),res);
50 51 
52         53          解析包
54         55         56         <param name="receive">原始接收数据包</param>
57         58         static T ParsePack<T>(string msg,1)">out  error)
59 60             error = .Empty;
61             int len = int.Parse(msg.Substring(0,1)">4));//传输内容的长度
62             string msg2 = msg.Substring(msg.IndexOf(|") + 1);
63             string[] array = msg2.Split(64             if (msg2.Length == len)
66                 string receive = array[];
67                 string begin = array[68                 string end = array[269                 if (begin == ChatMark.BEGIN.ToString(') && end == ChatMark.END.ToString())
70                 {
71                     T t = SerializerHelper.JsonDeserialize<T>(receive);
72                     if (t != null)
73                     {
74                          t;
75 
76                     }
77                     else {
78                         error = 接收的数据有误,无法进行解析"79                         default(T);
80 81                 }
82                 83                     error = 接收的数据格式有误,无法进行解析84                     85 86             }
87             88                 error = 接收数据失败,长度不匹配,定义长度{0},实际长度{1}89                 90 91 92 93 }
View Code

服务端类,如下所示:服务端开启时,需要进行端口监听,等待链接。

 Common;
 System.Configuration;
 System.IO;
 6  System.Net;
 System.Net.Sockets;
 9 10  System.Threading;
11 12 
13 14  描述:MeChat服务端,用于接收数据
15 16  MeChatServer
17 18      Program
19  服务端IP
22         23         private  IP;
24 
 服务端口
27         28         int PORT;
29 
30         31          服务端监听
32         33         static TcpListener tcpListener;
34 
35 
36         void Main([] args)
37 38             初始化信息
39             InitInfo();
40             IPAddress ipAddr = IPAddress.Parse(IP);
41             tcpListener =  TcpListener(ipAddr,PORT);
42             tcpListener.Start();
43           
44             Console.WriteLine(等待连接45             tcpListener.BeginAcceptTcpClient(new AsyncCallback(AsyncTcpCallback),1)">async如果用户按下Esc键,则结束
while (Console.ReadKey().Key != ConsoleKey.Escape)
48 49                 Thread.Sleep(200            tcpListener.Stop();
52 53 
 初始化信息
57         void InitInfo() {
58             初始化服务IP和端口
59             IP = ConfigurationManager.AppSettings[ip60             PORT = int.Parse(ConfigurationManager.AppSettings[port]);
初始化数据池
62             PackPool.ToSendList = new List<ChatMessage>();
63             PackPool.HaveSendList = 64             PackPool.obj = new object66 
67         68          Tcp异步接收函数
69         70         <param name="ar"></param>
71          AsyncTcpCallback(IAsyncResult ar) {
72             Console.WriteLine(已经连接73             ChatLinker linker =  ChatLinker(tcpListener.EndAcceptTcpClient(ar));
74             linker.BeginRead();
75             继续下一个连接
76             Console.WriteLine(77             tcpListener.BeginAcceptTcpClient(78 79 80 }
View Code

客户端类,如下所示:客户端主要进行数据的封装发送,接收解析等操作,并在页面关闭时,关闭连接。

  1   2   3   4  System.ComponentModel;
  5  System.Data;
  6  System.Drawing;
  7   8   9  10  11  12  System.Windows.Forms;
 13 
 14  MeChatClient
 15  16      17      聊天页面
 18      19     partial  FrmMain : Form
 20  21          22          链接客户端
 23          24         private TcpClient tcpClient;
 25 
 26          27          基础访问的数据流
 28          29          NetworkStream stream;
 30 
 31          32          读取的缓冲数组
 33          34         byte[] bufferRead;
 35 
 36          37          昵称信息
 38          39         private Dictionary<string,1)">string> dicNickInfo;
 40 
 41         public FrmMain()
 42  43             InitializeComponent();
 44  45 
 46         void MainForm_Load( sender,EventArgs e)
 47  48             获取昵称
 49             dicNickInfo = ChatInfo.GetNickInfo();
 50             设置标题
 51             string title = :{0}-->{1} 的对话 52             this.Text = {0}:{1}this.Text,title);
 53             初始化客户端连接
 54             this.tcpClient =  TcpClient(AddressFamily.InterNetwork);
 55             bufferRead = byte[.tcpClient.ReceiveBufferSize];
 56             this.tcpClient.BeginConnect(ChatInfo.IP,ChatInfo.PORT,1)">new AsyncCallback(RequestCallback),1)"> 57           
 58  59 
 60          61          异步请求链接函数
 62          63          64          RequestCallback(IAsyncResult ar) {
 65             .tcpClient.EndConnect(ar);
 66             this.lblStatus.Text = 连接服务器成功;
 67             获取流
 68             stream = .tcpClient.GetStream();
 69             先发送一个连接信息
 70             string text = CommonVar.LOGIN;
 71              PackHelper.GetSendMsgBytes(text,ChatInfo.Source,ChatInfo.Source);
 72             stream.BeginWrite(buffer,buffer.Length,1)">new AsyncCallback(WriteMessage),1)"> 73             只有stream不为空的时候才可以读
 74             stream.BeginRead(bufferRead,bufferRead.Length,1)">new AsyncCallback(ReadMessage),1)"> 75  76 
 77          78          发送信息
 79          80         <param name="sender"></param>
 81         <param name="e"></param>
 82         void btnSend_Click( 83  84             string text = .txtMsg.Text.Trim();
 85             if( .IsNullOrEmpty(text)){
 86                 MessageBox.Show(要发送的信息为空 87                  88  89              ChatInfo.GetSendMsgBytes(text);
 90             stream.BeginWrite(buffer,1)"> 91             this.rtAllMsg.AppendText(rn[{0}] 92             this.rtAllMsg.SelectionAlignment = HorizontalAlignment.Right;
 93             rn{0} 94              95  96 
 97     
 98          99          异步读取信息
100         101         102          ReadMessage(IAsyncResult ar)
103 104             if (stream.CanRead)
105 106                  stream.EndRead(ar);
107                 if (length >= 108 109 
110                     string msg = 111                     msg = string.Concat(msg,Encoding.UTF8.GetString(bufferRead,length));
112                     处理接收的数据
113                     string error = 114                     ChatMessage t = PackHelper.ParsePack<ChatMessage>(msg,1)">out error);
115                     if (.IsNullOrEmpty(error))
116 117                         this.rtAllMsg.Invoke(new Action(() =>
118                         {
119                             120                              HorizontalAlignment.Left;
121                             122                             123                             接收数据成功!124                         }));
125 126                     127                         接收数据失败:"+error;
128 129 130                 继续读数据
131                 stream.BeginRead(bufferRead,1)">132 133 134 
135         136          发送成功
137         138         139          WriteMessage(IAsyncResult ar)
140 141             .stream.EndWrite(ar);
142             发送成功
143 144 
145         146          页面关闭,断开连接
147         148         149         150         void FrmMain_FormClosing(151 152             if (MessageBox.Show(正在通话中,确定要关闭吗?关闭 DialogResult.Yes)
153 154                 e.Cancel = false155                  CommonVar.QUIT;
156                 157                 stream.Write(buffer,buffer.Length);
158                 发送完成后,关闭连接
159                 .tcpClient.Close();
160 
161 162             163                 e.Cancel = true164 165 166 167 }
View Code

备注:本示例中,所有的建立连接,数据接收,发送等都是采用异步方式,防止页面卡顿。

源码下载链接

备注

每一次的努力,都是幸运的伏笔。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读