“飞鸽传书”英文名叫IpMessager,作者是一位日本人,现在在大学的宿舍里广泛流行,至少我所在的学校几乎每位男同学的电脑里都有它,这个小巧的软件并没有采用MFC,而是纯正的SDK编程,这也正是它小的原因,当然我们不得不佩服作者Windows编程功底的深厚。
该软件的实现原理并不复杂,采用的不可靠的无连接服务,即UDP,不需要服务器,不需要添加好友,在局域网内部,当有人使用相同的软件的时候,会被局域网上其他使用该软件的人所看见,并且可以互相通信。这里有个问题,既然是无连接的通信,而且有没有服务器,那怎么才会看见其他人也在线上呢?笔者并没有看见其源码,但我猜想其过程大概如下:
首先在机器A上打开IpMessager,然后该机器会在局域网上广播,广播的内容就是:“我上线了”,并且该消息会以一定的时间间隔做定时地重复发送,这样如果机器B上也开有该软件,那么它就可以通过一定的时间来判断谁在线。
无连接的通信程序的编写相对容易,笔者不久前写了一个有连接(TCP)的局域网通信程序,我把主要代码贴出来以供大家参考,多提宝贵意见。
// MySocket.cpp : implementation file
//
#include "stdafx.h"
#include "LCHAT.h"
#include "MySocket.h"
#include "LCHATDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// MySocket
MySocket::MySocket()/
{
//WSAStartup(0x101,&WSAData);
}
MySocket::~MySocket()
{
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(MySocket, CAsyncSocket)
//{{AFX_MSG_MAP(MySocket)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/
// MySocket member functions
void MySocket::SetParenet(CDialog* pwnd)
{
m_pwnd=pwnd;
}
void MySocket::OnAccept(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
if(nErrorCode==0)
((CLCHATDlg*)m_pwnd)->OnAccept();
CAsyncSocket::OnAccept(nErrorCode);
}
void MySocket::OnClose(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
if(nErrorCode==0)
((CLCHATDlg*)m_pwnd)->OnClose();
CAsyncSocket::OnClose(nErrorCode);
}
void MySocket::OnConnect(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
if(nErrorCode==0)
((CLCHATDlg*)m_pwnd)->OnConnect();
CAsyncSocket::OnConnect(nErrorCode);
}
void MySocket::OnReceive(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
if(nErrorCode==0)
((CLCHATDlg*)m_pwnd)->OnReceive();
CAsyncSocket::OnReceive(nErrorCode);
}
void MySocket::OnSend(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
if(nErrorCode==0)
((CLCHATDlg*)m_pwnd)->OnSend();
CAsyncSocket::OnSend(nErrorCode);
}
// LCHATDlg.cpp : implementation file
//
#include "stdafx.h"
#include "LCHAT.h"
#include "LCHATDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CLCHATDlg dialog
CLCHATDlg::CLCHATDlg(CWnd* pParent /*=NULL*/)
: CDialog(CLCHATDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CLCHATDlg)
m_IP = _T("");
m_SEND = _T("");
m_STATUS = _T("");
m_PORT = 0;
m_INFO = _T("");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CLCHATDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CLCHATDlg)
DDX_Control(pDX, IDC_LIST_RECEIVE, m_RECEIVE);
DDX_Text(pDX, IDC_EDIT_IP, m_IP);
DDX_Text(pDX, IDC_EDIT_SEND, m_SEND);
DDX_Text(pDX, IDC_STATIC_STATUS, m_STATUS);
DDX_Text(pDX, IDC_EDIT_PORT, m_PORT);
DDX_Text(pDX, IDC_STATIC_INFO, m_INFO);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CLCHATDlg, CDialog)
//{{AFX_MSG_MAP(CLCHATDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON_SEND, OnButtonSend)
ON_BN_CLICKED(IDC_BUTTON_CONNECT, OnButtonConnect)
ON_BN_CLICKED(IDC_RADIO_SERVER, OnRadioServer)
ON_BN_CLICKED(IDC_RADIO_CLIENT, OnRadioClient)
ON_BN_CLICKED(IDC_BUTTON_CLOSE, OnButtonClose)
ON_BN_CLICKED(IDC_BUTTON_CLEAR, OnButtonClear)
ON_BN_CLICKED(IDC_BUTTON_ANTHOR, OnButtonAnthor)
ON_BN_CLICKED(IDC_BUTTON_FILE, OnButtonFile)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CLCHATDlg message handlers
BOOL CLCHATDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
TrayNotifyMsg(NIM_ADD, 102, "LCHAT");
// TODO: Add extra initialization here
char buf[200];
DWORD dd=200;
SYSTEMTIME time;
::GetComputerName(buf,&dd);
::GetSystemTime(&time);
WSADATA WSAData;
AfxSocketInit(&WSAData);
m_INFO.Format("机器名:%s/n/n日 期:%d年%d月%d日",buf,time.wYear,time.wMonth,time.wDay);
m_PORT=4000;
UpdateData(false);
m_listenSocket.SetParenet(this);
m_connectSocket.SetParenet(this);
isServer = false;
isFile = false;
isSendFile = false;
FileNameReady = false;
tmpsize = 0;
GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(false);
GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(false);
GetDlgItem(IDC_BUTTON_FILE)->EnableWindow(false);
return TRUE; // return TRUE unless you set the focus to a control
}
void CLCHATDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else if ((nID & 0xFFF0) == SC_MINIMIZE)
{
// 最小化时隐藏窗口
ShowWindow(0);
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CLCHATDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CLCHATDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CLCHATDlg::OnButtonSend()
{
// TODO: Add your control notification handler code here
OnSend();
}
void CLCHATDlg::OnButtonConnect()
{
// TODO: Add your control notification handler code here
UpdateData(true);
if(isServer)
{
if(m_PORT==0)
{
m_STATUS = "◆端口不能为空";
UpdateData(false);
return;
}
int error = m_listenSocket.Create(m_PORT);
if(!error)
{
m_STATUS = "◆发生一个未知错误!";
UpdateData(false);
return ;
}
m_listenSocket.Listen();
m_STATUS="◆正在侦听客户端连接...";
GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(true);
GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(false);
GetDlgItem(IDC_EDIT_IP)->EnableWindow(false);
GetDlgItem(IDC_EDIT_PORT)->EnableWindow(false);
UpdateData(false);
}
else
{
if(m_IP.IsEmpty()||m_PORT==0)
{
m_STATUS = "◆请正确填写IP地址和端口!";
UpdateData(false);
return;
}
GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(true);
GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(false);
m_STATUS="◆正在连接到服务器...";
UpdateData(false);
m_connectSocket.Create();
int error = m_connectSocket.Connect(m_IP,m_PORT);
if(!error)
{
m_STATUS = "◆发生一个未知错误!";
UpdateData(false);
return ;
}
else
{
m_STATUS="◆已经和服务器建立了连接!";
UpdateData(false);
}
GetDlgItem(IDC_BUTTON_FILE)->EnableWindow(TRUE);
GetDlgItem(IDC_EDIT_IP)->EnableWindow(false);
GetDlgItem(IDC_EDIT_PORT)->EnableWindow(false);
UpdateData(false);
}
}
void CLCHATDlg::OnButtonListence()
{
// TODO: Add your control notification handler code here
OnListence();
}
void CLCHATDlg::OnAccept()
{
int ret = m_listenSocket.Accept(m_connectSocket);
if(ret!=SOCKET_ERROR)
m_STATUS="◆已经和客户端建立了连接!";
GetDlgItem(IDC_BUTTON_FILE)->EnableWindow(TRUE);
UpdateData(false);
}
void CLCHATDlg::OnClose()
{
m_connectSocket.Close();
m_listenSocket.Close();
UpdateData(true);
m_STATUS = "◆断开连接!";
GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(true);
GetDlgItem(IDC_EDIT_IP)->EnableWindow(true);
GetDlgItem(IDC_EDIT_PORT)->EnableWindow(true);
GetDlgItem(IDC_BUTTON_FILE)->EnableWindow(false);
UpdateData(false);
}
void CLCHATDlg::OnConnect()
{
m_STATUS="◆已经和服务器建立了连接!";
GetDlgItem(IDC_BUTTON_FILE)->EnableWindow(TRUE);
UpdateData(false);
}
void CLCHATDlg::OnReceive()
{
char *pbuffer=new char[1025];
char temp[1024] ={0};
int bufferSize=1024;
CFile file;
CString str;
int iReceived=m_connectSocket.Receive(pbuffer,bufferSize);
if( iReceived==SOCKET_ERROR)
{
m_STATUS="◆接受消息失败!";
UpdateData(false);
}
if(pbuffer[0]=='#')/说明有文件进来
{
if(!FileNameReady)/还没有得到文件名
{
for(int i=1;pbuffer[i]!='*'&&i<1024;i++)
{
temp[i] = pbuffer[i];
}
temp[i++]='/0';/得到文件名
strFileName = temp;
FileNameReady = true;
isFile = true;
strcpy(temp,pbuffer+i);
lFileSize = atol(temp);
m_STATUS = strFileName;
UpdateData(false);
//CFileDialog dlg(false);
//if (dlg.DoModal() == IDOK)
//{
//strFileName = dlg.GetPathName();
//CFile file(strFileName, CFile::modeCreate | CFile::modeWrite|CFile::typeBinary);
file.Open(strFileName, CFile::modeCreate | CFile::modeWrite|CFile::typeBinary);
//}
}
}
if(FileNameReady&&isFile)
{
file.Write(pbuffer, iReceived);
tmpsize = tmpsize + iReceived;
}
/*if(tmpsize == lFileSize)
{
m_STATUS = "接收文件完成!";
UpdateData(false);
}
else
{
m_STATUS = "接收文件失败!";
UpdateData(false);
}*/
}
void CLCHATDlg::OnSend()
{
UpdateData(true);
if(isSendFile)
{
char temp[1024];
CFile file;
file.Open(strFileName, CFile::modeRead | CFile::typeBinary);
lFileSize = file.GetLength();
ltoa(lFileSize, temp, 10);
CString str;
str = "#";
str += file.GetFileName();
str += "*";
str += temp;
if(m_connectSocket.Send(LPCTSTR(str),str.GetLength()) == SOCKET_ERROR)
{
m_STATUS = "◆发送文件名失败, 请确认已经正确连接!";
UpdateData(false);
return;
}
else
{
int end = 0;
int tmp = 0;
m_STATUS = "◆正在发送文件...";
UpdateData(false);
while (1)
{
int len = file.Read(temp, 1024);
if (len == 0)
{
break;
}
m_connectSocket.Send (LPCTSTR(temp), sizeof(temp));
if (end == SOCKET_ERROR)
{
AfxMessageBox("发送线程UINT dlgSend(void *d)出错!");
break;
}
tmp += end;
//pDlg->m_progress.SetPos(tmp);
//strcpy(cent, ltoa(((tmp * 100) / lFileSize), cent, 10));
///strcat(cent, "%");
//pDlg->m_ctrCent.SetWindowText(cent);
}
file.Close();
/判断发送是否成功、结束处理
if (tmp == lFileSize)
{
m_STATUS = "◆发送文件成功!";
UpdateData(false);
}
else
{
m_STATUS = "◆发送文件失败!";
UpdateData(false);
}
//pDlg->m_progress.ShowWindow(SW_HIDE);
//cent[0] = '/0';
//pDlg->m_ctrCent.SetWindowText(cent);
//pDlg->m_ctrSpeed.SetWindowText("0");
//pDlg->m_ctrState.SetWindowText("发送文件结束");
}
}
else if(m_SEND !="")
{
if( m_connectSocket.Send (LPCTSTR(m_SEND), m_SEND.GetLength()) ==SOCKET_ERROR )
{
m_STATUS="◆发送消息失败, 请确认已经正确连接!";
UpdateData(false);
}
else
{
m_RECEIVE.AddString("●你说:"+m_SEND);
//m_RECEIVE.AddString("-------------------------------------------------------------");
m_SEND.Empty();
UpdateData(false);
}
isSendFile = false;
}
}
void CLCHATDlg::OnListence()
{
}
void CLCHATDlg::OnRadioServer()
{
// TODO: Add your control notification handler code here
isServer = true;
GetDlgItem(IDC_BUTTON_CONNECT)->SetWindowText("开始监听");
GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(true);
}
void CLCHATDlg::OnRadioClient()
{
// TODO: Add your control notification handler code here
isServer = false;
GetDlgItem(IDC_BUTTON_CONNECT)->SetWindowText("开始连接");
GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(true);
}
void CLCHATDlg::OnButtonClose()
{
// TODO: Add your control notification handler code here
OnClose();
}
void CLCHATDlg::OnButtonClear()
{
// TODO: Add your control notification handler code here
m_RECEIVE.ResetContent();
}
void CLCHATDlg::OnButtonAnthor()
{
// TODO: Add your control notification handler code here
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
void CLCHATDlg::OnButtonFile()
{
if(isFile)
{
isSendFile = false;
m_STATUS = "有文件进来!";
UpdateData(true);
}
else
{
isSendFile = true;
CFileDialog dlg(true);
if (dlg.DoModal() == IDOK)
strFileName = dlg.GetPathName();
}
}