您的位置:首页 > 职场人生

黑马程序员-我的入学笔记9-关于socket简易聊天室

2013-09-27 22:49 337 查看
---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IO开发S</a>、<a
href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

 关于套接字下写的简易聊天室,根据视频的内容,将代码实现,还有许多需要扩展完善的地方,只是初步的了解,下面是服务端和客户端的代码.

 服务端

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;

using System.Net;//IPAdress,IPEndPoint(ip和端口)类的命名空间
using System.Net.Sockets;//引用套接字
using System.Threading;//引用多线程
using System.Windows.Forms;//引用关于OpenFileDialog和SaveFileDialog
using System.IO;//引用IO流

namespace 聊天室
{
public partial class ChatSever : Form
{
public ChatSever()
{
InitializeComponent();
TextBox.CheckForIllegalCrossThreadCalls = false;//关闭对跨文本框线程的调用错误
}

Thread threadConnectWatch = null;//创建一个线程用于监听客户端连接请求
Socket socketWatch = null;//创建一个套接字,服务器端负责监听

private void btnConnect_Click(object sender, EventArgs e)
{

//new套接字,服务器端负责监听,参数(IP4寻址协议,流逝连接,用TCP协议接收数据)
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取文本框中的IP地址对象,Parse方法,将IPiuce字符串转换成实例
IPAddress adress = IPAddress.Parse(txtIPadress.Text.Trim());//Trim方法(去除尾部空白)
//创建包含IP和Point的网络节点对象
IPEndPoint endpoint = new IPEndPoint(adress, int.Parse(txtPoint.Text.Trim()));
//通过Bind方法建立连接,参数(之前建立的的网络节点对象)
socketWatch.Bind(endpoint);
//设置监听队列的长度
socketWatch.Listen(10);
ShowMsg("服务器启动监听!~~");
//开始监听客户端连接请求,注意Accept会阻断当前线程.利用多线程解决问题
//Socket socketConnection = socketWatch.Accept();

//修改如下,new一个线程,参数(wathConnect委托)
threadConnectWatch = new Thread(watchConnect);
threadConnectWatch.IsBackground = true;//设置成后台方法
threadConnectWatch.Start();//开启线程

}

//创建一个集合,用于存储新的连接套接字
Dictionary<string,Socket> dic=new Dictionary<string,Socket>();
Dictionary<string, Thread> dicThr = new Dictionary<string, Thread>();

/// <summary>
/// 负责监听客户端请求的方法
/// </summary>
void watchConnect()
{
while (true)//服务端不断的监听
{
//一旦监听到连接请求,返回一个新的套接字
Socket sokConnection = socketWatch.Accept();
//将新的套接字添加到集合中
dic.Add(sokConnection.RemoteEndPoint.ToString(),sokConnection);

//每次再为每个客户端添加一个用于接收改客户端消息的线程
Thread thrReceice = new Thread(ReceiveMsg);
thrReceice.IsBackground = true;
thrReceice.Start(sokConnection.RemoteEndPoint.ToString());

//将线程添加到线程集合中
dicThr.Add(sokConnection.RemoteEndPoint.ToString(), thrReceice);

//添加到列表栏,用唯一的ip端口字符串
lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
ShowMsg("与客户端连接成功!~~"+sokConnection.RemoteEndPoint.ToString());
}
}
/// <summary>
/// 接收客户端数据的方法
/// </summary>
/// <param name="sokConnects"></param>
void ReceiveMsg(object key)
{
//用套接字集合的key来调用不同套接字的Receive方法
string keys = key as string ;
while (true)
{
byte[] arrRecMsg = new byte[1024 * 1024 * 2];
int length = -1;
try
{
length = dic[keys].Receive(arrRecMsg);
}
catch (SocketException ex)
{
MessageBox.Show(ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
//判断接收的是否是文件
if (arrRecMsg[0] == 0)
{
string strRecMsg = System.Text.Encoding.UTF8.GetString(arrRecMsg, 1, length-1);
ShowMsg(keys + ",客户端发来信息:" + strRecMsg);
}
else if (arrRecMsg[0] == 1)
{
SaveFileDialog sfd = new SaveFileDialog();
if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string SaveFilePath = sfd.FileName;
using (FileStream fs = new FileStream(SaveFilePath, FileMode.Create))
{
fs.Write(arrRecMsg, 1, length-1);
ShowMsg("保存文件成功!" + SaveFilePath);
}
}
}
else
{
MessageBox.Show("没有接收到数据!");
}
}
}

/// <summary>
/// 服务器端发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
//先获取文本中发送的消息
string strMsg = txtSendMsg.Text;
//将要发送的数据转换成字节数据
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
byte[] arrMsgSend = new byte[arrMsg.Length+1];
arrMsgSend[0] = 0;
Buffer.BlockCopy(arrMsg, 0, arrMsgSend, 1, arrMsg.Length);
//利用连接套接字发送数据
string strClientKey = lbOnline.Text;
//或得选中列表中的对应的某个客户端的通信地址
try
{
dic[strClientKey].Send(arrMsgSend);
}
catch
{
MessageBox.Show("请选择要发送的客户端");
}
ShowMsg(String.Format("发送消息给{0}:{1}" ,lbOnline.Text,strMsg));
}

//群发消息
private void btnSendToAll_Click(object sender, EventArgs e)
{
string strMsg = txtSendMsg.Text;
//字符串转换成字节
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
//读取全部用于连接的套接字,全部发送消息
foreach (Socket s in dic.Values)
{
s.Send(arrMsg);
}
ShowMsg("群发完毕!~~");
}

//选择文件的路径
private void btnChooseFile_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
txtFilePath.Text = ofd.FileName;
}
}

