vc6局域网聊天室,支持文件传输的完成和修改

    最近马同学接到个任务,相关的是用windows的socket技术实现通信,部分要求如下:

(2) 采用TCP/IP Sock协议建立计算机间远程通讯;
(4) 实时记录传输数据;

(5) 采用多线程技术实现实时传输和数据记录;

    一、

     看要求后,决定参照东东,搞个聊天室,先实现发文件和聊天,实时更新数据下一步搞。参照视频如下:http://www.cctry.com/thread-6-1-1.html,这是syc做的,为图方便,将服务器与客户端为一体了,在他基础上,分离了服务端和客户端。

     本程序下载地址:

     http://www.cctry.com/forum.php?mod=viewthread&tid=39491&page=1#pid330149

     或 http://download.csdn.net/source/3528727 

   运行效果如下:

   

采用了skinsharp皮肤,使用方法如下:http://blog.csdn.net/akof1314/article/details/5038769,主要就是添加

#include "SkinH.h"
#pragma comment(lib,"SkinH.lib")
SkinH_Attach();
这3行,一般我喜欢添加在InitInstance中让所有对话框都是蓝色。哈哈

首先的几个框的初始化,比如ip初始化为127.0.0.1什么的,在OnInitDialog()中添加:

	SetDlgItemText(IDC_IPADDR,"127.0.0.1");
	SetDlgItemText(IDC_PORT_CLIENT,"8001"); //初始化端口和ip
	GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);
	GetDlgItem(IDC_SENDMSG)->EnableWindow(FALSE);

本程序采用多线程,在启动服务器时,启动监听文件和监听数据2个线程,代码如下:

void CServerDlg::OnStartServer() 
{
	AfxBeginThread(FileThread,this);//监听文件传输线程
	m_hListenThread=CreateThread(NULL,0,ListenThreadFunc,this,0,NULL);
	GetDlgItem(IDC_STOP_SERVER)->EnableWindow(TRUE);
	GetDlgItem(IDC_SEND)->EnableWindow(TRUE);
}
文件接收线程代码如下:(由于我们是开一个线程去监听,监听到有传送请求后才开线程接收文件,所以这里是2个线程)

struct	SAVEFILEPARAM
{
	SOCKET			sock;
	CServerDlg*	        pCLAN;
};
#define NAMELENGTH 1024
#define BUFFERSIZE 65000
	单线程接收文件线程	
