基于TCP的局域网视频聊天程序课程设计.docx
《基于TCP的局域网视频聊天程序课程设计.docx》由会员分享,可在线阅读,更多相关《基于TCP的局域网视频聊天程序课程设计.docx(41页珍藏版)》请在冰豆网上搜索。
基于TCP的局域网视频聊天程序课程设计
《网络程序设计》课程设计报告
题目:
专业:
学号:
姓名:
完成日期:
成绩:
一、题目及要求
1.基本要求
本课程设计的基本要求是在校园网范围内能进行视频聊天,文字聊天,文件传输。
2.功能详细描述
视频聊天:
在局域网范围内通过TCP协议实现客户端与客户端之间视频的传输,客户端能够看到服务端的视频,服务端也能够看到客户端的视频。
文字聊天:
客户端和服务端之间在进行视频的同时也能够进行简单的文字聊天。
文件传输:
客户端和服务端之间可以在建立连接之后在任意时间进行文件传输。
在文件传输的同时可以进行文字聊天和视频传输的功能。
3.可能出现的难点
系统开发的重难点在于视频画面的捕捉并传输、文件的传输,因此系统的重难点是视频的传输和文件的传输。
二、系统概要设计
系统开发用到了三大基本技术,分别为文字聊天、文件传输技术和视频处理技术,由于整个过程是基于TCP协议,首先介绍TCP协议的建立过程,如图2-1
1.文字聊天的基本原理
系统分为客户端和服务端,严格上说客户端在某些时刻也作为服务端使用,服务端在某些时刻也作为服务端使用。
服务端首先启动开始监听消息传输时使用的端口,当客户端启动时,主动连接服务端。
连接完成之后客户端与服务端建立套接字连接,在此之后两者之间便可以对等的进行文字聊天。
原理图如下所示:
2.文件传输技术
文件传输中使用了文件流和TCP套接字来传输,主要过程如下:
客户端和服务端建立连接
建立的过程和文字聊天建立的连接过程是相同的。
发送端向接收端发送要接受文件的文件名
发送端从文件中读取到缓冲区并发送给客户端
客户端和服务端的用于发送和接受的缓冲区的大小是相同的,为了实现两个的大小相同可在输出文件之前由发送方告诉接受方本次输出所用的缓冲区的大小。
每次发送之前应将剩余文件的大小与缓冲区的大小做比较,当剩余文件的大小小于缓冲区的大小时,调整缓冲区的大小同剩余文件相同,将剩余的文件发送到接收端。
关闭文件流
3.视频传输技术
视频的传输过程利用了avicap32.dll和user32.dll完成摄像头的视频采集。
avicap32.dll和user32.dll是WindowAPI应用程序接口的相关模块。
avicap32.dll接口可以方便地对摄像头和其他视频硬件进行API电影和视频的截取。
主要过程如下所示:
(将发起视频的称为客户端)
客户端发起视频连接的请求
客户端打开本地摄像头,每隔10ms向服务端发送采集到的图片,同时接受服务端发送过来的图片
服务端打开本地的摄像头,同时接受客户端发送过来的图片和向客户端发送图片
关闭视频传输
流程图如下所示:
三、系统详细设计
客户机与服务机之间的通信过程如下所示
由上图可知主要的函数为字节数组发送函数、字节数组接收函数、消息发送函数、消息接收函数、文件发送函数、文件接收函数、视频发送函数、视频接收函数。
详细介绍如下所示:
1.字节数组发送函数SendVarData
publicstaticintSendVarData(Sockets,byte[]data)
{
//函数返回的是发送字节数组的大小
inttotal=0;
intsize=data.Length;//发送数组的大小
intdataleft=size;
intsent;
byte[]datasize=newbyte[4];
datasize=BitConverter.GetBytes(size);
sent=s.Send(datasize);//首先发送字节数组的大小
while(total{//循环发送字节数组的主体sent=s.Send(data,total,dataleft,SocketFlags.None);total+=sent;dataleft-=sent;}returntotal;}2.字节数组接受函数ReceiveVarDatapublicstaticbyte[]ReceiveVarData(Sockets){//函数返回值是接受的字节数组inttotal=0;intrecv;byte[]datasize=newbyte[4];recv=s.Receive(datasize,0,4,SocketFlags.None);//首先接受字节数组的大小intsize=BitConverter.ToInt32(datasize,0);//转换为int型intdataleft=size;//剩余的大小byte[]data=newbyte[size];while(total{//接受主体函数recv=s.Receive(data,total,dataleft,SocketFlags.None);if(recv==0){data=null;break;}total+=recv;dataleft-=recv;}returndata;}3.消息接受函数ListenMessage()privatevoidListenMessage(){IPAddressmyIP=Dns.GetHostAddresses(Dns.GetHostName())[0];//获取本机的IP地址ServerMessageSocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);ServerMessageSocket.Bind(newIPEndPoint(myIP,myport2));//绑定端口号ServerMessageSocket.Listen(10);//等待客户链接client=ServerMessageSocket.Accept();IPEndPointip1=(IPEndPoint)client.RemoteEndPoint;//获取对方的IP地址SendIP=ip1.Address;//将对方的IP地址保存到全局变量中,以后使用message=DateTime.Now.ToString()+":客户端"+client.RemoteEndPoint+"接入\n";AddMessage(message);//打印提示消息while(Flag==true)//接受对方发过来的消息{stringMessage="对方:"+System.Text.Encoding.Unicode.GetString(TransFile.ReceiveVarData(client))+"\n";AddMessage(Message);}}4.接受文件的函数ReceiveFilepublicvoidReceiveFile(ObjectMyclient){Socketclient=(Socket)Myclient;stringSendFileName=System.Text.Encoding.Unicode.GetString(TransFile.ReceiveVarData(client));//获得[文件名]stringPath=SendFileName.Substring(0,SendFileName.LastIndexOf("\\")+1);//获取目录信息if(!Directory.Exists(@Path))//创建目录Directory.CreateDirectory(@Path);else{//如果目录已经存在则删除原有的目录信息Directory.Delete(@Path,true);Directory.CreateDirectory(@Path);}FileStreamMyFileStream=newFileStream(SendFileName,FileMode.Create,FileAccess.Write);//创建一个新文件intSendedCount=0;//已发送包的个数while(true){byte[]data=TransFile.ReceiveVarData(client);if(data.Length==0){break;}else{SendedCount++;//将接收到的数据包写入到文件流对象MyFileStream.Write(data,0,data.Length);}}//关闭文件流MyFileStream.Close();message=DateTime.Now.ToString()+":文件传输完成!文件保存在"+SendFileName+"\n";AddMessage(message);//打印出提示信息}5.发送文件的函数publicvoidStartSend(ObjectPath){stringfile=(string)Path.ToString();stringmessage=null; try{IPEndPointiep=newIPEndPoint(SendIP,2052);client2=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);//创建套接字client2.Connect(iep);//连接到发送端FileInfoEzoneFile=newFileInfo(file);FileStreamEzoneStream=EzoneFile.OpenRead();//打开文件流intPacketSize=10000;//包的大小intPacketCount=(int)(EzoneStream.Length/((long)PacketSize));//包的数量intLastDataPacket=(int)(EzoneStream.Length-((long)(PacketSize*PacketCount)));//最后一个包的大小stringpath="C:\\Temp\\"+EzoneFile.Name;//发送[文件名]到客户端TransFile.SendVarData(client2,System.Text.Encoding.Unicode.GetBytes(path));//数据包byte[]data=newbyte[PacketSize];//开始循环发送数据包for(inti=0;i{//从文件流读取数据并填充数据包EzoneStream.Read(data,0,data.Length);//发送数据包TransFile.SendVarData(client2,data);}//如果还有多余的数据包,则应该发送完毕!if(LastDataPacket!=0){data=newbyte[LastDataPacket];EzoneStream.Read(data,0,data.Length);TransFile.SendVarData(client2,data);}EzoneStream.Close();client2.Shutdown(SocketShutdown.Both);}catch{MessageBox.Show("远程主机未开启!");}//关闭文件流client2.Close();message=DateTime.Now.ToString()+":文件发送完毕\n";AddMessage(message);}6.发送视频函数privatevoidSend_One_Capture(){//将字节数组存放到内存流中MemoryStreamms=newMemoryStream();try{//将摄像头的一帧数据存放到剪贴板中myCamera.CaptureWindow();//从剪贴板中获取图片data=Clipboard.GetDataObject();//将截图存放到内存流中if(data.GetDataPresent(typeof(System.Drawing.Bitmap))){bmap=(Image)(data.GetData(typeof(System.Drawing.Bitmap)));bmap.Save(ms,System.Drawing.Imaging.ImageFormat.Bmp);}byte[]arrImage=ms.GetBuffer();//将内存流转换为字节数组stringarray=ms.ToString();TransFile.SendVarData(client_camera,arrImage);ms.Flush();}catch(Exceptionex){MessageBox.Show("视频发送错误!");}ms.Close();}7.视频接受函数privatevoidlistentCamera(){Form1.CheckForIllegalCrossThreadCalls=false;IPAddressmyIP=Dns.GetHostAddresses(Dns.GetHostName())[0];//本机IP地址ServerMessageSocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);ServerMessageSocket.Bind(newIPEndPoint(myIP,camera_port));//监听端口ServerMessageSocket.Listen(1);Socketclient=ServerMessageSocket.Accept();message=DateTime.Now.ToString()+":对方"+client.RemoteEndPoint+"请求视频\n";AddMessage(message);//打印消息if(single){while(true){//接受视频函数的主体byte[]buffer=TransFile.ReceiveVarData(client);receiveMS=newMemoryStream(buffer);this.pictureFriend.Image=Image.FromStream(receiveMS);//显示到控件receiveMS.Close();//关闭内存流}}else{yulan=false;this.buttonSee.Text="取消预览";LocalVedioShow();Threadsendshipin=newThread(sendSP);sendshipin.Start();while(true){byte[]buffer=TransFile.ReceiveVarData(client);receiveMS=newMemoryStream(buffer);this.pictureFriend.Image=Image.FromStream(receiveMS);receiveMS.Close();}}client.Close();}四、系统测试及结果1.服务端软件启动时界面如下所示当服务端软件启动时,首先单击【开启服务】,这样客户端才能连接到服务端上。●消息发送:在消息文本框中输入要发送的消息,单击下面的【发送】即可发送消息。发送的消息会显示在上面的聊天记录文本框中。●文件传输:单击【浏览】,选择要传送的文件,文件选择完毕后单击【发送】●视频聊天:单击【本地预览】可以看到本地视频预览的效果,但客户端连接到服务端时,对方邀请后即可进行视频聊天。2.客户端软件启动时界面如下所示当服务端启动之后,客户端才可以进行相关的操作:●在“服务方IP”中输入服务端的IP地址,如果测试的是本机就直接输入本机的IP地址,软后单击【启动服务】,便可以连接到服务端,在连接到服务端之后才可以进行其他的相关操作。●消息传输:在消息文本框中输入消息,单击下面的【发送】按钮,便可以将消息发送到服务端,发送的消息显示在上面的文本框中,同时接受的消息也显示在这个文本框中,可以达到聊天的效果。●文件传输:单击【浏览】按钮,选择要发送的文件,文件的路径会显示在他旁边的文本框中,单击发送按钮便可以将文件发送到服务端。●视频传输:当客户端与服务端连接之后便可以进行视频聊天,单击【本地预览】可以进行本地视频的预览,单击【邀请聊天】后,服务端会受到客户端的视频请求,这样之后便可以进行视频聊天了。3.文字聊天的截图4.文件传输截图5.视频聊天截图五、课程设计总结通过这次课程设计,我对软件开发有了进一步的了解,把理论知识应用于实验中。也让我重新熟悉了C#语言的相关内容,加深了对C#语言知识的深化和用途的理解。通过这次的课程设计,对于TCP/IP协议,套接字的使用、摄像头的启动与应用有了很深入的理解。实验过程中也遇到了很多的问题,如文件的传输、摄像头的驱动、画面的采集等,在上面花费了较多的时间,收集了很多的资料,最总将问题顺利解决。这同时也锻炼了我的解决问题和资料收集的能力。当然,软件中还有很多其他的问题,比如软件关闭后可能会有进程没有关闭等异常情况,这将在后期的维护当中逐步解决。总之,这次的课程设计的顺利完成使我受益匪浅。六、附录课程设计源代码客户端主要程序代码publicpartialclassForm1:Form{IPAddressSendIP=null;Socketclient1;//接收和发送消息的套接字boolFlag=true;//标示是否开启服务SocketServerFileSocket;//文件套接字SocketServerMessageSocket;//消息套接字intmyport1=2050;//文件发送端口intmyport2=2051;//消息监听端口intmyport3=2052;//文件监听端口intcamera_port=2053;//视频监听端口intcamera_port1=2054;//视频监听端口2ThreadListen1;//监听文件线程ThreadListen2;//监听消息线程Threadcamera_listen;//监听视频的线程boolisshipin=false;//是否在视频聊天stringmessage;Socketclient_camera;IDataObjectdata;Imagebmap;boolsingle=true;MemoryStreamreceiveMS;WebCameramyCamera=newWebCamera();publicForm1(){InitializeComponent();}privatevoidSearch_Click(objectsender,EventArgse){if(this.openFileDialog1.ShowDialog()==DialogResult.OK){this.FilePath.Text=this.openFileDialog1.FileName;}}//////利用委托向RichTextBox中添加信息//////privatedelegatevoidAddMessageDelegate(stringmessage);publicvoidAddMessage(stringmessage){if(richTextBox1.InvokeRequired){AddMessageDelegated=AddMessage;richTextBox1.Invoke(d,message);}else{richTextBox1.AppendText(message);}}privatevoidSend_Click(objectsender,EventArgse){if(this.FilePath.Text.Length==0){MessageBox.Show("请选择文件!");return;}stringPath=this.FilePath.Text;ThreadSendFile=newThread(StartSend);SendFile.Start(this.FilePath.Text);}//发送文件函数Socketclient;publicvoidStartSend(ObjectPath){stringfile=(string)Path.ToString();stringmessage=null;try{//指向远程服务端节点IPEndPointipep=newIPEndPoint(SendIP,myport1);
{//循环发送字节数组的主体
sent=s.Send(data,total,dataleft,SocketFlags.None);
total+=sent;
dataleft-=sent;
}
returntotal;
2.字节数组接受函数ReceiveVarData
publicstaticbyte[]ReceiveVarData(Sockets)
{//函数返回值是接受的字节数组
intrecv;
recv=s.Receive(datasize,0,4,SocketFlags.None);//首先接受字节数组的大小
intsize=BitConverter.ToInt32(datasize,0);//转换为int型
intdataleft=size;//剩余的大小
byte[]data=newbyte[size];
while(total{//接受主体函数recv=s.Receive(data,total,dataleft,SocketFlags.None);if(recv==0){data=null;break;}total+=recv;dataleft-=recv;}returndata;}3.消息接受函数ListenMessage()privatevoidListenMessage(){IPAddressmyIP=Dns.GetHostAddresses(Dns.GetHostName())[0];//获取本机的IP地址ServerMessageSocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);ServerMessageSocket.Bind(newIPEndPoint(myIP,myport2));//绑定端口号ServerMessageSocket.Listen(10);//等待客户链接client=ServerMessageSocket.Accept();IPEndPointip1=(IPEndPoint)client.RemoteEndPoint;//获取对方的IP地址SendIP=ip1.Address;//将对方的IP地址保存到全局变量中,以后使用message=DateTime.Now.ToString()+":客户端"+client.RemoteEndPoint+"接入\n";AddMessage(message);//打印提示消息while(Flag==true)//接受对方发过来的消息{stringMessage="对方:"+System.Text.Encoding.Unicode.GetString(TransFile.ReceiveVarData(client))+"\n";AddMessage(Message);}}4.接受文件的函数ReceiveFilepublicvoidReceiveFile(ObjectMyclient){Socketclient=(Socket)Myclient;stringSendFileName=System.Text.Encoding.Unicode.GetString(TransFile.ReceiveVarData(client));//获得[文件名]stringPath=SendFileName.Substring(0,SendFileName.LastIndexOf("\\")+1);//获取目录信息if(!Directory.Exists(@Path))//创建目录Directory.CreateDirectory(@Path);else{//如果目录已经存在则删除原有的目录信息Directory.Delete(@Path,true);Directory.CreateDirectory(@Path);}FileStreamMyFileStream=newFileStream(SendFileName,FileMode.Create,FileAccess.Write);//创建一个新文件intSendedCount=0;//已发送包的个数while(true){byte[]data=TransFile.ReceiveVarData(client);if(data.Length==0){break;}else{SendedCount++;//将接收到的数据包写入到文件流对象MyFileStream.Write(data,0,data.Length);}}//关闭文件流MyFileStream.Close();message=DateTime.Now.ToString()+":文件传输完成!文件保存在"+SendFileName+"\n";AddMessage(message);//打印出提示信息}5.发送文件的函数publicvoidStartSend(ObjectPath){stringfile=(string)Path.ToString();stringmessage=null; try{IPEndPointiep=newIPEndPoint(SendIP,2052);client2=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);//创建套接字client2.Connect(iep);//连接到发送端FileInfoEzoneFile=newFileInfo(file);FileStreamEzoneStream=EzoneFile.OpenRead();//打开文件流intPacketSize=10000;//包的大小intPacketCount=(int)(EzoneStream.Length/((long)PacketSize));//包的数量intLastDataPacket=(int)(EzoneStream.Length-((long)(PacketSize*PacketCount)));//最后一个包的大小stringpath="C:\\Temp\\"+EzoneFile.Name;//发送[文件名]到客户端TransFile.SendVarData(client2,System.Text.Encoding.Unicode.GetBytes(path));//数据包byte[]data=newbyte[PacketSize];//开始循环发送数据包for(inti=0;i{//从文件流读取数据并填充数据包EzoneStream.Read(data,0,data.Length);//发送数据包TransFile.SendVarData(client2,data);}//如果还有多余的数据包,则应该发送完毕!if(LastDataPacket!=0){data=newbyte[LastDataPacket];EzoneStream.Read(data,0,data.Length);TransFile.SendVarData(client2,data);}EzoneStream.Close();client2.Shutdown(SocketShutdown.Both);}catch{MessageBox.Show("远程主机未开启!");}//关闭文件流client2.Close();message=DateTime.Now.ToString()+":文件发送完毕\n";AddMessage(message);}6.发送视频函数privatevoidSend_One_Capture(){//将字节数组存放到内存流中MemoryStreamms=newMemoryStream();try{//将摄像头的一帧数据存放到剪贴板中myCamera.CaptureWindow();//从剪贴板中获取图片data=Clipboard.GetDataObject();//将截图存放到内存流中if(data.GetDataPresent(typeof(System.Drawing.Bitmap))){bmap=(Image)(data.GetData(typeof(System.Drawing.Bitmap)));bmap.Save(ms,System.Drawing.Imaging.ImageFormat.Bmp);}byte[]arrImage=ms.GetBuffer();//将内存流转换为字节数组stringarray=ms.ToString();TransFile.SendVarData(client_camera,arrImage);ms.Flush();}catch(Exceptionex){MessageBox.Show("视频发送错误!");}ms.Close();}7.视频接受函数privatevoidlistentCamera(){Form1.CheckForIllegalCrossThreadCalls=false;IPAddressmyIP=Dns.GetHostAddresses(Dns.GetHostName())[0];//本机IP地址ServerMessageSocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);ServerMessageSocket.Bind(newIPEndPoint(myIP,camera_port));//监听端口ServerMessageSocket.Listen(1);Socketclient=ServerMessageSocket.Accept();message=DateTime.Now.ToString()+":对方"+client.RemoteEndPoint+"请求视频\n";AddMessage(message);//打印消息if(single){while(true){//接受视频函数的主体byte[]buffer=TransFile.ReceiveVarData(client);receiveMS=newMemoryStream(buffer);this.pictureFriend.Image=Image.FromStream(receiveMS);//显示到控件receiveMS.Close();//关闭内存流}}else{yulan=false;this.buttonSee.Text="取消预览";LocalVedioShow();Threadsendshipin=newThread(sendSP);sendshipin.Start();while(true){byte[]buffer=TransFile.ReceiveVarData(client);receiveMS=newMemoryStream(buffer);this.pictureFriend.Image=Image.FromStream(receiveMS);receiveMS.Close();}}client.Close();}四、系统测试及结果1.服务端软件启动时界面如下所示当服务端软件启动时,首先单击【开启服务】,这样客户端才能连接到服务端上。●消息发送:在消息文本框中输入要发送的消息,单击下面的【发送】即可发送消息。发送的消息会显示在上面的聊天记录文本框中。●文件传输:单击【浏览】,选择要传送的文件,文件选择完毕后单击【发送】●视频聊天:单击【本地预览】可以看到本地视频预览的效果,但客户端连接到服务端时,对方邀请后即可进行视频聊天。2.客户端软件启动时界面如下所示当服务端启动之后,客户端才可以进行相关的操作:●在“服务方IP”中输入服务端的IP地址,如果测试的是本机就直接输入本机的IP地址,软后单击【启动服务】,便可以连接到服务端,在连接到服务端之后才可以进行其他的相关操作。●消息传输:在消息文本框中输入消息,单击下面的【发送】按钮,便可以将消息发送到服务端,发送的消息显示在上面的文本框中,同时接受的消息也显示在这个文本框中,可以达到聊天的效果。●文件传输:单击【浏览】按钮,选择要发送的文件,文件的路径会显示在他旁边的文本框中,单击发送按钮便可以将文件发送到服务端。●视频传输:当客户端与服务端连接之后便可以进行视频聊天,单击【本地预览】可以进行本地视频的预览,单击【邀请聊天】后,服务端会受到客户端的视频请求,这样之后便可以进行视频聊天了。3.文字聊天的截图4.文件传输截图5.视频聊天截图五、课程设计总结通过这次课程设计,我对软件开发有了进一步的了解,把理论知识应用于实验中。也让我重新熟悉了C#语言的相关内容,加深了对C#语言知识的深化和用途的理解。通过这次的课程设计,对于TCP/IP协议,套接字的使用、摄像头的启动与应用有了很深入的理解。实验过程中也遇到了很多的问题,如文件的传输、摄像头的驱动、画面的采集等,在上面花费了较多的时间,收集了很多的资料,最总将问题顺利解决。这同时也锻炼了我的解决问题和资料收集的能力。当然,软件中还有很多其他的问题,比如软件关闭后可能会有进程没有关闭等异常情况,这将在后期的维护当中逐步解决。总之,这次的课程设计的顺利完成使我受益匪浅。六、附录课程设计源代码客户端主要程序代码publicpartialclassForm1:Form{IPAddressSendIP=null;Socketclient1;//接收和发送消息的套接字boolFlag=true;//标示是否开启服务SocketServerFileSocket;//文件套接字SocketServerMessageSocket;//消息套接字intmyport1=2050;//文件发送端口intmyport2=2051;//消息监听端口intmyport3=2052;//文件监听端口intcamera_port=2053;//视频监听端口intcamera_port1=2054;//视频监听端口2ThreadListen1;//监听文件线程ThreadListen2;//监听消息线程Threadcamera_listen;//监听视频的线程boolisshipin=false;//是否在视频聊天stringmessage;Socketclient_camera;IDataObjectdata;Imagebmap;boolsingle=true;MemoryStreamreceiveMS;WebCameramyCamera=newWebCamera();publicForm1(){InitializeComponent();}privatevoidSearch_Click(objectsender,EventArgse){if(this.openFileDialog1.ShowDialog()==DialogResult.OK){this.FilePath.Text=this.openFileDialog1.FileName;}}//////利用委托向RichTextBox中添加信息//////privatedelegatevoidAddMessageDelegate(stringmessage);publicvoidAddMessage(stringmessage){if(richTextBox1.InvokeRequired){AddMessageDelegated=AddMessage;richTextBox1.Invoke(d,message);}else{richTextBox1.AppendText(message);}}privatevoidSend_Click(objectsender,EventArgse){if(this.FilePath.Text.Length==0){MessageBox.Show("请选择文件!");return;}stringPath=this.FilePath.Text;ThreadSendFile=newThread(StartSend);SendFile.Start(this.FilePath.Text);}//发送文件函数Socketclient;publicvoidStartSend(ObjectPath){stringfile=(string)Path.ToString();stringmessage=null;try{//指向远程服务端节点IPEndPointipep=newIPEndPoint(SendIP,myport1);
{//接受主体函数
recv=s.Receive(data,total,dataleft,SocketFlags.None);
if(recv==0)
data=null;
break;
total+=recv;
dataleft-=recv;
returndata;
3.消息接受函数ListenMessage()
privatevoidListenMessage()
IPAddressmyIP=Dns.GetHostAddresses(Dns.GetHostName())[0];//获取本机的IP地址
ServerMessageSocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
ServerMessageSocket.Bind(newIPEndPoint(myIP,myport2));//绑定端口号
ServerMessageSocket.Listen(10);//等待客户链接
client=ServerMessageSocket.Accept();
IPEndPointip1=(IPEndPoint)client.RemoteEndPoint;//获取对方的IP地址
SendIP=ip1.Address;//将对方的IP地址保存到全局变量中,以后使用
message=DateTime.Now.ToString()+":
客户端"+client.RemoteEndPoint+"接入\n";
AddMessage(message);//打印提示消息
while(Flag==true)//接受对方发过来的消息
stringMessage="对方:
"+System.Text.Encoding.Unicode.GetString(TransFile.ReceiveVarData(client))+"\n";
AddMessage(Message);
4.接受文件的函数ReceiveFile
publicvoidReceiveFile(ObjectMyclient)
Socketclient=(Socket)Myclient;
stringSendFileName=System.Text.Encoding.Unicode.GetString(TransFile.ReceiveVarData(client));//获得[文件名]
stringPath=SendFileName.Substring(0,SendFileName.LastIndexOf("\\")+1);//获取目录信息
if(!
Directory.Exists(@Path))//创建目录
Directory.CreateDirectory(@Path);
else
{//如果目录已经存在则删除原有的目录信息
Directory.Delete(@Path,true);
FileStreamMyFileStream=newFileStream(SendFileName,FileMode.Create,FileAccess.Write);//创建一个新文件
intSendedCount=0;//已发送包的个数
while(true)
byte[]data=TransFile.ReceiveVarData(client);
if(data.Length==0)
SendedCount++;
//将接收到的数据包写入到文件流对象
MyFileStream.Write(data,0,data.Length);
//关闭文件流
MyFileStream.Close();
文件传输完成!
文件保存在"+SendFileName+"\n";
AddMessage(message);//打印出提示信息
5.发送文件的函数
publicvoidStartSend(ObjectPath)
stringfile=(string)Path.ToString();
stringmessage=null;
try
IPEndPointiep=newIPEndPoint(SendIP,2052);
client2=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);//创建套接字
client2.Connect(iep);//连接到发送端
FileInfoEzoneFile=newFileInfo(file);
FileStreamEzoneStream=EzoneFile.OpenRead();//打开文件流
intPacketSize=10000;//包的大小
intPacketCount=(int)(EzoneStream.Length/((long)PacketSize));//包的数量
intLastDataPacket=(int)(EzoneStream.Length-((long)(PacketSize*PacketCount)));//最后一个包的大小
stringpath="C:
\\Temp\\"+EzoneFile.Name;//发送[文件名]到客户端
TransFile.SendVarData(client2,System.Text.Encoding.Unicode.GetBytes(path));//数据包
byte[]data=newbyte[PacketSize];
//开始循环发送数据包
for(inti=0;i{//从文件流读取数据并填充数据包EzoneStream.Read(data,0,data.Length);//发送数据包TransFile.SendVarData(client2,data);}//如果还有多余的数据包,则应该发送完毕!if(LastDataPacket!=0){data=newbyte[LastDataPacket];EzoneStream.Read(data,0,data.Length);TransFile.SendVarData(client2,data);}EzoneStream.Close();client2.Shutdown(SocketShutdown.Both);}catch{MessageBox.Show("远程主机未开启!");}//关闭文件流client2.Close();message=DateTime.Now.ToString()+":文件发送完毕\n";AddMessage(message);}6.发送视频函数privatevoidSend_One_Capture(){//将字节数组存放到内存流中MemoryStreamms=newMemoryStream();try{//将摄像头的一帧数据存放到剪贴板中myCamera.CaptureWindow();//从剪贴板中获取图片data=Clipboard.GetDataObject();//将截图存放到内存流中if(data.GetDataPresent(typeof(System.Drawing.Bitmap))){bmap=(Image)(data.GetData(typeof(System.Drawing.Bitmap)));bmap.Save(ms,System.Drawing.Imaging.ImageFormat.Bmp);}byte[]arrImage=ms.GetBuffer();//将内存流转换为字节数组stringarray=ms.ToString();TransFile.SendVarData(client_camera,arrImage);ms.Flush();}catch(Exceptionex){MessageBox.Show("视频发送错误!");}ms.Close();}7.视频接受函数privatevoidlistentCamera(){Form1.CheckForIllegalCrossThreadCalls=false;IPAddressmyIP=Dns.GetHostAddresses(Dns.GetHostName())[0];//本机IP地址ServerMessageSocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);ServerMessageSocket.Bind(newIPEndPoint(myIP,camera_port));//监听端口ServerMessageSocket.Listen(1);Socketclient=ServerMessageSocket.Accept();message=DateTime.Now.ToString()+":对方"+client.RemoteEndPoint+"请求视频\n";AddMessage(message);//打印消息if(single){while(true){//接受视频函数的主体byte[]buffer=TransFile.ReceiveVarData(client);receiveMS=newMemoryStream(buffer);this.pictureFriend.Image=Image.FromStream(receiveMS);//显示到控件receiveMS.Close();//关闭内存流}}else{yulan=false;this.buttonSee.Text="取消预览";LocalVedioShow();Threadsendshipin=newThread(sendSP);sendshipin.Start();while(true){byte[]buffer=TransFile.ReceiveVarData(client);receiveMS=newMemoryStream(buffer);this.pictureFriend.Image=Image.FromStream(receiveMS);receiveMS.Close();}}client.Close();}四、系统测试及结果1.服务端软件启动时界面如下所示当服务端软件启动时,首先单击【开启服务】,这样客户端才能连接到服务端上。●消息发送:在消息文本框中输入要发送的消息,单击下面的【发送】即可发送消息。发送的消息会显示在上面的聊天记录文本框中。●文件传输:单击【浏览】,选择要传送的文件,文件选择完毕后单击【发送】●视频聊天:单击【本地预览】可以看到本地视频预览的效果,但客户端连接到服务端时,对方邀请后即可进行视频聊天。2.客户端软件启动时界面如下所示当服务端启动之后,客户端才可以进行相关的操作:●在“服务方IP”中输入服务端的IP地址,如果测试的是本机就直接输入本机的IP地址,软后单击【启动服务】,便可以连接到服务端,在连接到服务端之后才可以进行其他的相关操作。●消息传输:在消息文本框中输入消息,单击下面的【发送】按钮,便可以将消息发送到服务端,发送的消息显示在上面的文本框中,同时接受的消息也显示在这个文本框中,可以达到聊天的效果。●文件传输:单击【浏览】按钮,选择要发送的文件,文件的路径会显示在他旁边的文本框中,单击发送按钮便可以将文件发送到服务端。●视频传输:当客户端与服务端连接之后便可以进行视频聊天,单击【本地预览】可以进行本地视频的预览,单击【邀请聊天】后,服务端会受到客户端的视频请求,这样之后便可以进行视频聊天了。3.文字聊天的截图4.文件传输截图5.视频聊天截图五、课程设计总结通过这次课程设计,我对软件开发有了进一步的了解,把理论知识应用于实验中。也让我重新熟悉了C#语言的相关内容,加深了对C#语言知识的深化和用途的理解。通过这次的课程设计,对于TCP/IP协议,套接字的使用、摄像头的启动与应用有了很深入的理解。实验过程中也遇到了很多的问题,如文件的传输、摄像头的驱动、画面的采集等,在上面花费了较多的时间,收集了很多的资料,最总将问题顺利解决。这同时也锻炼了我的解决问题和资料收集的能力。当然,软件中还有很多其他的问题,比如软件关闭后可能会有进程没有关闭等异常情况,这将在后期的维护当中逐步解决。总之,这次的课程设计的顺利完成使我受益匪浅。六、附录课程设计源代码客户端主要程序代码publicpartialclassForm1:Form{IPAddressSendIP=null;Socketclient1;//接收和发送消息的套接字boolFlag=true;//标示是否开启服务SocketServerFileSocket;//文件套接字SocketServerMessageSocket;//消息套接字intmyport1=2050;//文件发送端口intmyport2=2051;//消息监听端口intmyport3=2052;//文件监听端口intcamera_port=2053;//视频监听端口intcamera_port1=2054;//视频监听端口2ThreadListen1;//监听文件线程ThreadListen2;//监听消息线程Threadcamera_listen;//监听视频的线程boolisshipin=false;//是否在视频聊天stringmessage;Socketclient_camera;IDataObjectdata;Imagebmap;boolsingle=true;MemoryStreamreceiveMS;WebCameramyCamera=newWebCamera();publicForm1(){InitializeComponent();}privatevoidSearch_Click(objectsender,EventArgse){if(this.openFileDialog1.ShowDialog()==DialogResult.OK){this.FilePath.Text=this.openFileDialog1.FileName;}}//////利用委托向RichTextBox中添加信息//////privatedelegatevoidAddMessageDelegate(stringmessage);publicvoidAddMessage(stringmessage){if(richTextBox1.InvokeRequired){AddMessageDelegated=AddMessage;richTextBox1.Invoke(d,message);}else{richTextBox1.AppendText(message);}}privatevoidSend_Click(objectsender,EventArgse){if(this.FilePath.Text.Length==0){MessageBox.Show("请选择文件!");return;}stringPath=this.FilePath.Text;ThreadSendFile=newThread(StartSend);SendFile.Start(this.FilePath.Text);}//发送文件函数Socketclient;publicvoidStartSend(ObjectPath){stringfile=(string)Path.ToString();stringmessage=null;try{//指向远程服务端节点IPEndPointipep=newIPEndPoint(SendIP,myport1);
//从文件流读取数据并填充数据包
EzoneStream.Read(data,0,data.Length);
//发送数据包
TransFile.SendVarData(client2,data);
//如果还有多余的数据包,则应该发送完毕!
if(LastDataPacket!
=0)
data=newbyte[LastDataPacket];
EzoneStream.Close();
client2.Shutdown(SocketShutdown.Both);
catch
MessageBox.Show("远程主机未开启!
");
client2.Close();
文件发送完毕\n";
AddMessage(message);
6.发送视频函数
privatevoidSend_One_Capture()
//将字节数组存放到内存流中
MemoryStreamms=newMemoryStream();
//将摄像头的一帧数据存放到剪贴板中
myCamera.CaptureWindow();
//从剪贴板中获取图片
data=Clipboard.GetDataObject();
//将截图存放到内存流中
if(data.GetDataPresent(typeof(System.Drawing.Bitmap)))
bmap=(Image)(data.GetData(typeof(System.Drawing.Bitmap)));
bmap.Save(ms,System.Drawing.Imaging.ImageFormat.Bmp);
byte[]arrImage=ms.GetBuffer();//将内存流转换为字节数组
stringarray=ms.ToString();
TransFile.SendVarData(client_camera,arrImage);
ms.Flush();
catch(Exceptionex)
MessageBox.Show("视频发送错误!
ms.Close();
7.视频接受函数
privatevoidlistentCamera()
Form1.CheckForIllegalCrossThreadCalls=false;
IPAddressmyIP=Dns.GetHostAddresses(Dns.GetHostName())[0];//本机IP地址
ServerMessageSocket.Bind(newIPEndPoint(myIP,camera_port));//监听端口
ServerMessageSocket.Listen
(1);
Socketclient=ServerMessageSocket.Accept();
对方"+client.RemoteEndPoint+"请求视频\n";
AddMessage(message);//打印消息
if(single)
{//接受视频函数的主体
byte[]buffer=TransFile.ReceiveVarData(client);
receiveMS=newMemoryStream(buffer);
this.pictureFriend.Image=Image.FromStream(receiveMS);//显示到控件
receiveMS.Close();//关闭内存流
yulan=false;
this.buttonSee.Text="取消预览";
LocalVedioShow();
Threadsendshipin=newThread(sendSP);
sendshipin.Start();
this.pictureFriend.Image=Image.FromStream(receiveMS);
receiveMS.Close();
client.Close();
四、系统测试及结果
1.服务端软件启动时界面如下所示
当服务端软件启动时,首先单击【开启服务】,这样客户端才能连接到服务端上。
●消息发送:
在消息文本框中输入要发送的消息,单击下面的【发送】即可发送消息。
发送的消息会显示在上面的聊天记录文本框中。
●文件传输:
单击【浏览】,选择要传送的文件,文件选择完毕后单击【发送】
●视频聊天:
单击【本地预览】可以看到本地视频预览的效果,但客户端连接到服务端时,对方邀请后即可进行视频聊天。
2.客户端软件启动时界面如下所示
当服务端启动之后,客户端才可以进行相关的操作:
●在“服务方IP”中输入服务端的IP地址,如果测试的是本机就直接输入本机的IP地址,软后单击【启动服务】,便可以连接到服务端,在连接到服务端之后才可以进行其他的相关操作。
●消息传输:
在消息文本框中输入消息,单击下面的【发送】按钮,便可以将消息发送到服务端,发送的消息显示在上面的文本框中,同时接受的消息也显示在这个文本框中,可以达到聊天的效果。
单击【浏览】按钮,选择要发送的文件,文件的路径会显示在他旁边的文本框中,单击发送按钮便可以将文件发送到服务端。
●视频传输:
当客户端与服务端连接之后便可以进行视频聊天,单击【本地预览】可以进行本地视频的预览,单击【邀请聊天】后,服务端会受到客户端的视频请求,这样之后便可以进行视频聊天了。
3.文字聊天的截图
4.文件传输截图
5.视频聊天截图
五、课程设计总结
通过这次课程设计,我对软件开发有了进一步的了解,把理论知识应用于实验中。
也让我重新熟悉了C#语言的相关内容,加深了对C#语言知识的深化和用途的理解。
通过这次的课程设计,对于TCP/IP协议,套接字的使用、摄像头的启动与应用有了很深入的理解。
实验过程中也遇到了很多的问题,如文件的传输、摄像头的驱动、画面的采集等,在上面花费了较多的时间,收集了很多的资料,最总将问题顺利解决。
这同时也锻炼了我的解决问题和资料收集的能力。
当然,软件中还有很多其他的问题,比如软件关闭后可能会有进程没有关闭等异常情况,这将在后期的维护当中逐步解决。
总之,这次的课程设计的顺利完成使我受益匪浅。
六、附录课程设计源代码
客户端主要程序代码
publicpartialclassForm1:
Form
IPAddressSendIP=null;
Socketclient1;//接收和发送消息的套接字
boolFlag=true;//标示是否开启服务
SocketServerFileSocket;//文件套接字
SocketServerMessageSocket;//消息套接字
intmyport1=2050;//文件发送端口
intmyport2=2051;//消息监听端口
intmyport3=2052;//文件监听端口
intcamera_port=2053;//视频监听端口
intcamera_port1=2054;//视频监听端口2
ThreadListen1;//监听文件线程
ThreadListen2;//监听消息线程
Threadcamera_listen;//监听视频的线程
boolisshipin=false;//是否在视频聊天
stringmessage;
Socketclient_camera;
IDataObjectdata;
Imagebmap;
boolsingle=true;
MemoryStreamreceiveMS;
WebCameramyCamera=newWebCamera();
publicForm1()
InitializeComponent();
privatevoidSearch_Click(objectsender,EventArgse)
if(this.openFileDialog1.ShowDialog()==DialogResult.OK)
this.FilePath.Text=this.openFileDialog1.FileName;
///
///利用委托向RichTextBox中添加信息
privatedelegatevoidAddMessageDelegate(stringmessage);
publicvoidAddMessage(stringmessage)
if(richTextBox1.InvokeRequired)
AddMessageDelegated=AddMessage;
richTextBox1.Invoke(d,message);
richTextBox1.AppendText(message);
privatevoidSend_Click(objectsender,EventArgse)
if(this.FilePath.Text.Length==0)
MessageBox.Show("请选择文件!
return;
stringPath=this.FilePath.Text;
ThreadSendFile=newThread(StartSend);
SendFile.Start(this.FilePath.Text);
//发送文件函数
Socketclient;
//指向远程服务端节点
IPEndPointipep=newIPEndPoint(SendIP,myport1);
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1