//发送文件
private void btnSendFile_Click(object sender, EventArgs e)
{
//一使用完就释放内存用using
using(FileStream fs=new FileStream(txtFilePath.Text,FileMode.Open))//打开文件的路径和方式
{
byte[] arrFile = new byte[1024 * 1024 * 2];//new一个2M的字节数组(缓存区)
//将文件读到字节数组中,并得到真实数据的长度
int length=fs.Read(arrFile, 0, arrFile.Length);
//新定义个字节数组,存储带标示位的数据
byte[] arrFileSend = new byte[length+1];
arrFileSend[0] = 1;//代表发送的数据是文件
//最简单的方法,利用循环将原数据往后移一位
//for (int i = 0; i < length; i++)
//{
//    arrFileSend[i + 1] = arrFile[i];
//}
//将arrFile从第0个数据拷贝到arrFileSend中,从第1个开始到length长度
Buffer.BlockCopy(arrFile, 0, arrFileSend, 1,length);
//选择客户端发送带标志位的数据
dic[lbOnline.Text].Send(arrFileSend);
}
}
/// <summary>
/// 文本框的显示
/// </summary>
/// <param name="msg"></param>
void ShowMsg(string msg)
{
try
{
txtMsg.AppendText(msg + "\r\n");
}
catch
{
MessageBox.Show("请选择客户端!");
}
}

}
}

客户端

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;
using System.IO;

namespace 客户端
{
public partial class client : Form
{
public client()
{
InitializeComponent();
TextBox.CheckForIllegalCrossThreadCalls = false;
}

Socket socketClient = null;
Thread threadRec = null;

private void btnConnect_Click(object sender, EventArgs e)
{
//定义一个套接字,用于连接
IPAddress address = IPAddress.Parse(txtIPadress.Text);
IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPoint.Text.Trim()));
socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接服务器
if (endPoint == null)
{
MessageBox.Show("没有可连接的服务器!");
}
else
{
socketClient.Connect(endPoint);
}
//利用线程接收服务器端发来的消息
threadRec = new Thread(ReceiveMsg);
threadRec.IsBackground = true;
threadRec.Start();

}
/// <summary>
/// 用来接收消息的套接字方法
/// </summary>
void ReceiveMsg()
{
while (true)
{
//接收数据定义的缓存区(2M),字节类型
byte[] arrMsgRec = new byte[1024 * 1024 * 2];
//length是服务器端真正传过来数据的长度(Receive的返回值),Receive接收数据
int length = -1;
try
{
length = socketClient.Receive(arrMsgRec);
}
catch (SocketException ex)
{
MessageBox.Show(ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
//判断接收的是否是文件
if (arrMsgRec[0]== 0)
{
string strRecMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length-1);
ShowMsg( "服务端发来信息:" + strRecMsg);
}
else if (arrMsgRec[0] == 1)
{
SaveFileDialog sfd = new SaveFileDialog();
if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string SaveFilePath = sfd.FileName;
using (FileStream fs = new FileStream(SaveFilePath, FileMode.Create))
{
fs.Write(arrMsgRec, 1, length-1);
ShowMsg("保存文件成功!" + SaveFilePath);
}
}
}
////将字节装换成了字符串,只转真正有用的字符
//string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,0,length);
////打印出来
//ShowMsg(strMsgRec);
}
}
/// <summary>
/// 将接收到信息打印到文本框中
/// </summary>
/// <param name="msg"></param>
void ShowMsg(string msg)
{
txtMsg.AppendText(msg + "\r\n");
}

//发送消息
private void btnSend_Click(object sender, EventArgs e)
{
string strSendMsg = txtSendMsg.Text;
//将字符串转成网络传送的字节数据
byte[] arrSendMsg = System.Text.Encoding.UTF8.GetBytes(strSendMsg);
byte[] arrSend = new byte[arrSendMsg.Length+1];
arrSend[0] = 0;
Buffer.BlockCopy(arrSendMsg, 0, arrSend, 1, arrSendMsg.Length);
//发送数据
socketClient.Send(arrSend);
ShowMsg("我说:" + strSendMsg);
}

//选择文件路径
private void btnChosseFile_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
txtFilePath.Text = ofd.FileName;
}
}

//发送文件
private void btnSendFile_Click(object sender, EventArgs e)
{
//一使用完就释放内存用using
using (FileStream fs = new FileStream(txtFilePath.Text, FileMode.Open))//打开文件的路径和方式
{
byte[] arrFile = new byte[1024 * 1024 * 2];//new一个2M的字节数组(缓存区)
//将文件读到字节数组中,并得到真实数据的长度
int length = fs.Read(arrFile, 0, arrFile.Length);
//新定义个字节数组,存储带标示位的数据
byte[] arrFileSend = new byte[length + 1];
arrFileSend[0] = 1;//代表发送的数据是文件

//最简单的方法,利用循环将原数据往后移一位
//for (int i = 0; i < length; i++)
//{
//    arrFileSend[i + 1] = arrFile[i];
//}

//将arrFile从第0个数据拷贝到arrFileSend中,从第1个开始到length长度
Buffer.BlockCopy(arrFile, 0, arrFileSend, 1, length);
//选择客户端发送带标志位的数据
socketClient.Send(arrFileSend);
}

}

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: