由“飞鸽传书”所想到的--------利用WinSocket写局域网聊天程序(转载请注明出处)

来源:互联网 发布:linux虚拟机配置ip 编辑:程序博客网 时间:2024/05/04 22:39


      “飞鸽传书”英文名叫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();
 }
}

原创粉丝点击