UINT	SaveFileSingle(	LPVOID	param	)
{
	SAVEFILEPARAM	sp;
	sp.pCLAN=((SAVEFILEPARAM*)param)->pCLAN;
	sp.sock=((SAVEFILEPARAM*)param)->sock;

	CSocket sock;
	sock.Attach(sp.sock	);
	
	DWORD	Length;
	char	fileName[NAMELENGTH];
	CString		savePathName;
	int		modal,err;

	memset(fileName,0,NAMELENGTH);	
	err=sock.Receive(&Length,sizeof(DWORD));//接收文件长度
	if(0==err)
	{
		AfxMessageBox("连接被关闭了。");
		sock.Close();
		return	0;
	}
	if(SOCKET_ERROR==err)
	{
		AfxMessageBox("出错啦,socket的bug");
		sock.Close();
		return	0;
	}
	err=sock.Receive(fileName,NAMELENGTH);//接收文件名
	if(0==err)
	{
		AfxMessageBox("连接被关闭啦");
		sock.Close();
		return	0;
	}
	if(SOCKET_ERROR==err)
	{
		AfxMessageBox("出错啦,fuck");
		sock.Close();
		return	0;
	}
	int iret = AfxMessageBox("文件到啦,要不要",MB_OKCANCEL);
	if(	IDCANCEL==iret	)
	{
		sock.Close();
		return	0;
	}

	CFileDialog		fdlg(false,NULL,fileName);//保存文件对话框。	
	modal=fdlg.DoModal(	);
	if(IDCANCEL==modal)	
	{
		sock.Close(		);
		return	0; //用户取消保存。
	}
	savePathName=fdlg.GetPathName();
	//
	int	finish=0;
	DWORD	step=0;
	CFile	file;
	char	buffer[BUFFERSIZE];//每次收,清空buffer?

	if(	0==file.Open(savePathName,CFile::modeWrite	|CFile::modeCreate|	CFile::typeBinary)	)
	{
		AfxMessageBox("打开要写入的文件出错。");
		sock.Close();
		return	0;
	}
	//
	while(	true)
	{
		finish=sock.Receive(buffer,BUFFERSIZE);
		if(0==finish)
			break;
		if(SOCKET_ERROR==finish)
		{
			sock.Close();
			sp.pCLAN->SetFocus();
			return	0;
		}
		file.Write(buffer,finish);
		step+=finish;
	}
	
	file.Close();
	sock.Close();
	
	CString		strFinish;
	strFinish.Format("已成功接收文件\r\n\r\n%s",fileName);
	AfxMessageBox(strFinish);

	sp.pCLAN->SetFocus();

	return	0;
}
UINT FileThread(LPVOID param)
{//发文件我们固定用7000端口
	CServerDlg*	pDlg=(CServerDlg*)param;
	CSocket   fileListen;//定义监听套接字进行监听
	
	if (!AfxSocketInit())
	{
		AfxMessageBox("AfxSocketInit()失败。");
		return 0;
	}
	if(0==fileListen.Create(7000))//端口是7000啊,别忘啦
	{
		AfxMessageBox("监听套接字创建失败。");
		fileListen.Close();
		return	0;
	}
	if(0==fileListen.Listen())
	{
		AfxMessageBox("监听套接字监听失败。");
		fileListen.Close();
		return	0;
	}	
	//监听套接字创建成功。
	//监听套接字等待连接请求。
	while(	true	)
	{
		CSocket		sockClient;
		if(0==fileListen.Accept(sockClient))
		{
			AfxMessageBox("监听套接字接受服务失败。");
			fileListen.Close();
			break	;
		}
		//用线程函数来接收文件。
		/
		SAVEFILEPARAM	param;
		param.sock=sockClient.Detach();
		param.pCLAN=pDlg;
		
		AfxBeginThread(SaveFileSingle,(LPVOID)¶m);
		//

	}
	fileListen.Close();	
	return 0 ;
}
监听数据线程代码如下:(SOCKET_Selet为syc提供的线程函数,主要是封装select方法,进行异步I/O)

BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut, BOOL bRead)
{
	fd_set fdset;     //fd_set为管理多个套接字的结构体
	timeval tv;   //超时时间结构体
	FD_ZERO(&fdset);//将集合初始化为空集合
	FD_SET(hSocket, &fdset);//将套接字hSocket加入集合fdset
	nTimeOut = nTimeOut > 1000 ? 1000 : nTimeOut;
	tv.tv_sec  = 0;//超时时间---秒
	tv.tv_usec = nTimeOut;//超时时间---毫秒
	
	int iRet = 0;
	if ( bRead ) {
		iRet = select(0, &fdset, NULL , NULL, &tv);//查看服务器套接字集合是否有可读
	}else{
		iRet = select(0, NULL , &fdset, NULL, &tv);//查看服务器套接字集合是否有可写
	}
	
	if(iRet <= 0) {
		return FALSE;
	} else if (FD_ISSET(hSocket, &fdset)){   //查看套接字 hSocket是否在集合fdset中
		return TRUE;
	}
	return FALSE;
}

DWORD WINAPI ListenThreadFunc(LPVOID pParam)
{
	CServerDlg *pChatRoom = (CServerDlg *)pParam;
	ASSERT(pChatRoom != NULL);
	pChatRoom->m_ListenSock = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);
	if ( pChatRoom->m_ListenSock == INVALID_SOCKET ) {
		AfxMessageBox(_T("新建Socket失败!"));
		return FALSE;
	}
	
	int iPort = pChatRoom->GetDlgItemInt(IDC_PORT_LISTEN);
	
	sockaddr_in service;
	service.sin_family = AF_INET;
	service.sin_addr.s_addr = INADDR_ANY;
	service.sin_port = htons(iPort);
	
	if ( bind(pChatRoom->m_ListenSock, (sockaddr*)&service, sizeof(sockaddr_in)) == SOCKET_ERROR ) {
		AfxMessageBox(_T("绑定端口失败!"));
		goto __Error_End;
	}	
	if( listen(pChatRoom->m_ListenSock, 5) == SOCKET_ERROR ) {
		AfxMessageBox(_T("监听失败!"));
		goto __Error_End;
	}	
	while( TRUE) {
		if ( SOCKET_Select(pChatRoom->m_ListenSock, 100, TRUE) ) {
			sockaddr_in clientAddr;
			int iLen = sizeof(sockaddr_in);
			SOCKET accSock = accept(pChatRoom->m_ListenSock, (struct sockaddr *)&clientAddr , &iLen);
			if (accSock == INVALID_SOCKET) {
				continue;
			}
			//客户端结点
			CClientItem tItem;
			tItem.m_Socket = accSock;
			tItem.m_pMainWnd = pChatRoom;
			tItem.m_strIp = inet_ntoa(clientAddr.sin_addr);
			INT_PTR idx = pChatRoom->m_ClientArray.Add(tItem);
			tItem.hThread = CreateThread(NULL, 0, ClientThreadProc, &(pChatRoom->m_ClientArray.GetAt(idx)), CREATE_SUSPENDED, NULL);
			pChatRoom->m_ClientArray.ElementAt(idx).hThread = tItem.hThread;
			ResumeThread(tItem.hThread);//恢复一个线程的执行

			Sleep(100);
		}
	}
	
__Error_End:
	closesocket(pChatRoom->m_ListenSock);
	return TRUE;
}

#define MAX_BUF_SIZE 1024
DWORD WINAPI ClientThreadProc(LPVOID lpParameter)//客户端线程的创建
{
	CString strMsg;
	CClientItem m_ClientItem = *(CClientItem *)lpParameter;
	while( TRUE ){
		if ( SOCKET_Select(m_ClientItem.m_Socket, 100, TRUE) ) {
			TCHAR szBuf[MAX_BUF_SIZE] = {0};
			int iRet = recv(m_ClientItem.m_Socket, (char *)szBuf, MAX_BUF_SIZE, 0);
			if ( iRet > 0 ) {
				//right;
				strMsg=szBuf;
				strMsg = _T("客户端:") + m_ClientItem.m_strIp + _T(">") + strMsg;
				m_ClientItem.m_pMainWnd->ShowMsg(strMsg); //接收线程在这里显示
				m_ClientItem.m_pMainWnd->SendClientsMsg(strMsg,&m_ClientItem);//向除原信息发送客户端以外所有客户端发送信息
			}else{
				//close socket;
				strMsg = _T("客户端:") + m_ClientItem.m_strIp + _T(" 离开了聊天室!");
				m_ClientItem.m_pMainWnd->ShowMsg(strMsg);
				m_ClientItem.m_pMainWnd->RemoveClientFromArray(m_ClientItem);
				break;
			}
		}
		Sleep(500);
	}
	return TRUE;
}

由于服务端需要保存客户端队列,所以引入类CClientItem,在Inc.h中定义,并在CCserverDlg.h中包含Inc.h,在线程中监听到客户连接,并将客户连接加入CArray中。

#pragma once
class CServerDlg;
class CClientItem 
{
public:
	CString m_strIp;
	SOCKET m_Socket;
	HANDLE hThread;
	CServerDlg *m_pMainWnd;
	CClientItem()
	{
		m_pMainWnd = NULL;
		m_Socket = INVALID_SOCKET;
		hThread = NULL;
	}
};
DWORD WINAPI ListenThreadFunc(LPVOID pParam);
DWORD WINAPI ConnectThreadFunc(LPVOID pParam);//客户端连接线程
BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut=100, BOOL bRead=FALSE);
server端增加以下变量和函数:(要使用CArray需要包含头文件afxtempl.h)

	SOCKET m_ListenSock;
	HANDLE m_hListenThread;  //监听线程句柄
	CArray <CClientItem,CClientItem> m_ClientArray;//服务器接收客户端队列
	void ShowMsg(CString strMsg);  //保存聊天记录
	void RemoveClientFromArray(CClientItem in_Item);//删除客户端连接结点
	void SendClientsMsg(CString strMsg, CClientItem *pNotSend=NULL);//客户端发送消息
	void StopServer();

两个成员在CServerDlg的构造函数中的初始化如下:

	m_ListenSock=INVALID_SOCKET;//初始化
	m_hListenThread = NULL;

另外4个函数实现如下:

void CServerDlg::ShowMsg(CString strMsg)
{
	m_MsgEdit.SetSel(MAKELONG(-1, -1));//把光标定位到所有文本最后一个位置
	m_MsgEdit.ReplaceSel(strMsg+_T("\r\n"));//把光标替换一下,替换存参数括号里面的字符串
}
//删除客户端连接结点
void CServerDlg::RemoveClientFromArray(CClientItem in_Item)
{
	for( int idx = 0; idx <m_ClientArray.GetSize(); idx++ ) {
		CClientItem tItem = m_ClientArray.GetAt(idx);
		if ( tItem.m_Socket == in_Item.m_Socket &&
			tItem.hThread == in_Item.hThread &&
			tItem.m_strIp == in_Item.m_strIp ) {
			m_ClientArray.RemoveAt(idx);
		}
	}
}
void CServerDlg::SendClientsMsg(CString strMsg, CClientItem *pNotSend)//向客户端发送消息
{
    char szBuf[1024] = {0};//此处TCHAR换成char
	
    char* pchar = strMsg.GetBuffer(0); 
    strcpy(szBuf,pchar);	
    for( INT_PTR idx = 0; idx < m_ClientArray.GetSize(); idx++ ) {
		if ( !pNotSend || pNotSend->m_Socket != m_ClientArray.ElementAt(idx).m_Socket ||
			pNotSend->hThread != m_ClientArray.ElementAt(idx).hThread ||
			pNotSend->m_strIp != m_ClientArray.ElementAt(idx).m_strIp) {
			send(m_ClientArray.ElementAt(idx).m_Socket, (char *)szBuf, _tcslen(strMsg)*sizeof(char), 0);
		}
	}
}
void CServerDlg::OnSend() 
{
	CString strMsg;
	GetDlgItemText(IDC_SEVER_MSG, strMsg);

	strMsg = "服务器:>" + strMsg;
	ShowMsg(strMsg);
	SendClientsMsg(strMsg);//发送给队列中的所有客户端	
	SetDlgItemText(IDC_SEVER_MSG, _T(""));
}
void CServerDlg::OnStopServer() 
{
	int iret=MessageBox("您真的想停止吗?","",MB_OKCANCEL);
	if (iret=IDOK)
	{
		StopServer();//客户端线程的结束
		ShowMsg("停止客户端成功!");
	}
}
//服务端结束函数
void CServerDlg::StopServer()
{
	int nCount = m_ClientArray.GetSize();
	HANDLE *m_pHandles = new HANDLE[nCount+1];
	m_pHandles[0] = m_hListenThread;
	for( int idx = 0; idx < nCount; idx++ )
	{
		m_pHandles[idx+1] = m_ClientArray.ElementAt(idx).hThread;
	}
	DWORD dwRet = WaitForMultipleObjects(nCount+1, m_pHandles, TRUE, 1000);//等待多个内核对象激发的api
	if ( dwRet != WAIT_OBJECT_0 ) {
		for( INT_PTR i = 0; i < m_ClientArray.GetSize(); i++ ) {
			TerminateThread(m_ClientArray.ElementAt(i).hThread, -1);
			closesocket(m_ClientArray.ElementAt(i).m_Socket);
		}
		TerminateThread(m_hListenThread, -1);
		closesocket(m_ListenSock);
	}
	delete [] m_pHandles;
	m_hListenThread = NULL;
	m_ListenSock = INVALID_SOCKET;
}
客户端发送文件,固定通过ip控件中的地址,虽然我们初始化为127.0.0.1,发送文件固定用端口7000,于是发送函数如下:

void CClientDlg::OnSendfile() 
{
	int			modal,nCount;
	CString		fileName;
	CString     ip ;
	GetDlgItemText(IDC_IPADDR,ip);
	
	CFileDialog fdlg(true);
	modal=fdlg.DoModal(	);
	if(IDCANCEL==modal)	
	{
		return	; //用户取消发送。
	}
	
	SENDFILEPARAM*	p=new	SENDFILEPARAM;
	fileName=fdlg.GetFileName(	);
	p->pathName=fdlg.GetPathName(	);
	nCount=fileName.GetLength(	);
	memset(p->fileName,0,NAMELENGTH);
	for(int	i=0;i<nCount;i++)
	{
		p->fileName[i]=fileName.GetAt(i);
	}
	p->pCLAN=this;
	p->ip = ip;
		
	AfxBeginThread(SendFileSingle,(LPVOID)p);//调用线程函数,fuck
}
调用的发送线程以及其他结构如下:

struct	SENDFILEPARAM
{
	CString				pathName;
	CString				ip;
	char				fileName[NAMELENGTH];
	CClientDlg*	        pCLAN;
};
		单线程发送文件线程。		///
UINT	SendFileSingle(	LPVOID	param)
{
	if (!AfxSocketInit())
	{
		AfxMessageBox("SendFileSingle	AfxSocketInit()失败");
		return 0;
	}	
	CClientDlg*	pCLAN=((SENDFILEPARAM*)param)->pCLAN;	
	CString		ip;
	char		fileName[NAMELENGTH];
	char		buffer[BUFFERSIZE] = {""};
	DWORD		Length; //文件长度
	CFile		file;
	memset(fileName,0,NAMELENGTH);
	strcpy(fileName,((SENDFILEPARAM*)param)->fileName);
	
	ip=((SENDFILEPARAM*)param)->ip;
	
	if(	0==file.Open(((SENDFILEPARAM*)param)->pathName,CFile::modeRead	|	CFile::typeBinary)	)
	{
		return	0;
	}
	Length=file.GetLength(	);//获取文件长度,字节
	
	//创建发送套接字准备发送	
	CSocket		sockSend;
	if( 0==sockSend.Create() )
	{
		AfxMessageBox("创建发送套接字出错。");
		return	0;
	}
	
	if( 0==sockSend.Connect(ip,	7000) )
	{
		AfxMessageBox("猪啊,服务器都没有,你发个毛线,是吧?\r\n");
		sockSend.Close(	);
		return	0;
	}
	if(SOCKET_ERROR ==sockSend.Send(&Length,sizeof(DWORD)))//发送文件长度
	{
		AfxMessageBox("发送文件长度出错。\r\n");
		sockSend.Close(	);
		return	0;
	}
	if(SOCKET_ERROR ==sockSend.Send(fileName,NAMELENGTH))//发送文件名
	{
		AfxMessageBox("发送文件名出错。\r\n");
		sockSend.Close(	);
		return	0;
	}
	DWORD	step=0;
	int		over,err;
	
	
	//发送文件内容。
	do
	{
		file.Seek(step,CFile::begin);
		over=file.Read(buffer,BUFFERSIZE);
		err=sockSend.Send(buffer,over);
		if(	BUFFERSIZE>over	)
			break;	
		if(SOCKET_ERROR==err)
		{
			AfxMessageBox("发送文件内容出错。\r\n");			
			sockSend.Close(		);
			pCLAN->SetFocus(	);
			return	0;
		}	
		step+=err; //少了句,发错了,哈哈
		//让你丫的不断的发,日啊。。。
		///		
	}while(	true	);
	file.Close();
	sockSend.Close(	);
	
	CString		strFinish;
	strFinish.Format("已成功发送文件\r\n\r\n%s。",fileName);
	AfxMessageBox(strFinish);
	pCLAN->SetFocus();
	delete	(SENDFILEPARAM*)param;
	return	0;
}				
客户端发送,需要两个成员,一个连接进程的句柄,一个连接的socket:

	HANDLE m_hConnectThread;
	SOCKET m_ConnectSock;
并在构造函数中初始化:

	m_hConnectThread = NULL;
	m_ConnectSock = INVALID_SOCKET;
点击连接,启动进程

void CClientDlg::OnConnect() 
{
	m_hConnectThread=CreateThread(NULL,0,ConnectThreadFunc,this,0,NULL);
	GetDlgItem(IDC_DISCONNECT)->EnableWindow(TRUE);
	GetDlgItem(IDC_SENDMSG)->EnableWindow(TRUE);
}
连接线程:

BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut=100, BOOL bRead=TRUE)
{
	fd_set fdset;     //fd_set为管理多个套接字的结构体
	timeval tv;   //超时时间结构体
	FD_ZERO(&fdset);//将集合初始化为空集合
	FD_SET(hSocket, &fdset);//将套接字hSocket加入集合fdset
	nTimeOut = nTimeOut > 1000 ? 1000 : nTimeOut;
	tv.tv_sec  = 0;//超时时间---秒
	tv.tv_usec = nTimeOut;//超时时间---毫秒
	
	int iRet = 0;
	if ( bRead ) {
		iRet = select(0, &fdset, NULL , NULL, &tv);//查看服务器套接字集合是否有可读
	}else{
		iRet = select(0, NULL , &fdset, NULL, &tv);//查看服务器套接字集合是否有可写
	}
	
	if(iRet <= 0) {
		return FALSE;
	} else if (FD_ISSET(hSocket, &fdset)){   //查看套接字 hSocket是否在集合fdset中
		return TRUE;
	}
	return FALSE;
}


//客户端连接线程
#define MAX_BUF_SIZE 1024
DWORD WINAPI ConnectThreadFunc(LPVOID pParam)
{
	CClientDlg *pChatRoom = (CClientDlg *)pParam;
	ASSERT(pChatRoom != NULL);
	pChatRoom->m_ConnectSock = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);
	if ( pChatRoom->m_ConnectSock == INVALID_SOCKET ) {
		AfxMessageBox(_T("新建Socket失败!"));
		return FALSE;
	}
	CString strServIp;
	pChatRoom->GetDlgItemText(IDC_IPADDR, strServIp);
	int iPort = pChatRoom->GetDlgItemInt(IDC_PORT_CLIENT);
	if ( iPort <= 0 || iPort > 65535 ) {
		AfxMessageBox(_T("请输入合适的端口:1 - 65535"));
		goto __Error_End;
	}
	
	sockaddr_in server;
	server.sin_family = AF_INET;
	server.sin_port = htons(iPort);
	server.sin_addr.s_addr = inet_addr(strServIp);
	if ( connect(pChatRoom->m_ConnectSock, (struct sockaddr *)&server,  sizeof(struct sockaddr)) == SOCKET_ERROR ) {
		AfxMessageBox(_T("连接失败,请重试!"));
		goto __Error_End;
	}
	
	pChatRoom->GetDlgItem(IDC_DISCONNECT)->EnableWindow(TRUE);
	pChatRoom->ShowMsg(_T("系统信息:连接服务器成功!"));
	while( TRUE) {
		if ( SOCKET_Select(pChatRoom->m_ConnectSock) ) {
			char szBuf[MAX_BUF_SIZE] = {0};   //此处TCHAR改为char
			int iRet = recv(pChatRoom->m_ConnectSock, (char *)szBuf, MAX_BUF_SIZE, 0);
			if ( iRet > 0 ) {
				//right;
				pChatRoom->ShowMsg(szBuf);   //这里显示出服务器发的信息
			}else{
				//close socket;
				pChatRoom->ShowMsg(_T("服务器已停止,请重新进行连接!"));
				break;
			}
		}
		Sleep(500);
	}
	
