基于加密的TCPIP网络聊天软件的设计与实现.docx
《基于加密的TCPIP网络聊天软件的设计与实现.docx》由会员分享,可在线阅读,更多相关《基于加密的TCPIP网络聊天软件的设计与实现.docx(15页珍藏版)》请在冰豆网上搜索。
基于加密的TCPIP网络聊天软件的设计与实现
基于加密的TCP/IP网络聊天软件的设计与实现
1绪论
本课题是一个基于加密的TCP/IP网络聊天软件的设计与实现。
通过基于网络方面的编程设计,深入理解C#语言的基础理论知识,更深刻地领会使用C#实现网络编程的精髓!
TCP/IP协议是当前广域网和局域网通用的网络协议,因此,基于TCP/IP的编程就格外重要。
从应用上来说,现在直接利用C层次SocketAPI进行TCP/IP编程的人确实越来越少了,各种现成的框架(如ACE、Java、.NETFCL)和控件(如IP*Works)大大简化了TCP/IP编程的难度。
但是,如果想要在这个领域达到融会贯通的地步,不打下坚实的基础是不可想象的。
正如RichardStevens在TCP/IPIllustrated中所说,在网络编程领域,开发者所遇到的实际问题中,大约有90%都与开发者对于TCP/IP的理解相关。
高层的框架和控件总结了TCP/IP的主要使用模式,并且进行了抽象和封装,这固然非常好,但是想要真正掌握TCP/IP网络编程的真谛,还得靠自己一不一个脚印从基础的开始。
诚然C#语言的抽象、封装给我们使用带来方便,但是我觉得弄清楚其底层实现是非常重要的!
信息化的发展使得丰富的资源共享,而有的人就喜欢贪得无厌去窃取别人的隐私,所以加密技术也不断的发展。
本课题是使用对称加密算法才加密数据。
信息时代计算机技术的发展给整个社会的生活、工作方式带来了一次革命。
因此网络编程设计是很有意义的!
2系统原理分析
1、服务器和客户端连接
基于TCP/IP套接字编程三个步骤:
服务器监听,客户端请求,连接确认。
所谓服务器监听,是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。
为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。
而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
所以Socket接口的网络通讯原理:
首先,服务端有一个进程(或多个进程)在指定的端口等待客户来连接,服务程序等待客户的连接信息,一旦连接上之后,就可以按设计的数据交换方法和格式进行数据传输。
在使用TCP协议时,一般服务端进程先使用socket调用得到一个描述符,然后使用bind调用将一个名字与socket描述符连接起来,对于Internet域就是将Internet地址联编到socket。
对于实例服务端用TcpListener监听,然后把连接的对象实例化为一个TcpClient,调用TcpClient.GetStream()方法,返回网络流实例化为一个NetworlStream流,然后进行Send,Receive。
2、使用对称加密传输流程分析
本程序使用DESCryptoServiceProvider类实现DES对称加密运算。
服务器端:
首先从客户端接收共密钥,然后使用公共密钥加密未来使用的对称密钥,再将加密了的对称密钥发给客户端,最后才给客户端发送加密了的信息!
客户端:
首先建立和发送公共密钥给服务器,然后从服务器接受加密的对称密钥,再解密该对称密钥将它作为自己私有的不对称密钥,最后才接收信息!
3详细代码设计
服务器端开发的详细代码:
//添加的命名空间引用(原始生成的略)
usingSystem.Net;
usingSystem.Net.Sockets;
usingSystem.Threading;
usingSystem.IO;
usingSystem.Security.Cryptography;
namespaceEncryptedTcpServer
{
publicpartialclassFormServer:
Form
{
//连接的用户
System.Collections.Generic.ListuserList=newList();
privatedelegatevoidSetListBoxCallback(stringstr);
privateSetListBoxCallbacksetListBoxCallback;
privatedelegatevoidSetComboBoxCallback(Useruser);
privateSetComboBoxCallbacksetComboBoxCallback;
//使用的本机IP地址
IPAddresslocalAddress;
//监听端口
privateintport=6788;//和书上一样
privateTcpListenermyListener;
publicFormServer()
{
InitializeComponent();
listBoxStatus.HorizontalScrollbar=true;
setListBoxCallback=newSetListBoxCallback(SetListBox);
setComboBoxCallback=newSetComboBoxCallback(AddComboBoxitem);
IPAddress[]addrIP=Dns.GetHostAddresses(Dns.GetHostName());
localAddress=addrIP[0];
buttonStop.Enabled=false;
}
//开始监听
privatevoidbuttonStart_Click(objectsender,EventArgse)
{
myListener=newTcpListener(localAddress,port);
myListener.Start();
SetListBox(string.Format("开始在{0}:
{1}监听客户连接",localAddress,port));
//创建一个线程监听客户端连接请求
ThreadStartts=newThreadStart(ListenClientConnect);
ThreadmyThread=newThread(ts);
myThread.Start();
buttonStart.Enabled=false;
buttonStop.Enabled=true;
}
//接收客户端连接的线程
privatevoidListenClientConnect()
{
while(true)
{
TcpClientnewClient=null;
try
{
//等待用户进入
newClient=myListener.AcceptTcpClient();
}
catch
{
//当单击“停止监听”或者退出此窗体时AcceptTcpClient()会产生异常,因此可以利用此异常退出循环
break;
}
//每接受一个客户端连接,就创建一个对应的线程循环接收该客户端发来的信息
ParameterizedThreadStartpts=newParameterizedThreadStart(ReceiveData);
ThreadthreadReceive=newThread(pts);
Useruser=newUser(newClient);
threadReceive.Start(user);
userList.Add(user);
AddComboBoxitem(user);
SetListBox(string.Format("[{0}]进入",newClient.Client.RemoteEndPoint));
SetListBox(string.Format("当前连接用户数:
{0}",userList.Count));
}
}
//接收、处理客户端信息的线程,每客户1个线程,参数用于区分是哪个客户
privatevoidReceiveData(objectobj)
{
Useruser=(User)obj;
TcpClientclient=user.client;
//是否正常退出接收线程
boolnormalExit=false;
//用于控制是否退出循环
boolexitWhile=false;
while(exitWhile==false)
{
//保存接收的命令字符串
stringreceiveString=null;
//每条命令均带有一个参数,值为true或者false,表示是否有紧跟的字节数组
string[]splitString=null;
byte[]receiveBytes=null;
try
{
//从网络流中读出命令字符串
//此方法会自动判断字符串长度前缀,并根据长度前缀读出字符串
receiveString=user.br.ReadString();
splitString=receiveString.Split(',');
if(splitString[1]=="true")
{
//先从网络流中读出32位的长度前缀
intbytesLength=user.br.ReadInt32();
//然后读出指定长度的内容保存到字节数组中
receiveBytes=user.br.ReadBytes(bytesLength);
}
}
catch
{
//底层套接字不存在时会出现异常
SetListBox("接收数据失败");
}
if(receiveString==null)
{
if(normalExit==false)
{
//如果停止了监听,Connected为false
if(client.Connected==true)
{
SetListBox(string.Format(
"与[{0}]失去联系,已终止接收该用户信息",client.Client.RemoteEndPoint));
}
}
break;
}
SetListBox(string.Format("来自[{0}]:
{1}",user.client.Client.RemoteEndPoint,receiveString));
if(receiveBytes!
=null)
{
SetListBox(string.Format("来自[{0}]:
{1}",
user.client.Client.RemoteEndPoint,Encoding.Default.GetString(receiveBytes)));
}
switch(splitString[0])//公钥和私钥的加密解密实现
{
case"rsaPublicKey":
user.rsa.FromXmlString(Encoding.Default.GetString(receiveBytes));
//加密对称加密的私钥
try
{
byte[]encryptedKey=user.rsa.Encrypt(user.tdes.Key,false);
SendToClient(user,"tdesKey,true",encryptedKey);
//加密IV
byte[]encryptedIV=user.rsa.Encrypt(user.tdes.IV,false);
SendToClient(user,"tdesIV,true",encryptedIV);
}
catch(Exceptionerr)
{
MessageBox.Show(err.Message);
}
break;
case"Logout":
//格式:
Logout
SetListBox(string.Format("[{0}]退出",user.client.Client.RemoteEndPoint));
normalExit=true;
exitWhile=true;
break;
case"Talk":
//解密
stringtalkString=DecryptText(receiveBytes,user.tdes.Key,user.tdes.IV);
if(talkString!
=null)
{
SetListBox(string.Format("[{0}]说:
{1}",client.Client.RemoteEndPoint,talkString));
}
break;
default:
SetListBox("什么意思啊:
"+receiveString);
break;
}
}
userList.Remove(user);
client.Close();
SetListBox(string.Format("当前连接用户数:
{0}",userList.Count));
}
//对称加密算法加密数据的具体实现
privatebyte[]EncryptText(stringstr,byte[]Key,byte[]IV)
{
//创建一个内存流
MemoryStreammemoryStream=newMemoryStream();
//使用传递的私钥和IV创建加密流
CryptoStreamcryptoStream=newCryptoStream(memoryStream,
newTripleDESCryptoServiceProvider().CreateEncryptor(Key,IV),
CryptoStreamMode.Write);
//将传递的字符串转换为字节数组
byte[]toEncrypt=Encoding.UTF8.GetBytes(str);
try
{
//将字节数组写入加密流,并清除缓冲区
cryptoStream.Write(toEncrypt,0,toEncrypt.Length);
cryptoStream.FlushFinalBlock();
//得到加密后的字节数组
byte[]encryptedBytes=memoryStream.ToArray();
returnencryptedBytes;
}
catch(Exceptionerr)
{
SetListBox("加密出错:
"+err.Message);
returnnull;
}
finally
{
cryptoStream.Close();
memoryStream.Close();
}
}
//使用对称加密算法解密接收的数据
privatestringDecryptText(byte[]dataBytes,byte[]Key,byte[]IV)
{
//根据加密后的字节数组创建一个内存流
MemoryStreammemoryStream=newMemoryStream(dataBytes);
//使用传递的私钥、IV和内存流创建解密流
CryptoStreamcryptoStream=newCryptoStream(memoryStream,
newTripleDESCryptoServiceProvider().CreateDecryptor(Key,IV),
CryptoStreamMode.Read);
//创建一个字节数组保存解密后的数据
byte[]decryptBytes=newbyte[dataBytes.Length];
try
{
//从解密流中将解密后的数据读到字节数组中
cryptoStream.Read(decryptBytes,0,decryptBytes.Length);
//得到解密后的字符串
stringdecryptedString=Encoding.UTF8.GetString(decryptBytes);
returndecryptedString;
}
catch(Exceptionerr)
{
SetListBox("解密出错:
"+err.Message);
returnnull;
}
finally
{
cryptoStream.Close();
memoryStream.Close();
}
}
//发送信息到客户端
privatevoidSendToClient(Useruser,stringcommand,byte[]bytes)
{
//每条命令均带有一个参数,值为true或者false,表示是否有紧跟的字节数组
string[]splitCommand=command.Split(',');
try
{
//先将命令字符串写入网络流,此方法会自动附加字符串长度前缀
user.bw.Write(command);
SetListBox(string.Format("向[{0}]发送:
{1}",user.client.Client.RemoteEndPoint,command));
if(splitCommand[1]=="true")
{
//先将字节数组的长度(32位整数)写入网络流
user.bw.Write(bytes.Length);
//然后将字节数组写入网络流
user.bw.Write(bytes);
user.bw.Flush();
SetListBox(string.Format("向[{0}]发送:
{1}",
user.client.Client.RemoteEndPoint,Encoding.UTF8.GetString(bytes)));
if(splitCommand[0]=="Talk")
{
SetListBox("加密前内容:
"+textBoxSend.Text);
}
}
}
catch
{
SetListBox(string.Format("向[{0}]发送信息失败",user.client.Client.RemoteEndPoint));
}
}
privatevoidAddComboBoxitem(Useruser)
{
if(comboBoxReceiver.InvokeRequired==true)
{
this.Invoke(setComboBoxCallback,user);
}
else
{
comboBoxReceiver.Items.Add(user.client.Client.RemoteEndPoint);
}
}
privatevoidSetListBox(stringstr)
{
if(listBoxStatus.InvokeRequired==true)
{
this.Invoke(setListBoxCallback,str);
}
else
{
listBoxStatus.Items.Add(str);
listBoxStatus.SelectedIndex=listBoxStatus.Items.Count-1;
listBoxStatus.ClearSelected();
}
}
//单击停止监听按钮触发的事件
privatevoidbuttonStop_Click(objectsender,EventArgse)
{
SetListBox(string.Format("目前连接用户数:
{0}",userList.Count));
SetListBox("开始停止服务,并依次使用户退出!
");
for(inti=0;i{
comboBoxReceiver.Items.Remove(userList[i].client.Client.RemoteEndPoint);
userList[i].bw.Close();
userList[i].br.Close();
userList[i].client.Close();
}
//通过停止监听让myListener.AcceptTcpClient()产生异常退出监听线程
myListener.Stop();
buttonStart.Enabled=true;
buttonStop.Enabled=false;
}
//单击发送按钮的Click事件
privatevoidbuttonSend_Click(objectsender,EventArgse)
{
intindex=comboBoxReceiver.SelectedIndex;
if(index==-1)
{
MessageBox.Show("请先选择接收方,然后再单击[发送]");
}
else
{
Useruser=(User)userList[index];
//加密textBoxSend.Text的内容
byte[]encryptedBytes=EncryptText(textBoxSend.Text,user.tdes.Key,user.tdes.IV);
if(encryptedBytes!
=null)
{
SendToClient(user,"Talk,true",encryptedBytes);
textBoxSend.Clear();
}
}
}
privatevoidFormServer_FormClosing(objectsender,FormClosingEventArgse)
{
//未单击开始监听就直接退出时,myListener为null
if(myListener!
=null)
{
buttonStop_Click(null,null);
}
}
//textBoxSend获得焦点并释放按键后触发的事件
privatevoidtextBoxSend_KeyPres