WinForm版聊天室复习Socket通信
聊天室:服务器端-------------客户端
最终演示展示图:
一. 服务器端
对服务端为了让主窗体后台不处理具体业务逻辑,因此对服务端进行了封装,专门用来处理某个客户端通信的过程。
而由于通信管理类中需要处理具体与某个客户端的通信业务,所以在构造函数中传入了具体的套接字对象。
针对消息提醒:由于需要再通信管理类中进行消息提示,而需要调用主窗体的ShowMsg方法。因此将打印消息的方法通过委托传给了通信管理类的构造函数
同理针对意外关闭的客户端连接也同样通过委托将移除客户端的方法传给了通信管理类。
因此,再通信管理类的全局变量里就有了如下的定义:
//与某个客户端通信套接字
Socket sokMsg = null;
//通信线程
Thread thrMsg = null;
//创建一个委托对象, 在窗体显示消息的方法
DGShowMsg dgShow = null;
//创建一个关闭连接的方法
DGCloseConn dgCloseConn = null;
//通信管理类的构造函数
public MsgConnection(Socket sokMsg, DGShowMsg dgShow, DGCloseConn dgCloseConn).........
1.0 开始监听
1.1.1 创建监听套接字
//创建监听套接字,使用ip4协议,流式传输,tcp链接
sokWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
1.1.2 绑定端口
//获取网络节点对象
IPAddress address = IPAddress.Parse(txbIp.Text);
IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txbPort.Text));
// 绑定端口(其实内部 就向系统的端口表中注册了一个端口,并指定了当前程序句柄)
sokWatch.Bind(endPoint);
1.1.3 设置监听队列,指限制同时处理的连接请求数,即同时处理的客户端连接请求。
sokWatch.Listen();
1.1.4 开始监听,调用监听线程 执行 监听套接字的监听方法。
thrWatch = new Thread(WatchConncting);
thrWatch.IsBackground = true;
thrWatch.Start();
ShowMsg("服务器启动!");
2.0 服务器监听方法 + void WatchConncting()
void WatchConncting()
{
try
{
//循环监听客户端的连接请求。
while (isWatch)
{
//2.4开始监听,返回了一个通信套接字
Socket sockMsg = sokWatch.Accept();
//2.5 创建通信管理类
MsgConnection conn = new MsgConnection(sockMsg, ShowMsg, RemoveClient); //将当前连接成功的【与客户端通信的套接字】的标识保存起来,并显示到列表中
//将远程客户端的 ip 和 端口 字符串 存入列表
listOnline.Items.Add(sockMsg.RemoteEndPoint.ToString());
//将服务器端的通信套接字存入字典集合。
dictConn.Add(sockMsg.RemoteEndPoint.ToString(), conn);
ShowMsg("有客户端连接了!");
}
}
catch (Exception ex)
{
ShowMsg("异常" + ex);
}
}
3.0 服务端向指定的客户端发送消息
string strClient = listOnline.Text;
if (dictConn.ContainsKey(strClient))
{
string strMsg = txtInput.Text.Trim();
ShowMsg("向客户端【" + strClient + "】说:" + strMsg);
//通过指定的套接字将字符串发送到指定的客户端
try
{
dictConn[strClient].Send(strMsg);
}
catch (Exception ex)
{
ShowMsg("异常" + ex.Message);
}
}
4.0 根据要中断的客户端ipport关闭连接 + void RemoveClient(string clientIpPort)
//1.0 移除列表中的项
listOnline.Items.Remove(clientIpPort);
//2.0 关闭通信管理类
dictConn[clientIpPort].Close();
//3.0 从字典中移除对应的通信管理类的项
dictConn.Remove(clientIpPort);
5.0 选择要发送的文件
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
//将选中的要发送的文件路径,显示到文本框中。
txtFilePath.Text = ofd.FileName;
}
6.0 发送文件
string strClient = listOnline.Text;
if (dictConn.ContainsKey(strClient))
{
string strMsg = txtInput.Text.Trim();
//通过指定的套接字将字符串发送到指定的客户端
try
{
dictConn[strClient].SendFile(txtFilePath.Text.Trim());
}
catch (Exception ex)
{
ShowMsg("异常" + ex.Message);
}
}
7.0 向指定的客户端发送抖屏
string strClient = listOnline.Text;
if (dictConn.ContainsKey(strClient))
{
string strMsg = txtInput.Text.Trim();
//通过指定的套接字将字符串发送到指定的客户端
try
{
dictConn[strClient].SendShake();
}
catch (Exception ex)
{
ShowMsg("异常" + ex.Message);
}
}
8.0 打印消息 + ShowMsg(string strmsg)
this.txtShow.AppendText(strmsg + "\r\n");
服务器端通信管理类,负责处理与某个客户端通信的过程
public class MsgConnection
{
//与某个客户端通信套接字
Socket sokMsg = null;
//通信线程
Thread thrMsg = null;
//创建一个委托对象, 在窗体显示消息的方法
DGShowMsg dgShow = null;
//创建一个关闭连接的方法
DGCloseConn dgCloseConn = null; #region 1.0 构造函数
public MsgConnection(Socket sokMsg, DGShowMsg dgShow, DGCloseConn dgCloseConn)
{
this.sokMsg = sokMsg;
this.dgShow = dgShow;
this.dgCloseConn = dgCloseConn;
//创建通信线程,负责调用通信套接字,来接收客户端消息。
thrMsg = new Thread(ReceiveMsg);
thrMsg.IsBackground = true;
thrMsg.Start(this.sokMsg);
}
#endregion bool isReceive = true;
#region 2.0 接收客户端发送的消息
void ReceiveMsg(object obj)
{
Socket sockMsg = obj as Socket;
//3 通信套接字 监听客户端的消息,传输的是byte格式。
//3.1 开辟了一个 1M 的空间,创建的消息缓存区,接收客户端的消息。
byte[] arrMsg = new byte[ * * ];
try
{
while (isReceive)
{
//注意:Receive也会阻断当前的线程。
//3.2 接收客户端的消息,并存入消息缓存区。
//并 返回 真实接收到的客户端数据的字节长度。
int realLength = sockMsg.Receive(arrMsg);
//3.3 将接收的消息转成字符串
string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg, , realLength);
//3.4 将消息显示到文本框
dgShow(strMsg);
}
}
catch (Exception ex)
{
//调用窗体类的关闭移除方法
dgCloseConn(sokMsg.RemoteEndPoint.ToString());
//显示消息
dgShow("客户端断开连接!");
}
}
#endregion #region 3.0 向客户端发送文本消息 + void Send(string msg)
/// <summary>
/// 3.0 向客户端发送文本消息
/// </summary>
/// <param name="msg"></param>
public void Send(string msg)
{
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(msg);
//通过指定的套接字将字符串发送到指定的客户端
try
{
sokMsg.Send(MakeNewByte("str",arrMsg));
}
catch (Exception ex)
{
dgShow("异常" + ex.Message);
}
}
#endregion #region 4.0 向客户端发送文件 + void SendFile(string strPath)
/// <summary>
/// 4.0 向客户端发送文件
/// </summary>
/// <param name="strFilePath"></param>
public void SendFile(string strFilePath)
{
//4.1 读取要发送的文件
byte[] arrFile = System.IO.File.ReadAllBytes(strFilePath);
//4.2 向客户端发送文件
sokMsg.Send(MakeNewByte("file", arrFile));
}
#endregion #region 4.1 向客户端发送抖屏命令 + void SendShake()
/// <summary>
/// 4.1 向客户端发送抖屏命令
/// </summary>
public void SendShake()
{
sokMsg.Send(new byte[] { });
}
#endregion #region 5.0 返回带标识的新数组 + byte[] MakeNew(string type, byte[] oldArr)
/// <summary>
/// 返回带标识的新数组
/// </summary>
/// <param name="type"></param>
/// <param name="oldArr"></param>
/// <returns></returns>
public byte[] MakeNewByte(string type, byte[] oldArr)
{
//5.1 创建一个新数组(是原数组长度 +1)
byte[] newArrFile = new byte[oldArr.Length + ];
//5.2 将原数组数据复制到新数组中(从新数组下标为1的位置开始)
oldArr.CopyTo(newArrFile, );
//5.3 根据内容类型为新数组第一个元素设置标识符号
switch (type.ToLower())
{
case "str":
newArrFile[] = ; //只能存0-255之间的数值
break;
case "file":
newArrFile[] = ;
break;
default:
newArrFile[] = ;
break;
}
return newArrFile;
}
#endregion #region 6.0 关闭通信
/// <summary>
/// 关闭通信
/// </summary>
public void Close()
{
isReceive = false;
sokMsg.Close();
sokMsg = null;
}
#endregion
}
二 . 客户端
1.0 发送连接服务端请求
try
{
//1.0创建连接套接字,使用ip4协议,流式传输,tcp链接
sokMsg = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2.0获取要链接的服务端节点
//2.1获取网络节点对象
IPAddress address = IPAddress.Parse(txtIp.Text);
IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPort.Text));
//3 向服务端发送链接请求。
sokMsg.Connect(endPoint);
ShowMsg("连接服务器成功!"); //4 开启通信线程
thrMsg = new Thread(ReceiveMsg);
thrMsg.IsBackground = true;
//win7, win8 需要设置客户端通信线程同步设置,才能在接收文件时打开文件选择框
thrMsg.SetApartmentState(ApartmentState.STA);
thrMsg.Start();
}
catch (Exception ex)
{
ShowMsg("连接服务器失败!" + ex.Message);
}
2.0 接收服务端消息
//准备一个消息缓冲区域
byte[] arrMsg = new byte[ * * ];
try
{
while (isReceive)
{
//接收 服务器发来的数据,因为包含了一个标示符,所以内容的真实长度应该-1
int realLength = sokMsg.Receive(arrMsg)-;
switch (arrMsg[])
{
//文本
case :
GetMsg(arrMsg,realLength);
break;
//文件
case :
GetFile(arrMsg,realLength);
break;
default:
ShakeWindow();
break;
}
}
}
catch (Exception ex)
{
sokMsg.Close();
sokMsg = null;
ShowMsg("服务器断开连接!");
}
2.1 接收服务端文本消息 + GetMsg(byte[] arrContent, int realLength)
//获取接收的内容,去掉第一个标示符。
string strMsg = System.Text.Encoding.UTF8.GetString(arrContent, , realLength);
ShowMsg("服务器说:" + strMsg);
2.2 保存文件 + GetFile(byte[] arrContent, int realLength)
SaveFileDialog sfd = new SaveFileDialog();
if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string savaPath = sfd.FileName;
//使用文件流,保存文件
using (System.IO.FileStream fs = new System.IO.FileStream(savaPath, System.IO.FileMode.OpenOrCreate))
{
//将收到的文件数据数组,写入硬盘。
fs.Write(arrContent, , realLength);
}
ShowMsg("保存文件到 【" + savaPath + "】成功!");
}
2.3 抖屏 + void ShakeWindow()
// 保存当前窗体位置
Point oldPoint = this.Location;
for (int i = ; i < ; i++)
{
//随机生成新位置
Point newPoint = new Point(oldPoint.X + ran.Next(), oldPoint.Y + ran.Next());
//将新位置设置给窗体
this.Location = newPoint;
System.Threading.Thread.Sleep();
this.Location = oldPoint;
//休息15毫秒
System.Threading.Thread.Sleep();
}
3.0 客户端发送消息到服务端
string strMsg = txtInput.Text.Trim();
if (strMsg != "")
{
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
try
{
sokMsg.Send(arrMsg);
}
catch (Exception ex)
{
ShowMsg("发送消息失败!" + ex.Message);
}
}
else
{
MessageBox.Show("未输入任何信息!");
}
4.0 展示消息方法 + ShowMsg(string strmsg)
this.txtShow.AppendText(strmsg + "\r\n");
注:小弟不断学习中,还希望园友指导交流。同时也作为个人学习的一个小记录。【好记性不如烂笔头】(*^__^*)
源码分享: http://download.csdn.net/detail/huayuqiang/8270749
WinForm版聊天室复习Socket通信的更多相关文章
- swoole实验版聊天室
“swoole实验版聊天室”是依据一堂swoole培训课内容改编的,结合了bootstrap前端框架.redis数据库.jquery框架等实现基本功能,只是体现了swoole的应用,并不是为了专门写个 ...
- 如何利用WebSocket实现网页版聊天室
花了将近一周的时间终于完成了利用WebSocket完成网页版聊天室这个小demo,期间还走过了一段"看似弯曲"的道路,但是我想其实也不算是弯路吧,因为你走过的路必将留下你的足迹.这 ...
- angular版聊天室|仿微信界面IM聊天|NG2+Node聊天实例
一.项目介绍 运用angular+angular-cli+angular-router+ngrx/store+rxjs+webpack+node+wcPop等技术实现开发的仿微信angular版聊天室 ...
- 从前有个聊天室(socket&threading)
服务器端: # -*- coding: utf-8 -*- import socket, threading con = threading.Condition() HOST = raw_input( ...
- 基于WebSocket实现网页版聊天室
WebSocket ,HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议,其使用简单,应用场景也广泛,不同开发语言都用种类繁多的实现,仅Java体系中,Tomcat,Jetty,Sp ...
- 命令行界面的C/S聊天室应用 (Socket多线程实现)
命令行界面即在Eclipe控制台输入数据. 服务器端包含多个线程,每个Socket对应一条线程,该线程负责读取对应输入流的数据(从客户端发送过来的数据),并将读到的数据向每个Socket输出流发送一遍 ...
- Swoole实现h5版聊天室笔记
声明:该聊天室目前只有一对多,一对一的聊天功能,另外,因为没有使用到mysql,所以还存在比较多的缺陷地方,但知道原理就差不多了,这里主要分享下swoole简易的聊天室制作思路. 开发环境:cento ...
- golang简易版聊天室
功能需求: 创建一个聊天室,实现群聊和单聊的功能,直接输入为群聊,@某人后输入为单聊 效果图: 群聊: 单聊: 服务端: package main import ( "fmt" ...
- 使用node.js实现多人聊天室(socket.io、B/S)
通过B/S架构实现多人聊天,客户端连接服务器,发送信息,服务器接收信息之后返回给客户端. 主要是通过socket.io实现浏览器和服务器之间进行实时,双向和基于事件的通信. socket.io官方文档 ...
随机推荐
- Monkey log分析说明
运行命令: adb shell monkey -p com.crazyhornets.MyHokageAndroidZSY -v -v -v 20 -- throttle 1000 Log: :Mon ...
- jeakins配置邮件通知,附带解决535报错:authentication failed,如果发现测试邮件可以发出,项目构成无法发出邮件,请开启SSL认证,端口号改为(465),qq邮箱、163邮箱通用
535报错解决方案:调用163邮箱服务器来发送邮件,我们需要开启POP3/SMTP服务,这时163邮件会让我们设置客户端授权码,这个授权码替代上面代码部分的passwd即可成功发送邮件 如果设置的邮箱 ...
- How Ironic Inspector Works
翻译官网概述. 操作员将节点注册为Ironic,例如 通过openstack baremetal CLI命令. 电源管理认证应该在这一步提供给Ironic. 如节点状态所述,节点被置于正确的自省状态. ...
- python re 模块小结
前言: 本人环境windows 7 64位,python2.7 re是什么: regular expression缩写,意为正则表达式,是python的众多模块之一 re用途: 从文本中有选择的批量抽 ...
- PAT1024
科学计数法是科学家用来表示很大或很小的数字的一种方便的方法,其满足正则表达式[+-][1-9]"."[0-9]+E[+-][0-9]+,即数字的整数部分只有1位,小数部分至少有1位 ...
- 自己搭建了一个blog
https://svtt.sinaapp.com 利用JustWriting开源项目搭建的,不过还是有些许问题.但是考虑到自己的blog好处多多,暂且用着--有时间或者乐趣来了,自己再用wordpre ...
- zabbix3.4 源码部署
centos6.8 系统 zabbix3.4.9 msyql5.7.22 php5.6.24 在centos6上面源码部署zabbix.3.4 问题比较多,需要花不少时间去解决,建议 ...
- 解决ie8下页面刚出现时候的晃动问题
出现这个问题的原因的页面的高度超过一屏,这个时候需要在开始的时候给 html,body {overflow:scroll;overflow-x:hidden}; 这样就可以解决这个问题了
- Bzoj1974 [Sdoi2010]auction 代码拍卖会
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 375 Solved: 151 Description 随着iPig在P++语言上的造诣日益提升,他形成 ...
- Eclipse与MyEclipse增加主题
下载所需文件 下载地址:https://pan.baidu.com/s/1slq9lFn 如果eclipse和myeclipse里的MyEclipse 10\dropins存在features和plu ...