用c#写的一个多人聊天室的小程序 用的是socket 没用TcpClien以下是代码
客户端:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Threading;
using System.Net.Sockets;
namespace 客户端
{
public partial class Form1 : Form
{
Socket lian;
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 不停接收信息的线程
/// </summary>
void jieshou()
{
while (true)
{
Byte[] buff = new Byte[1000];
try
{
int s = lian.Receive(buff);
if (s == 0)
break;
string str = Encoding.UTF8.GetString(buff, 0, s);
this.Invoke(new Action(() =>
{
this.neirong.AppendText(str + "\r\n");
}));
}
catch { }
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
if (this.button2.Text != "关闭")
{
MessageBox.Show("请先连接");
return;
}
string str = this.liaotian.Text;
string strr = this.name.Text + ":" + str;
byte[] buf = Encoding.UTF8.GetBytes(strr);
try
{
lian.Send(buf);
}
catch
{
//服务器关闭 或者发生异常 断开连接
lian.Close();
MessageBox.Show("请重新连接");
this.button2.Text = "连接";
return;
}
this.liaotian.Clear();
this.neirong.AppendText("我:" + str + "\r\n");
}
/// <summary>
/// 点第一次连接 第二次关闭连接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
if (this.button2.Text == "关闭")
{
lian.Close();
this.button2.Text = "连接";
return;
}
try
{
//创建负责通信的Socket
lian = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint point = new IPEndPoint(ip, 3333);
//获得要连接的远程服务器应用程序的IP地址和端口号
lian.Connect(point);
//开启一个新的线程不停的接收服务端发来的消息
Thread t = new Thread(jieshou);
t.IsBackground = true;
t.Start();
this.button2.Text = "关闭";
}
catch
{
}
}
}
}
服务器端:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace 聊天室
{
public partial class Form1 : Form
{
//所有通信的socket
List<Socket> alllian = new List<Socket>();
/// <summary>
/// 初始化界面 同时开启服务器监听
/// </summary>
public Form1()
{
InitializeComponent();
//防止线程无法访问ui界面的控件 但是隐患太大 推荐使用this.Invoke(new Action(() =>
//Control.CheckForIllegalCrossThreadCalls = false;
try
{
//新建套接字
Socket jianting = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 绑定 可用ip
jianting.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3333));
//同时连接的最大上限 多了排队
jianting.Listen(2);
//创建后台线程一直等待连接连接
Thread t = new Thread(lianjie);
t.IsBackground = true;
t.Start(jianting);
}
catch { }
}
/// <summary>
/// 一直监听 线程只能传object
/// </summary>
/// <param name="jianting"></param>
void lianjie(object jianting)
{
while (true)
{
Socket jt = (Socket)jianting;
//返回通信的套接字
try
{
Socket l = jt.Accept();
//持续接收消息
alllian.Add(l);
Thread t = new Thread(jieshou);
t.IsBackground = true;
t.Start(l);
}
catch { }
}
}
/// <summary>
/// 持续接收发来的消息
/// </summary>
/// <param name="l"></param>
void jieshou(object ll)
{
Socket l = (Socket)ll;
while (true)
{
Byte[] buff = new Byte[1000];
try
{
int s = l.Receive(buff);
if (s == 0)
{
alllian.Remove(l);
break;
}
//显示内容
string str = Encoding.UTF8.GetString(buff, 0, s);
this.Invoke(new Action(() =>
{
this.neirong.AppendText(str + "\r\n");
}));
buff = Encoding.UTF8.GetBytes(str);
//发送给别人 直接用发来bytes转发的话有一些问题 长度不匹配
foreach (Socket all in alllian)
{
if (all != l)
try
{
all.Send(buff);
}
catch
{
continue;
}
}
}
catch { }
}
}
/// <summary>
/// 发送消息给所有人
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void fasong_Click(object sender, EventArgs e)
{
bool r=false;
string str = this.liaotian.Text;
string strr = "房主:" + str;
byte[] buf = Encoding.UTF8.GetBytes(strr);
//给所有人发送消息 不要用foreach 报错 集合已修改 无法进行枚举操作
for (int i = 0; i < alllian.Count;i++ )
{
try
{
alllian[i].Send(buf);
r = true;
}
catch
{
alllian.Remove(alllian[i]);
i--;
continue;
}
}
//如果可以发送出去消息 则还有人连接
if (r)
{
this.liaotian.Clear();
this.neirong.AppendText("我:"+str + "\r\n");
}
else
{
alllian.Clear();
MessageBox.Show("无人聊天");
}
}
}
}
写完这个小程序有几点需要总结的:
1.socket、线程、集合的使用
socket:多练练就会用了 网上资料不少
线程:传参数只能是object 之后可强转 可 as(比较安全) winform程序线程想访问控件最好使用
this.Invoke(new Action(() => 这类的操作
而 Control.CheckForIllegalCrossThreadCalls = false; 只是拆了东墙补
集合:在循环里进行操作勿用foreach 比如集合在删除中间元素的时候后面的元素会往前移动 for删除完元素之后 i--退一位即可
2.通信的双方不能明确的知晓对方是否断开连接
服务器:客户端断开连接,服务器发送消息的时候会报异常
客户端:服务器关闭 ,客户端发送消息会报异常
捕捉这个异常 则证明对方断开连接 做相应的处理(这样不太正规,万一不是断开连接引起的异常就不好了) 这个我用着还可以
3.关于检测对方是否断开连接的方法
百度有不少 心跳包是主流 我的方法是偏方 - -