主要就是多线程与Socket的知识,注释写的还算详细,还是实际点帖全部代码吧。
Client:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;
namespace TestClient
{
public partial class Chat : Form
{
//以下是客户端到服务器端的消息开头
const string LOGININ = "10" ;//请求登陆的消息|||消息形式:10+自己的用户名
const string LOGINOUT = "11" ;//请求登出的消息|||消息形式:11+自己的用户名
const string GETULIST = "12" ;//请求获得在线用户列表|||消息形式:12+自己的用户名
const string P2PCONN = "13" ;//请求P2P连接的消息|||消息形式:13+自己的用户名+对方的用户名
const string HOLDLINE = "14";//保持连接.|||消息开式:14+自己的用户名
const string LOGINADD = "15";//新用户登录
const string LOGINREMOVE = "16";//用户登出
//以下是服务器到客户端的消息开头
const string HVUSER = "20";//用户名已存在
const string GETUSER = "21";//在线用户列表|||消息格式:21+用户名+EP
const string MAKHOLD = "22";//打洞命令|||消息格式:22+IP
const string LOGINOK = "23";//登陆成功
const string SERVCLS = "24";//服务器关闭
const string MSGEND = "25";//'消息结束
const string CHATMSGALL = "26";//发送给所有人
//以下是客户端到客户端的消息开头
const string HOLDOK = "30" ;//打洞成功
const string CHATMSG = "31" ;//聊天消息
const string CHTMSGEND = "32" ;//聊天消息发送成功
private string userName;
public string UserName
{
private get { return userName; }
set { userName = value; }
}
private IPEndPoint serverEP;
public IPEndPoint ServerEP
{
set
{
serverEP = value;
}
private get
{
return serverEP;
}
}
Socket clientSocket;
public Socket ClientSocket
{
private get { return clientSocket; }
set { clientSocket = value; }
}
int getUrecCount;
//和服务器保持连接连接时用到的byte数组
byte[] holdBytes;
List<string> OLUserName;
List<IPEndPoint> OLUserEP;
//为保持在线状态弄得
TimerCallback timerDelegate;
bool msgSendEnd = false;//消息是否发送成功,若发送成功,则会返回结束消息
private ManualResetEvent getUDone;//用来阻塞请求好友名单的线程,等待接收好友名单
private ManualResetEvent chatDone;//用来阻塞发送聊天消息时的线程
private ManualResetEvent holdDone;//用来阻塞打洞时的线程
private ManualResetEvent sendDone ;//用来阴塞发送消息的线程.等待收到回送的确认消息
//监听的线程
Thread thListen;
delegate void myMethodDelegate(ref byte[] myInData);//登陆时用的事件
delegate void TextChangedMelegate(string text);//给textbox1赋值用到的委托
delegate void ComboboxBindDelegate();//用户绑定combobox的委托
static bool testHold = false;
static bool testChat = false;
//用户列表的数据源
DataTable dt;
//上一条消息
string lastMsg = "";
public Chat()
{
InitializeComponent();
}
private void Chat_Load(object sender, EventArgs e)
{
Login login = new Login();
login.StartPosition = FormStartPosition.CenterParent;
login.Tag = this;
login.ShowDialog();
thListen = new Thread(new ThreadStart(this.Listen));
thListen.IsBackground = true;
thListen.Start();
}
public void LoadInit()
{
//timerDelegate = new TimerCallback(Holdonline);
//holdBytes = Encoding.Unicode.GetBytes(HOLDLINE + UserName);
////登陆成功后.用一个timer,每隔50秒向服务器发送消息,保持在线状态跟在主机注册的端口
//System.Threading.Timer timer = new System.Threading.Timer(timerDelegate, null, 10000, 10000);
//登陆成功后.用一个timer,每隔50秒向服务器发送消息,保持在线状态跟在主机注册的端口
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Tick += new EventHandler(Holdonline);
timer.Interval = 50000;
timer.Start();
//请求在线名单
this.Text = "正在获取在线名单,请稍后....";
bool getUbool = false;
//while (getUbool == false)
//{
// //getUbool = GetU();
// //if (getUbool == false)
// //{
// // if (MessageBox.Show("是否重试?", "是否重试", MessageBoxButtons.YesNo) == DialogResult.Yes)
// // {
// // getUbool = false;
// // }
// // else
// // {
// // getUbool = true;
// // }
// //}
//}
}
private bool GetU()
{
getUDone = new ManualResetEvent(false);
byte[] getUbytes = Encoding.Unicode.GetBytes(GETULIST);
ClientSocket.SendTo(getUbytes, ServerEP);
byte[] data = new byte[4056];
string comStr = Encoding.Unicode.GetString(data, 0, 4);
myMethodDelegate GUrecv = new myMethodDelegate(RecvGetU);
GUrecv.BeginInvoke(ref data, null, null);
getUDone.WaitOne(30000, true);
string recvStr = Encoding.Unicode.GetString(data, 0, 4);
if (recvStr == comStr)
{
MessageBox.Show("服务器超时.或取好友名单失败!!");
return false;
}
string test = Encoding.Unicode.GetString(data);
if (Encoding.Unicode.GetString(data, 0, 4) == GETUSER)
{
GetUserList(data, getUrecCount);
this.comboBox1.Invoke(new ComboboxBindDelegate(ShowUserList));
//ShowUserList();
return true;
}
else
{
MessageBox.Show("服务器未知错误,获取在线名单失败!!");
return false;
}
}
/// <summary>
/// 显示在线用户
/// </summary>
private void ShowUserList()
{
lock (OLUserName)
{
dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("IP");
DataRow dr1 = dt.NewRow();
dr1["Name"] = "所有人";
dt.Rows.Add(dr1);
this.comboBox1.DataSource = dt;
this.comboBox1.ValueMember = "IP";
this.comboBox1.DisplayMember = "Name";
for (int i = 0; i < OLUserName.Count; i++)
{
if (OLUserName[i] != "")
{
DataRow dr = dt.NewRow();
dr["Name"] = OLUserName[i];
dr["IP"] = OLUserEP[i];
dt.Rows.Add(dr);
}
}
}
}
/// <summary>
/// 处理收到的在线用户信息
/// </summary>
/// <param name="userInfobytes"></param>
/// <param name="reccount"></param>
private void GetUserList(byte[] userInfobytes, int reccount)
{
string ustr = Encoding.Unicode.GetString(userInfobytes, 4, reccount - 4);
string[] splitStr = null;
splitStr = ustr.Split('|');
string[] IPEPSplit = null;
OLUserName = new List<string>();
OLUserEP = new List<IPEndPoint>();
int i = 0;
for(int k=0;k<splitStr.Length -2;k = k+2)
{
OLUserName.Add(splitStr[k]);
IPEPSplit = splitStr[k + 1].Split(':');
OLUserEP.Add(new IPEndPoint(IPAddress.Parse(IPEPSplit[0]), Convert.ToInt32(IPEPSplit[1])));
IPEPSplit = null;
i++;
}
}
//用保持在线状态的函数
private void Holdonline(object state,EventArgs e)
{
holdBytes = Encoding.Unicode.GetBytes(HOLDLINE + UserName);
ClientSocket.SendTo(holdBytes, ServerEP);
}
//登陆时用来异步的接收服务器发送的消息
void RecvGetU(ref byte[] inData)
{
getUrecCount = ClientSocket.Receive(inData);
getUDone.Set();
}
/// <summary>
/// 发送
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonSend_Click(object sender, EventArgs e)
{
if (this.textBox2.Text.Trim() == "")
return;
else
{
SendChatMsg(this.comboBox1.Text, this.textBox2.Text);
}
}
/// <summary>
/// 发送聊天消息
/// </summary>
/// <param name="remoteUser"></param>
/// <param name="chatMsgStr"></param>
private void SendChatMsg(string remoteUser, string chatMsgStr)
{
if (lastMsg.Trim() == chatMsgStr.Trim())
{
MessageBox.Show("看你傻样,还想刷屏!");
return;
}
else
lastMsg = chatMsgStr;
IPEndPoint remoteUEP = null;
if (remoteUser == "所有人")
{
for(int i=0;i<OLUserName.Count;i++)
{
if (OLUserName[i].ToUpper().Trim() == this.UserName.ToUpper().Trim())
continue;
byte[] msgbytes = Encoding.Unicode.GetBytes(CHATMSGALL + UserName + "|" + chatMsgStr);
string test = OLUserEP[i].Address.ToString();
string port = OLUserEP[i].Port.ToString();
ClientSocket.SendTo(msgbytes, OLUserEP[i]);
}
this.textbox.AppendText(this.UserName + " to " + this.comboBox1.Text + ":" + chatMsgStr + "\n");
this.textbox.Focus();
Application.DoEvents();
this.textBox2.Focus();
}
else
{
for (int i = 0; i < OLUserName.Count; i++)
{
if (remoteUser == OLUserName[i])
remoteUEP = OLUserEP[i];
}
byte[] msgbytes = Encoding.Unicode.GetBytes(CHATMSG + userName + "|" + chatMsgStr);
byte[] holdbytes = Encoding.Unicode.GetBytes(P2PCONN + userName + "|" + remoteUser);
chatDone = new ManualResetEvent(false);
string test = remoteUEP.Address.ToString();
string port = remoteUEP.Port.ToString();
ClientSocket.SendTo(msgbytes, remoteUEP);
chatDone.WaitOne(1000, true);
this.textbox.AppendText(this.UserName + " to " + this.comboBox1.Text + ":" + chatMsgStr + "\n");
//this.textbox.Focus();
Application.DoEvents();
//this.textBox2.Focus();
if (testChat)
{
testChat = false;
return;
}
testHold = false;
while (testHold == false)
{
//开始打洞
holdDone = new ManualResetEvent(false);
ClientSocket.SendTo(holdbytes, remoteUEP);
ClientSocket.SendTo(holdbytes, ServerEP);
holdDone.WaitOne(1000, true);
if (testHold == false)
{
//打洞超时
testHold = true;
}
else
{
//打洞成功
}
}
while (testChat == false)
{
//打洞成功准备发送
chatDone = new ManualResetEvent(false);
ClientSocket.SendTo(msgbytes, remoteUEP);
chatDone.WaitOne(1000, true);
if (testChat == false)
{
//发送超时
testChat = true;
}
else
{
//发送成功
testChat = true;
}
}
}
this.textBox2.Text = "";
testHold = false;
testChat = false;
}
/// <summary>
/// 客户程序监听的函数
/// </summary>
private void Listen()
{
while (true)
{
try
{
int recv = 0;//收到的字节数
byte[] data = new byte[1024];//缓冲区大小
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;
recv = ClientSocket.ReceiveFrom(data, ref tempRemoteEP);
//获得消息头的内容
string msgHead = Encoding.Unicode.GetString(data, 0, 4);
switch (msgHead)
{
case MSGEND:
msgSendEnd = true;
sendDone.Set();
break;
case LOGININ:
AddOnLine(data, recv);
break;
case LOGINOUT:
RemoveOnLine(data, recv);
break;
case MAKHOLD:
MakeHold(data, recv);
break;
case CHATMSG:
ShowChatMsg(data, recv);
break;
case CHATMSGALL:
ShowChatMsgAll(data, recv);
break;
case HOLDOK:
testHold = true;
holdDone.Set();
break;
case CHTMSGEND:
testChat = true;
chatDone.Set();
break;
case LOGINADD:
this.GetU();
break;
case LOGINREMOVE:
this.GetU();
break;
}
}
catch
{ }
}
}
/// <summary>
/// 处理聊天消息
/// </summary>
/// <param name="data"></param>
/// <param name="recv"></param>
private void ShowChatMsgAll(byte[] data, int recv)
{
string msgStr = Encoding.Unicode.GetString(data, 4, recv - 4);
string[] splitStr = msgStr.Split('|');
string fromUname = splitStr[0];
string msg = splitStr[1];
this.textbox.Invoke(new TextChangedMelegate(SetTextBox), fromUname + " to 所有人:" + msg);
}
/// <summary>
/// 处理聊天消息
/// </summary>
/// <param name="data"></param>
/// <param name="recv"></param>
private void ShowChatMsg(byte[] data, int recv)
{
string msgStr = Encoding.Unicode.GetString(data, 4, recv-4);
string[] splitStr = msgStr.Split('|');
string fromUname = splitStr[0];
string msg = splitStr[1];
this.textbox.Invoke(new TextChangedMelegate(SetTextBox), fromUname + " to " + userName + ":" + msg);
//this.textBox1.Text += fromUname + " to " + userName + ":" + msg + "\n";
byte[] tempbytes = Encoding.Unicode.GetBytes(CHTMSGEND);
for (int i = 0; i < OLUserName.Count; i++)
{
if (OLUserName[i].ToUpper() == fromUname.ToUpper())
{
ClientSocket.SendTo(tempbytes, OLUserEP[i]);
}
}
}
private void SetTextBox(string text)
{
this.textbox.SelectionColor = Color.Blue;
this.textbox.AppendText(text + "\n");
//this.textbox.Focus();
Application.DoEvents();
//this.textBox2.Focus();
}
/// <summary>
/// 处理打洞函数
/// </summary>
/// <param name="data"></param>
/// <param name="recv"></param>
private void MakeHold(byte[] indata, int recvcount)
{
string makholdstr = Encoding.Unicode.GetString(indata, 4, recvcount);
string[] ipepstr = makholdstr.Split(':');
IPEndPoint holdEP = new IPEndPoint(IPAddress.Parse(ipepstr[0]), Convert.ToInt32(ipepstr[1]));
byte[] holdbytes = Encoding.Unicode.GetBytes(HOLDOK + UserName);
//回送打洞消息
ClientSocket.SendTo(holdbytes, holdEP);
}
/// <summary>
/// 处理用户上线的函数
/// </summary>
private void AddOnLine(byte[] inData, int recvCount)
{
string inStr = Encoding.Unicode.GetString(inData, 4, recvCount - 4);
string[] userinfo = inStr.Split('|');
string[] strUserEP = userinfo[1].Split(':');
int count = OLUserName.Count - 1;
//for (int i = 0; i < count; i++)
//{
// if (OLUserName[i] == "")
// {
OLUserName.Add(userinfo[0]);
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(strUserEP[0]), Convert.ToInt32(strUserEP[1]));
OLUserEP.Add(ip);
lock (dt)
{
DataRow dr = this.dt.NewRow();
dr["Name"] = userinfo[0];
dr["IP"] = ip;
dt.Rows.Add(dr);
}
this.textbox.Invoke(new TextChangedMelegate(SetTextBox), userinfo[0] + "上线了!");
//this.textBox1.Text += userinfo[0] + "上线了!\n";
//break;
// }
//}
}
/// <summary>
/// 处理用户下线的函数
/// </summary>
private void RemoveOnLine(byte[] inData,int recvCount)
{
string offUname = Encoding.Unicode.GetString(inData,4,recvCount-4);
for(int i = 0; i< OLUserName.Count-1;i++)
{
if(OLUserName[i] == offUname)
{
OLUserName.Remove(offUname);
OLUserEP.RemoveAt(i);
lock (dt)
{
foreach (DataRow dr in dt.Rows)
{
if (dr["name"].ToString().Trim() == offUname.Trim())
{
dt.Rows.Remove(dr);
break;
}
}
}
this.textbox.Invoke(new TextChangedMelegate(SetTextBox), offUname + "下线了!");
//this.textBox1.Text += offUname + "下线了!\n";
break;
}
}
}
private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == '\r')
this.buttonSend_Click(null, null);
}
private void textbox_TextChanged(object sender, EventArgs e)
{
////让文本框获取焦点
//this.textbox.Focus();
////设置光标的位置到文本尾
//this.textbox.Select(this.richTextBoxInfo.TextLength, 0);
////滚动到控件光标处
//this.textbox.ScrollToCaret();
}
private void Chat_FormClosed(object sender, FormClosedEventArgs e)
{
this.thListen.Abort();
string loginOutStr = LOGINOUT + UserName;
byte[] sendBytes = Encoding.Unicode.GetBytes(loginOutStr);
ClientSocket.SendTo(sendBytes,ServerEP);
}
}
}
namespace TestClient
{
partial class Chat
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.buttonSend = new System.Windows.Forms.Button();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.textbox = new System.Windows.Forms.RichTextBox();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
this.panel1.Controls.Add(this.buttonSend);
this.panel1.Controls.Add(this.textBox2);
this.panel1.Controls.Add(this.label1);
this.panel1.Controls.Add(this.comboBox1);
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel1.Location = new System.Drawing.Point(0, 382);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(557, 59);
this.panel1.TabIndex = 0;
//
// buttonSend
//
this.buttonSend.Location = new System.Drawing.Point(470, 29);
this.buttonSend.Name = "buttonSend";
this.buttonSend.Size = new System.Drawing.Size(75, 23);
this.buttonSend.TabIndex = 1;
this.buttonSend.Text = "发送";
this.buttonSend.UseVisualStyleBackColor = true;
this.buttonSend.Click += new System.EventHandler(this.buttonSend_Click);
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(3, 29);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(443, 21);
this.textBox2.TabIndex = 0;
this.textBox2.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.textBox2_KeyPress);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(3, 6);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(53, 12);
this.label1.TabIndex = 2;
this.label1.Text = "发送给:";
//
// comboBox1
//
this.comboBox1.FormattingEnabled = true;
this.comboBox1.Location = new System.Drawing.Point(62, 3);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(121, 20);
this.comboBox1.TabIndex = 3;
//
// textbox
//
this.textbox.BackColor = System.Drawing.SystemColors.ActiveCaptionText;
this.textbox.Dock = System.Windows.Forms.DockStyle.Fill;
this.textbox.Location = new System.Drawing.Point(0, 0);
this.textbox.Name = "textbox";
this.textbox.ReadOnly = true;
this.textbox.Size = new System.Drawing.Size(557, 382);
this.textbox.TabIndex = 1;
this.textbox.Text = "";
this.textbox.TextChanged += new System.EventHandler(this.textbox_TextChanged);
//
// Chat
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(557, 441);
this.Controls.Add(this.textbox);
this.Controls.Add(this.panel1);
this.Name = "Chat";
this.Text = "Chat";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Chat_FormClosed);
this.Load += new System.EventHandler(this.Chat_Load);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.Button buttonSend;
private System.Windows.Forms.RichTextBox textbox;
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace TestClient
{
public partial class Login : Form
{
//以下是客户端到服务器端的消息开头
const string LOGININ = "10";//请求登陆的消息|||消息形式:10+自己的用户名
const string LOGINOK = "23";//登陆成功
//以下是服务器到客户端的消息开头
const string HVUSER = "20";//用户名已存在
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //客户端套节字的定义
IPEndPoint serverEP;//服务器的IPEP
delegate void myMethodDelegate(ref byte[] myInData);//登陆时用的事件
private ManualResetEvent receiveDone;//在登陆时用来阻塞线程,等待收到数据
bool inPutOk = false;
public Login()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
serverEP = new IPEndPoint(IPAddress.Parse(this.textBoxServerIP.Text), 11000);
inPutOk = true;
}
catch
{
MessageBox.Show("你输入的服务器IP不正确,请重新输入.");
inPutOk = false;
}
//判断用户是否登陆成功
bool ok = false;
while (ok == false)
{
bool loginOk = LogOn(this.textBoxUserName.Text);
if (loginOk)
ok = true;
else
{
DialogResult res = MessageBox.Show("是否重试?", "是否重试", MessageBoxButtons.YesNo);
if (res == DialogResult.No)
ok = true;
else
ok = false;
}
}
Chat c = (Chat)this.Tag;
c.UserName = this.textBoxUserName.Text;
c.ServerEP = serverEP;
c.ClientSocket = clientSocket;
c.LoadInit();
this.Close();
}
//登陆函数
private bool LogOn(string username)
{
receiveDone = new ManualResetEvent(false);
byte[] userBytes = null;
bool userOk = false;
//判断用户名是否符合格式
while(userOk != true)
{
username = username.ToUpper();
userBytes = Encoding.Unicode.GetBytes(LOGININ + username);
if(userBytes.Length > 24 || userBytes.Length < 10)
{
MessageBox.Show("用户名不得小于6个字节,且不得大于20个字节.");
return false;
}
else
userOk = true;
}
//向服务器发送客户消息
clientSocket.SendTo(userBytes, serverEP);
byte[] data = new Byte[1024];
string comStr = Encoding.Unicode.GetString(data,0,4);
//异面的接收服务器回送的消息
myMethodDelegate DGrecv = new myMethodDelegate(RecvLogin);
DGrecv.BeginInvoke(ref data, null, null);
//等待服务器回送消息的时长为10秒,否则为服务器超时
receiveDone.WaitOne(30000, true);
string recvStr = Encoding.Unicode.GetString(data,0,4);
if(recvStr == comStr)
{
MessageBox.Show("服务器超时.登陆失败!!");
return false;
}
else
{
if(Encoding.Unicode.GetString(data,0,4) == LOGINOK)
{
MessageBox.Show("登陆成功!!");
}
else
{
if (Encoding.Unicode.GetString(data, 0, 4) == HVUSER)
{
MessageBox.Show("用户名重复.登陆失败!!");
return false;
}
else
{
if (Encoding.Unicode.GetString(data, 0, 4) == LOGINOK)
{
MessageBox.Show("服务器未知错误,登陆失败!!!!");
return false;
}
}
}
}
return true;
}
//登陆时用来异步的接收服务器发送的消息
void RecvLogin(ref byte[] inData)
{
clientSocket.Receive(inData);
receiveDone.Set();
}
}
}
namespace TestClient
{
partial class Login
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.label2 = new System.Windows.Forms.Label();
this.textBoxServerIP = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.textBoxUserName = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(56, 27);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(77, 12);
this.label2.TabIndex = 7;
this.label2.Text = "服务器IP:";
//
// textBoxServerIP
//
this.textBoxServerIP.Location = new System.Drawing.Point(139, 24);
this.textBoxServerIP.Name = "textBoxServerIP";
this.textBoxServerIP.Size = new System.Drawing.Size(100, 21);
this.textBoxServerIP.TabIndex = 6;
this.textBoxServerIP.Text = "10.24.11.231";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(56, 68);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(53, 12);
this.label1.TabIndex = 9;
this.label1.Text = "用户名:";
//
// textBoxUserName
//
this.textBoxUserName.Location = new System.Drawing.Point(139, 65);
this.textBoxUserName.Name = "textBoxUserName";
this.textBoxUserName.Size = new System.Drawing.Size(100, 21);
this.textBoxUserName.TabIndex = 8;
//
// button1
//
this.button1.Location = new System.Drawing.Point(100, 106);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 10;
this.button1.Text = "登 陆";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Login
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 144);
this.Controls.Add(this.button1);
this.Controls.Add(this.label1);
this.Controls.Add(this.textBoxUserName);
this.Controls.Add(this.label2);
this.Controls.Add(this.textBoxServerIP);
this.Name = "Login";
this.Text = "Login";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox textBoxServerIP;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBoxUserName;
private System.Windows.Forms.Button button1;
}
}
Server:
using System;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Text;
namespace ChatServer
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public partial class Form1 : System.Windows.Forms.Form
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
//全局变量
Socket ServerSocket = null;
IPEndPoint ipep = null;
Hashtable htUserList = null;
string[] userName = new string[1000];
IPEndPoint[] userIPEP = new IPEndPoint[1000];
int[] userTime = new int[1000];
TimerCallback timerDelegate = null;
WriteLog writeLog = null;
Thread listenTH = null;
bool flag = true;
//常量
//以下是客户端到服务器端的消息开头
const string LOGININ = "10"; //请求登陆的消息|||消息形式:10+自己的用户名
const string LOGINOUT = "11"; //请求登出的消息|||消息形式:11+自己的用户名
const string GETULIST = "12"; //请求获得在线用户列表|||消息形式:12
const string P2PCONN = "13"; //请求P2P连接的消息|||消息形式:13+自己的用户名+|+对方的用户名
const string HOLDLINE = "14"; //保持连接.|||消息开式:14+自己的用户名
const string LOGINADD = "15";//新用户登录
const string LOGINREMOVE = "16";//用户登出
//以下是服务器到客户端的消息开头
const string HVUSER = "20"; //用户名已存在
const string GETUSER = "21"; //在线用户列表|||消息格式:21+用户名+EP
const string MAKHOLD = "22"; //打洞命令|||消息格式:22+IP
const string LOGINOK = "23"; //登陆成功
const string SERVCLS = "24"; //服务器关闭
const string MSGEND = "25"; //消息结束
//以下是服务器端的命名
const string EXITPRO = "EXIT"; //退出命令
const string SHOWULIST = "SHOWUSER";
private System.Windows.Forms.StatusBar statusBar1;
private System.Windows.Forms.StatusBarPanel statusBarPanel1;
private System.Windows.Forms.TextBox textBox1; //显示在线用户
const string HELP = "HELP"; //显示帮助
private List<IPEndPoint> onlineIPList;
public Form1()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
//初始化变量
ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
ipep = new IPEndPoint(IPAddress.Any, 11000);
htUserList = new Hashtable();
writeLog = new WriteLog();
timerDelegate = new TimerCallback(OnLineTimeOut);
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.statusBar1 = new System.Windows.Forms.StatusBar();
this.statusBarPanel1 = new System.Windows.Forms.StatusBarPanel();
this.textBox1 = new System.Windows.Forms.TextBox();
((System.ComponentModel.ISupportInitialize)(this.statusBarPanel1)).BeginInit();
this.SuspendLayout();
//
// statusBar1
//
this.statusBar1.Location = new System.Drawing.Point(0, 319);
this.statusBar1.Name = "statusBar1";
this.statusBar1.Panels.AddRange(new System.Windows.Forms.StatusBarPanel[] {
this.statusBarPanel1});
this.statusBar1.ShowPanels = true;
this.statusBar1.Size = new System.Drawing.Size(466, 22);
this.statusBar1.TabIndex = 0;
//
// statusBarPanel1
//
this.statusBarPanel1.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Spring;
this.statusBarPanel1.Text = "statusBarPanel1";
this.statusBarPanel1.Width = 450;
//
// textBox1
//
this.textBox1.Dock = System.Windows.Forms.DockStyle.Left;
this.textBox1.Location = new System.Drawing.Point(0, 0);
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.textBox1.Size = new System.Drawing.Size(286, 319);
this.textBox1.TabIndex = 1;
this.textBox1.Text = "";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(466, 341);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.statusBar1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.Name = "Form1";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "服务程序";
this.Load += new System.EventHandler(this.Form1_Load);
this.Closed += new System.EventHandler(this.Form1_Closed);
((System.ComponentModel.ISupportInitialize)(this.statusBarPanel1)).EndInit();
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// 启动服务器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, System.EventArgs e)
{
IPAddress[] addressList = Dns.GetHostByName(Dns.GetHostName()).AddressList;
IPAddress ServerIP = addressList[0];
ServerSocket.Bind(ipep);
SetStatusText("服务器正在启动......");
SetStatusText("服务器IP:" + ServerIP.ToString() + " 正在监听" + ipep.Port.ToString() + "端口");
listenTH = new Thread(new ThreadStart(Listen)); //监听线程
listenTH.Start(); //启动
SetStatusText("服务器启动成功.....");
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 5000;
timer.Start();
}
/// <summary>
/// 服务器监听函数
/// </summary>
private void Listen()
{
try
{
while (flag)
{
int recv = 0;
byte[] data = new byte[1024];
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = sender as EndPoint;
recv = ServerSocket.ReceiveFrom(data, ref tempRemoteEP);
string msgHead = Encoding.Unicode.GetString(data, 0, 4);
switch (msgHead)
{
case LOGININ: //用户登录
string LoginThing = UserLogin(data, (IPEndPoint)tempRemoteEP, recv);
if (LoginThing == HVUSER) //用户存在
{
SendMsg(HVUSER, (IPEndPoint)tempRemoteEP);
}
else
{
SendMsg(LOGINOK, (IPEndPoint)tempRemoteEP);
if (onlineIPList == null)
{
onlineIPList = new List<IPEndPoint>();
}
onlineIPList.Add((IPEndPoint)tempRemoteEP);
foreach (IPEndPoint ip in onlineIPList)
{
SendMsg(LOGINADD, ip);
}
}
break;
case LOGINOUT: //用户退出
Userloginout(data, recv);
onlineIPList.Remove((IPEndPoint)tempRemoteEP);
foreach (IPEndPoint ip in onlineIPList)
{
SendMsg(LOGINREMOVE, ip);
}
break;
case GETULIST: // 获取用户列表
string userInfor = GetUserList();
SendMsg(GETUSER + userInfor, (IPEndPoint)tempRemoteEP);
break;
case P2PCONN: //打洞
QuestP2PConn(data, recv);
break;
case HOLDLINE:
HoldOnLine(data, recv);
break;
}
}
}
catch (System.Exception errr)
{
writeLog.AppendErrorLog(errr.ToString());
}
}
/// <summary>
/// 保持用户在线
/// </summary>
/// <param name="data"></param>
/// <param name="recvCount"></param>
private void HoldOnLine(byte[] data, int recvCount)
{
string Uname = Encoding.Unicode.GetString(data, 4, recvCount - 4);
int i = 0;
for (i=0;i<userName.Length;i++)
{
if (Uname == userName[i])
{
userTime[i] = 60;
break;
}
}
}
/// <summary>
/// 用户超时退出
/// </summary>
private void OnLineTimeOut(object state)
{
int i = 0, j= 0;
for (i=0;i<userName.Length;i++)
{
if (userTime[i] > 0)
{
userTime[i] -= 5;
if (userTime[i] < 0)
{
string loginoutmsg = LOGINOUT + userName[i];
userName[i] = "";
userIPEP[i] = null;
byte[] ULoginOutbytes = Encoding.Unicode.GetBytes(loginoutmsg);
for (i=1;j<userName.Length;j++)
{
if (userName[j] != "")
{
if (userIPEP[j] != null)
{
ServerSocket.SendTo(ULoginOutbytes, userIPEP[j]);
}
}
}
}
}
}
}
/// <summary>
/// 转发P2P连接请求
/// </summary>
private void QuestP2PConn(byte[] data, int recv)
{
string recvStr = Encoding.Unicode.GetString(data, 4, recv - 4);
string[] split = recvStr.Split(new char[]{'|'});
IPEndPoint fromEP = null, toEP = null;
int i;
for (i=0;i<userName.Length;i++)
{
if (userName[i] == split[0])
{
fromEP = userIPEP[i];
}
if (userName[i] == split[1])
{
toEP = userIPEP[i];
}
}
byte[] holdbyte = Encoding.Unicode.GetBytes(MAKHOLD + fromEP.ToString());
ServerSocket.SendTo(holdbyte, toEP);
}
/// <summary>
/// 用户登录,直接返回登陆是否成功的值
/// </summary>
/// <param name="data"></param>
/// <param name="userEP"></param>
/// <param name="recvCount"></param>
/// <returns></returns>
private string UserLogin(byte[] data, IPEndPoint userEP, int recvCount)
{
string Uname = Encoding.Unicode.GetString(data, 4, recvCount - 4);
byte[] Uinfobytes = null;
int i, j;
for (i=0;i<userName.Length;i++)
{
if (userName[i] != "" && userName[i] != null)
{
if (userName[i].Equals(Uname))
return HVUSER;
}
}
for (i=0;i<userName.Length;i++)
{
if (userName[i] == "" || userName[i] == null)
{
userName[i] = Uname;
userIPEP[i] = userEP;
userTime[i] = 60;
Uinfobytes = Encoding.Unicode.GetBytes(LOGININ + userName[i] + "|" + userIPEP[i].ToString());
for (j=1;j<userName.Length;j++)
{
if (userName[j] != "" && userName[j] != Uname && userName[j] != null && userIPEP[j] != null)
{
ServerSocket.SendTo(Uinfobytes, userIPEP[j]);
}
}
break;
}
}
return LOGINOK;
}
/// <summary>
/// 用户退出
/// </summary>
/// <param name="data"></param>
/// <param name="recvCount"></param>
private void Userloginout(byte[] data, int recvCount)
{
int i = 0, j = 0;
string Uname = Encoding.Unicode.GetString(data, 4, recvCount - 4);
for (i=0;i<userName.Length;i++)
{
if (Uname.ToUpper() == userName[i].ToUpper())
{
string loginOutMsg = LOGINOUT + Uname;
userName[i] = "";
userIPEP[i] = null;
userTime[i] = 0;
for (j=1;j<userName.Length;j++)
{
if (userName[j]!= null && userName[j] != "")
{
SendMsg(loginOutMsg, userIPEP[j]);
}
}
break;
}
}
}
/// <summary>
/// 获取用户列表
/// </summary>
/// <returns></returns>
private string GetUserList()
{
string userInfor = "";
int i = 0;
for (i=0;i<userName.Length;i++)
{
if (userName[i] != "" && userName[i] != null && userIPEP[i] != null)
{
userInfor += userName[i] + "|" + userIPEP[i].ToString() + "|";
}
}
return userInfor;
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="msg"></param>
/// <param name="remoteEP"></param>
private void SendMsg(string msg, IPEndPoint remoteEP)
{
byte[] sendBytes = Encoding.Unicode.GetBytes(msg);
try
{
ServerSocket.SendTo(sendBytes, remoteEP);
}
catch (System.Exception errr) {
writeLog.AppendErrorLog(errr.ToString());
}
}
/// <summary>
/// 提示信息
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
private void SetStatusText(string text)
{
this.statusBarPanel1.Text = text;
}
/// <summary>
/// 显示在线用户
/// </summary>
private StringBuilder ShowUser()
{
StringBuilder strB = new StringBuilder();
if (userName != null && userName.Length > 0)
{
int i;
for (i=0;i<userName.Length;i++)
{
if (userName[i] != "" && userIPEP[i] != null)
{
strB.Append(Environment.NewLine + "用户名:" + userName[i] + " 地址:" + userIPEP[i].ToString());
}
}
}
return strB;
}
/// <summary>
/// 退出
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Closed(object sender, System.EventArgs e)
{
flag = false;
if (listenTH != null)
{
if (listenTH.ThreadState == ThreadState.Running)
{
listenTH.Abort();
}
listenTH = null;
}
timerDelegate = null;
ServerSocket.Close();
}
private void timer_Tick(object sender, EventArgs e)
{
OnLineTimeOut(null);
StringBuilder strB = ShowUser();
this.textBox1.Text = strB.ToString();
}
}
}
using System;
using System.Text;
using System.IO;
using System.Windows.Forms;
namespace ChatServer
{
/// <summary>
/// WriteLog 的摘要说明。
/// </summary>
public class WriteLog
{
public WriteLog()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
/// <summary>
/// 追加日志文件
/// </summary>
/// <param name="errorMsg"></param>
public void AppendErrorLog(string errorMsg)
{
try
{
StringBuilder strB = new StringBuilder();
strB.Append(DateTime.Now.ToString());
strB.Append(" ");
strB.Append(Environment.MachineName.ToString());
strB.Append(" ");
strB.Append(Environment.UserDomainName.ToString() + "\\" + Environment.UserName);
strB.Append(" ");
strB.Append(Environment.OSVersion.Platform.ToString() + "\\" + Environment.OSVersion.Version.ToString());
strB.Append(Environment.NewLine + Environment.NewLine);
strB.Append(errorMsg);
strB.Append(Environment.NewLine + Environment.NewLine);
AppendErrorLog(strB);
}
catch{}
}
/// <summary>
/// 追加日志文件
/// </summary>
/// <param name="strB"></param>
public void AppendErrorLog(StringBuilder strB)
{
try
{
string filePath = "";
filePath = CreateErrorLogFile();
FileStream fileStream = File.Open(filePath, FileMode.Append, FileAccess.Write);
StreamWriter streamWriter = new StreamWriter(fileStream);
streamWriter.Write(strB.ToString());
streamWriter.Flush();
streamWriter.Close();
fileStream.Close();
fileStream = null;
}
catch{}
}
/// <summary>
/// 创建日志文件
/// </summary>
private string CreateErrorLogFile()
{
string directoryPath = Application.StartupPath + "\\ErrorLog";
string filePath = string.Empty;
//判断日志文件夹是否存在
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
//根据日期创建日志文件
filePath = directoryPath + "\\Error" + string.Format("{0:D2}", DateTime.Today.Year) + string.Format("{0:D2}", DateTime.Today.Month) + string.Format("{0:D2}", DateTime.Today.Day) + ".log";
if (!File.Exists(filePath))
{
FileStream fileStream = File.Create(filePath);
fileStream.Close();
}
return filePath;
}
}
}