https://github.com/yinyoupoet/chatRoomTest
代码、可运行程序、详细设计文档、使用说明等都在里面啦
然后再上图你们感受下:
客户端本来想做个在线列表的,就在那空出来的那块,因为时间原因做出了半成品但是还有bug,就没上传了。
下面讲一下设计思路。(如果是新手不懂下面的术语也没关系,我在代码的注释里会写的很清楚,可以先大致了解一下)
多人聊天用p2p不太好,就是用c/s模式了。服务器设置好监听套接字,绑定监听的IP地址和端口,然后一直监听该端口就行了。
客户端首先需要连接上服务器,所以设置的IP和端口(要连接的服务器的IP和端口)必须是得和服务器一样的,不然肯定连接不上。
然后客户端连接上后,服务器将其用户名与客户端的套接字绑定好,然后保存在一个Dictionary里面。以后客户端发送的消息由服务器直接向该数组中所有套接字转发即可。
代码有点长,而且是winform,所以不好放代码,本程序是用vs2013写的,必须装了.NET Fragment 4.5以上框架的windows 64位系统才能运行程序。
不过把代码放在最后,供github抽风进不去时看看,注释很详细,我也是从完全不懂抄别人的慢慢找资料学会的。只放了后台代码,界面的不好放这了,不过一般都可根据命名和截图猜得出。建议还是直接去github里面下载下来,看的更舒服。
服务器端:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Threading;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Security.Cryptography;
namespace chatRoomServer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ipadr = IPAddress.Loopback;
}
public static Dictionary<String, Socket> clientList = null;
Socket serverSocket = null;
Boolean isListen = true;
Thread thStartListen;
IPAddress ipadr;
IPEndPoint endPoint;
private void btnStart_Click(object sender, EventArgs e)
{
if (serverSocket == null)
{
try
{
isListen = true;
clientList = new Dictionary<string, Socket>();
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
endPoint = new IPEndPoint(ipadr, 8080);
try
{
serverSocket.Bind(endPoint);
serverSocket.Listen(100);
thStartListen = new Thread(StartListen);
thStartListen.IsBackground = true;
thStartListen.Start();
txtMsg.BeginInvoke(new Action(() =>
{
txtMsg.Text += "服务启动成功...\r\n";
}));
}
catch (Exception eg)
{
MessageBox.Show("输入的IP地址无效,请重新输入!");
txtMsg.BeginInvoke(new Action(() =>
{
txtMsg.Text += "服务启动失败...\r\n";
}));
if (serverSocket != null)
{
serverSocket.Close();
thStartListen.Abort();
BroadCast.PushMessage("Server has closed", "", false, clientList);
foreach (var socket in clientList.Values)
{
socket.Close();
}
clientList.Clear();
serverSocket = null;
isListen = false;
}
}
}
catch(SocketException ex)
{
MessageBox.Show(ex.ToString());
}
}
}
private void StartListen()
{
isListen = true;
Socket clientSocket = default(Socket);
while (isListen)
{
try
{
if (serverSocket == null)
{
return;
}
clientSocket = serverSocket.Accept();
}
catch (SocketException e)
{
File.AppendAllText("E:\\Exception.txt", e.ToString() + "\r\nStartListen\r\n" + DateTime.Now.ToString() + "\r\n");
}
Byte[] bytesFrom = new Byte[4096];
String dataFromClient = null;
if (clientSocket != null && clientSocket.Connected)
{
try
{
Int32 len = clientSocket.Receive(bytesFrom);
if (len > -1)
{
String tmp = Encoding.UTF8.GetString(bytesFrom, 0, len);
dataFromClient = tmp;
Int32 sublen = dataFromClient.LastIndexOf("$");
if (sublen > -1)
{
dataFromClient = dataFromClient.Substring(0, sublen);
if (!clientList.ContainsKey(dataFromClient))
{
clientList.Add(dataFromClient, clientSocket);
BroadCast.PushMessage(dataFromClient + "Joined", dataFromClient, false, clientList);
HandleClient client = new HandleClient(txtMsg);
client.StartClient(clientSocket, dataFromClient, clientList);
txtMsg.BeginInvoke(new Action(() =>
{
txtMsg.Text += dataFromClient + "连接上了服务器\r" + DateTime.Now + "\r\n";
}));
}
else
{
clientSocket.Send(Encoding.UTF8.GetBytes("#" + dataFromClient + "#"));
}
}
}
}
catch (Exception ep)
{
File.AppendAllText("E:\\Exception.txt", ep.ToString() + "\r\n\t\t" + DateTime.Now.ToString() + "\r\n");
}
}
}
}
private void btnStop_Click(object sender, EventArgs e)
{
if (serverSocket != null)
{
serverSocket.Close();
thStartListen.Abort();
BroadCast.PushMessage("Server has closed", "", false, clientList);
foreach (var socket in clientList.Values)
{
socket.Close();
}
clientList.Clear();
serverSocket = null;
isListen = false;
txtMsg.Text += "服务停止,断开所有客户端连接\t"+DateTime.Now.ToString()+"\r\n";
}
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
clientList = new Dictionary<string, Socket>();
serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
endPoint = new IPEndPoint(ipadr,8080);
serverSocket.Bind(endPoint);
serverSocket.Listen(100);
thStartListen = new Thread(StartListen);
thStartListen.IsBackground = true;
thStartListen.Start();
txtMsg.BeginInvoke(new Action(() =>
{
txtMsg.Text += "服务启动成功...\r\n";
}
));
labIPnow.BeginInvoke(new Action(() => {
labIPnow.Text = endPoint.Address.ToString();
}));
}
catch (SocketException ep)
{
MessageBox.Show(ep.ToString());
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (serverSocket != null)
{
BroadCast.PushMessage("Server has closed", "", false, clientList);
foreach (var socket in clientList.Values)
{
socket.Close();
}
clientList.Clear();
serverSocket.Close();
serverSocket = null;
isListen = false;
txtMsg.Text += "服务停止\r\n";
}
}
private void btnResetIp_Click(object sender, EventArgs e)
{
if (!String.IsNullOrWhiteSpace(txtIP.Text.ToString().Trim()))
{
try
{
ipadr = IPAddress.Parse(txtIP.Text.ToString().Trim());
btnStop_Click(sender, e);
txtMsg.BeginInvoke(new Action(() =>
{
txtMsg.Text += "服务器重启中,请稍候...\r\n";
}));
btnStart_Click(sender, e);
labIPnow.BeginInvoke(new Action(() =>
{
labIPnow.Text = endPoint.Address.ToString();
}));
}
catch (Exception ep)
{
MessageBox.Show("请输入正确的IP后重试");
}
}
else
{
MessageBox.Show("请输入重置后的IP地址后重试!");
}
}
private void btnRcv_Click(object sender, EventArgs e)
{
if (ipadr == IPAddress.Loopback)
{
MessageBox.Show("当前已经处于默认状态,无需修改");
}
else
{
ipadr = IPAddress.Loopback;
btnStop_Click(sender, e);
txtMsg.BeginInvoke(new Action(() =>
{
txtMsg.Text += "服务器重启中,请稍候...\r\n";
}));
btnStart_Click(sender, e);
labIPnow.BeginInvoke(new Action(() =>
{
labIPnow.Text = endPoint.Address.ToString();
}));
}
}
private void btnClear_Click(object sender, EventArgs e)
{
txtMsg.BeginInvoke(new Action(() =>
{
txtMsg.Text = "-----------已清屏-----------\r\n";
}));
}
}
public class HandleClient
{
Socket clientSocket;
String clNo;
Dictionary<String, Socket> clientList = new Dictionary<string, Socket>();
TextBox txtMsg;
public HandleClient() { }
public HandleClient(TextBox tb)
{
txtMsg = tb;
}
public void StartClient(Socket inClientSocket, String clientNo, Dictionary<String, Socket> cList)
{
clientSocket = inClientSocket;
clNo = clientNo;
clientList = cList;
Thread th = new Thread(Chat);
th.IsBackground = true;
th.Start();
}
private void Chat()
{
Byte[] bytesFromClient = new Byte[4096];
String dataFromClient;
String msgTemp = null;
Byte[] bytesSend = new Byte[4096];
Boolean isListen = true;
while (isListen)
{
try
{
if (clientSocket == null || !clientSocket.Connected)
{
return;
}
if (clientSocket.Available > 0)
{
Int32 len = clientSocket.Receive(bytesFromClient);
if (len > -1)
{
dataFromClient = Encoding.UTF8.GetString(bytesFromClient, 0, len);
if (!String.IsNullOrWhiteSpace(dataFromClient))
{
dataFromClient = dataFromClient.Substring(0, dataFromClient.LastIndexOf("$"));
if (!String.IsNullOrWhiteSpace(dataFromClient))
{
BroadCast.PushMessage(dataFromClient, clNo, true, clientList);
msgTemp = clNo + ":" + dataFromClient + "\t\t" + DateTime.Now.ToString();
String newMsg = msgTemp;
File.AppendAllText("E:\\MessageRecords.txt", newMsg + "\r\n", Encoding.UTF8);
}
else
{
isListen = false;
clientList.Remove(clNo);
txtMsg.BeginInvoke(new Action(() =>
{
txtMsg.Text += clNo+ "已断开与服务器连接\r" + DateTime.Now + "\r\n";
}));
BroadCast.PushMessage(clNo + "已下线\r","",false,clientList);
clientSocket.Close();
clientSocket = null;
}
}
}
}
}
catch (Exception e)
{
isListen = false;
clientList.Remove(clNo);
clientSocket.Close();
clientSocket = null;
File.AppendAllText("E:\\Exception.txt",e.ToString()+"\r\nChat\r\n"+DateTime.Now.ToString()+"\r\n");
}
}
}
}
public class BroadCast
{
public static void PushMessage(String msg, String uName, Boolean flag, Dictionary<String, Socket> clientList)
{
foreach (var item in clientList)
{
Socket brdcastSocket = (Socket)item.Value;
String msgTemp = null;
Byte[] castBytes = new Byte[4096];
if (flag == true)
{
msgTemp = uName + ":" + msg + "\t\t" + DateTime.Now.ToString();
castBytes = Encoding.UTF8.GetBytes(msgTemp);
}
else
{
msgTemp = msg + "\t\t" + DateTime.Now.ToString();
castBytes = Encoding.UTF8.GetBytes(msgTemp);
}
try
{
brdcastSocket.Send(castBytes);
}
catch (Exception e)
{
brdcastSocket.Close();
brdcastSocket = null;
File.AppendAllText("E:\\Exception.txt",e.ToString()+"\r\nPushMessage\r\n"+DateTime.Now.ToString()+"\r\n");
continue;
}
}
}
}
}
客户端:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace chatRoomClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ipadr = IPAddress.Loopback;
}
Socket clientSocket = null;
static Boolean isListen = true;
Thread thDataFromServer;
IPAddress ipadr;
private void btnSend_Click(object sender, EventArgs e)
{
SendMessage();
}
private void SendMessage()
{
if (String.IsNullOrWhiteSpace(txtSendMsg.Text.Trim()))
{
MessageBox.Show("发送内容不能为空哦~");
return;
}
if (clientSocket != null && clientSocket.Connected)
{
Byte[] bytesSend = Encoding.UTF8.GetBytes(txtSendMsg.Text + "$");
clientSocket.Send(bytesSend);
txtSendMsg.Text = "";
}
else
{
MessageBox.Show("未连接服务器或者服务器已停止,请联系管理员~");
return;
}
}
private void btnConnect_Click(object sender, EventArgs e)
{
if (String.IsNullOrWhiteSpace(txtName.Text.Trim()))
{
MessageBox.Show("请设置个用户名哦亲");
return;
}
if (txtName.Text.Length>=17 && txtName.Text.ToString().Trim().Substring(0, 17).Equals("Server has closed"))
{
MessageBox.Show("该用户名中包含敏感词,请更换用户名后重试");
return;
}
if (clientSocket == null || !clientSocket.Connected)
{
try
{
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
if (!String.IsNullOrWhiteSpace(txtIP.Text.ToString().Trim()))
{
try
{
ipadr = IPAddress.Parse(txtIP.Text.ToString().Trim());
}
catch
{
MessageBox.Show("请输入正确的IP后重试");
return;
}
}
else
{
ipadr = IPAddress.Loopback;
}
clientSocket.BeginConnect(ipadr, 8080, (args) =>
{
if (args.IsCompleted)
{
Byte[] bytesSend = new Byte[4096];
txtName.BeginInvoke(new Action(() =>
{
bytesSend = Encoding.UTF8.GetBytes(txtName.Text.Trim() + "$");
if (clientSocket != null && clientSocket.Connected)
{
clientSocket.Send(bytesSend);
txtName.Enabled = false;
txtSendMsg.Focus();
thDataFromServer = new Thread(DataFromServer);
thDataFromServer.IsBackground = true;
thDataFromServer.Start();
}
else
{
MessageBox.Show("服务器已关闭");
}
}));
txtIP.BeginInvoke(new Action(() =>
{
if (clientSocket != null && clientSocket.Connected)
{
txtIP.Enabled = false;
}
}));
}
},null);
}
catch (SocketException ex)
{
MessageBox.Show(ex.ToString());
}
}
else
{
MessageBox.Show("你已经连接上服务器了");
}
}
private void ShowMsg(String msg)
{
txtReceiveMsg.BeginInvoke(new Action(() =>
{
txtReceiveMsg.Text += Environment.NewLine + msg;
}));
}
private void DataFromServer()
{
ShowMsg("Connected to the Chat Server...");
isListen = true;
try
{
while (isListen)
{
Byte[] bytesFrom = new Byte[4096];
Int32 len = clientSocket.Receive(bytesFrom);
String dataFromClient = Encoding.UTF8.GetString(bytesFrom, 0, len);
if (!String.IsNullOrWhiteSpace(dataFromClient))
{
if (dataFromClient.ToString().Length >=17 && dataFromClient.ToString().Substring(0, 17).Equals("Server has closed"))
{
clientSocket.Close();
clientSocket = null;
txtReceiveMsg.BeginInvoke(new Action(() =>
{
txtReceiveMsg.Text += Environment.NewLine + "服务器已关闭";
}));
txtName.BeginInvoke(new Action(() =>
{
txtName.Enabled = true;
}));
txtIP.BeginInvoke(new Action(() =>
{
txtIP.Enabled = true;
}));
thDataFromServer.Abort();
return;
}
if (dataFromClient.StartsWith("#") && dataFromClient.EndsWith("#"))
{
String userName = dataFromClient.Substring(1, dataFromClient.Length - 2);
this.BeginInvoke(new Action(() =>
{
MessageBox.Show("用户名:[" + userName + "]已经存在,请尝试其他用户名并重试");
}));
isListen = false;
txtName.BeginInvoke(new Action(() =>
{
txtName.Enabled = true;
clientSocket.Send(Encoding.UTF8.GetBytes("$"));
clientSocket.Close();
clientSocket = null;
}));
txtIP.BeginInvoke(new Action(() =>
{
txtIP.Enabled = true;
}));
}
else
{
ShowMsg(dataFromClient);
}
}
}
}
catch (SocketException ex)
{
isListen = false;
if (clientSocket != null && clientSocket.Connected)
{
clientSocket.Send(Encoding.UTF8.GetBytes("$"));
MessageBox.Show(ex.ToString());
}
}
}
private void Form1_Activated(object sender, EventArgs e)
{
txtSendMsg.Focus();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (clientSocket != null && clientSocket.Connected)
{
clientSocket.Send(Encoding.UTF8.GetBytes("$"));
}
}
private void btnBreak_Click(object sender, EventArgs e)
{
if (clientSocket != null && clientSocket.Connected)
{
thDataFromServer.Abort();
clientSocket.Send(Encoding.UTF8.GetBytes("$"));
clientSocket.Close();
clientSocket = null;
txtReceiveMsg.BeginInvoke(new Action(() =>
{
txtReceiveMsg.Text += Environment.NewLine + "已断开与服务器的连接";
}));
txtName.BeginInvoke(new Action(() =>
{
txtName.Enabled = true;
}));
txtIP.BeginInvoke(new Action(() =>
{
txtIP.Enabled = true;
}));
}
}
}
}
欢迎大家加入QQ群一起交流讨论,「吟游」程序人生——YinyouPoet
请发表评论