__Error_End:
	closesocket(pChatRoom->m_ConnectSock);
	return TRUE;
}
其中ShowMsg函数与Server端定义相同。其余断开连接和发送的函数定义如下:

void CClientDlg::OnDisconnect() 
{
	int iret=MessageBox("您真的想停止吗?","",MB_OKCANCEL);
	if (iret=IDOK)
	{
		StopClient();//客户端线程的结束
		ShowMsg("停止客户端成功!");
	}
}
void CClientDlg::OnSendmsg() 
{
	CString strMsg;
	GetDlgItemText(IDC_MSG_SEND, strMsg);
	CString strTmp = _T("客户端:>") + strMsg;
	int iSend = send(m_ConnectSock, (char *)strMsg.GetBuffer(sizeof(strMsg)), strMsg.GetLength()*sizeof(TCHAR), 0);
	strMsg.ReleaseBuffer();
	SetDlgItemText(IDC_MSG_SEND, _T(""));
}
void CClientDlg::StopClient()
{
	DWORD dwRet = WaitForSingleObject(m_hConnectThread, 1000);//(WaitForSingleObject()等待某一个内核对象被激发,这个函数才返回)
	if ( dwRet != WAIT_OBJECT_0 ) {  //没正常结束线程
		TerminateThread(m_hConnectThread, -1);   //强制线程结束方法1
		closesocket(m_ConnectSock);              //强制线程结束方法2,两个都不建议使用
	}
	m_hConnectThread = NULL;
	m_ConnectSock = INVALID_SOCKET;
}
     程序目前运行良好,可以完善的地方, 在于可以加上个进度条显示文件传输啊

   

    二、 

    鉴于他们任务要求是实时获取数据,这里又没有采集设备什么的,就采取了定时器思想,对前面的程序进行了如下修改:

   1、在启动按钮时,启动定时器;

   2、添加WM_TIMER响应函数,在这里进行数据发送;

   3、监听线程中,不进行其他收发处理,只将客户socket加入CArray中;

    即,在启动按钮中添加:(此处为2s刷新一次)

	SetTimer(100,2000,0);
在OnTimer中:(这里只是随机生成3个数,需包含time.h并进行发送)
void CServerDlg::OnTimer(UINT nIDEvent) 
{
	srand(time(NULL));
	int name = rand()%1000;
	int age = rand()%90;
	int freq = rand()%80;
	CString str;
	str.Format("%d-%d*%d",name,age,freq);
	SendClientsMsg(str); //每5s发送一次ai
	CDialog::OnTimer(nIDEvent);
}
客户端进行解析并显示就好了

测试界面修改如下:


还是能显示正常的,2s刷新一次,基本可以模拟实时数据更新这一要求。

问题在于收发数据,恐怕得写个协议,以方便解析,不然都是CString,也蛋疼。


   基本搞定,就是这样了,菜鸟goes on ~~~


8-22补充:发现一个问题,在启动并结束后,再次启动server,则会提示创建套接字失败,想了想,应该是在结束线程之前关闭套接字,因为用于监听的SOCKET是在线程里创建的。于是StopServer函数的几行改为:

if ( dwRet != WAIT_OBJECT_0 ) {
		for( INT_PTR i = 0; i < m_ClientArray.GetSize(); i++ ) 
		{
			closesocket(m_ClientArray.ElementAt(i).m_Socket);
			TerminateThread(m_ClientArray.ElementAt(i).hThread, -1);			
		}
		closesocket(m_ListenSock);
		TerminateThread(m_hListenThread, -1);
	}

问题解决,不再提示失败

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值