/*-------------------------------------------------------------------- Pop.h : main header file for the POP application -----------------------------------------------------------------------*/ #if !defined(AFX_POP_H__A44B38B6_697C_11D1_881E_00001C302581__INCLUDED_) #define AFX_POP_H__A44B38B6_697C_11D1_881E_00001C302581__INCLUDED_ #define CONNECTION_CHECK 0 #define USER_CHECK 1 #define PASSWORD_CHECK 2 #define QUIT_CHECK 3 #define DELETE_CHECK 4 #define RSET_CHECK 5 #define STAT_CHECK 6 #define NOOP_CHECK 7 #define LIST_CHECK 8 #define RETR_CHECK 9 ///////////////////////////////////////////////////////////////////////////// class CPop { public: BOOL List(); CWordArray m_SizeOfMsg; CString GetErrorMessage(); // If there is any error this will return it method CString GetPassword(); // Getting Password stored in class void SetPassword(CString& Password); // Setting Password in class CString GetUser(); // Getting user name stored in class void SetUser(CString& User); // Setting user name in class CString GetHost(); // Getting Host name (email server name) stored in class void SetHost(CString& Host); // Setting Host name (email server name) in class BOOL Connect(); // Connecting to email server int GetTotalMailSize(); // it returns the Total Mail Size int GetNumberOfMails(); // It return the number of mails CString GetMsgContents(); BOOL Statistics(); // issue the STAT command on email server BOOL Retrieve(int MsgNumber); // Getting any particular mail message BOOL Reset(); // issue the reset command on email server int GetMessageSize(int MsgNumber); // Return a size of any particular mail BOOL Noop(); // issue the NOOP command on email server BOOL Disconnect(); // issue the QUIT command on email server BOOL Delete(int& MsgNumber); // Deleteing a particular message from email server BOOL Connect(CString& Host, CString& User, CString& Password); CPop(); virtual ~CPop(); private: CString m_ErrorMessage; BOOL CheckResponse(int ResponseType); CString m_Password; CString m_User; CString m_Host; CString m_MsgContents; int m_TotalSize; int m_NumberMail; CSocket m_PopServer; }; /#endif // !defined(AFX_POP_H__A44B38B6_697C_11D1_881E_00001C302581__INCLUDED_) /*----------------------------------------------------------------------------------------------- // Pop.cpp : Defines the class behaviors for the application. ---------------------------------------------------------------------------------------------------*/ #include "stdafx.h" #include "Pop.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //////////////////////////////////////////////////////////////////// // CPop Class ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CPop::CPop() { m_PopServer.Create(); } //{4EEC1C91-6BE1-11d1-8824-00001C302581} CPop::~CPop() { m_PopServer.Close(); } BOOL CPop::Connect(CString & Host, CString & User, CString & Password) { char buf [512]; if (!m_PopServer.Connect(Host,110)) // 110 Pop3 Port { m_ErrorMessage = _T("Server cannot be connected"); return FALSE; } else { if(CheckResponse(CONNECTION_CHECK)==FALSE) return FALSE; wsprintf (buf, "USER %s\r\n", (LPCSTR) User); m_PopServer.Send(buf, strlen (buf)); if(CheckResponse(USER_CHECK)==FALSE) return FALSE; wsprintf (buf, "PASS %s\r\n", (LPCSTR) Password); m_PopServer.Send(buf, strlen (buf)); if (CheckResponse(PASSWORD_CHECK)==FALSE) return FALSE; return TRUE; } } BOOL CPop::Delete(int & MsgNumber) { char buf [512]; wsprintf (buf, "DELE %d\r\n",MsgNumber ); m_PopServer.Send(buf, strlen (buf)); if (CheckResponse(DELETE_CHECK)==FALSE) return FALSE; else return TRUE; } BOOL CPop::Disconnect() { char buf [512]; wsprintf (buf, "QUIT \r\n"); m_PopServer.Send(buf, strlen (buf)); if (CheckResponse(QUIT_CHECK)==FALSE) return FALSE; else return TRUE; } BOOL CPop::Noop() { char buf [512]; wsprintf (buf, "NOOP \r\n"); m_PopServer.Send(buf, strlen (buf)); if (CheckResponse(NOOP_CHECK)==FALSE) return FALSE; else return TRUE; } // Return the Msg Size for given msg number int CPop::GetMessageSize(int MsgNumber) { if(m_SizeOfMsg.GetSize() < MsgNumber+1) return 0; else return m_SizeOfMsg[MsgNumber+1]; } BOOL CPop::Reset() { char buf [512]; wsprintf (buf, "RSET \r\n"); m_PopServer.Send(buf, strlen (buf)); if (CheckResponse(RSET_CHECK)==FALSE) return FALSE; else return TRUE; } // MsgContents will hold the msg body BOOL CPop::Retrieve(int MsgNumber) { char buf [512]; wsprintf (buf, "RETR %d\r\n",MsgNumber ); m_PopServer.Send(buf, strlen (buf)); if (CheckResponse(RETR_CHECK)==FALSE) return FALSE; else return TRUE; } BOOL CPop::Statistics() { char buf [512]; wsprintf (buf, "STAT \r\n"); m_PopServer.Send(buf, strlen (buf)); if (CheckResponse(STAT_CHECK)==FALSE) return FALSE; else return TRUE; } CString CPop::GetMsgContents() { return m_MsgContents; } int CPop::GetNumberOfMails() { return m_NumberMail; } int CPop::GetTotalMailSize() { return m_TotalSize; } BOOL CPop::Connect() { Connect(m_Host, m_User, m_Password); return TRUE; } void CPop::SetHost(CString & Host) { m_Host = Host; } CString CPop::GetHost() { return m_Host; } void CPop::SetUser(CString & User) { m_User = User; } CString CPop::GetUser() { return m_User; } void CPop::SetPassword(CString & Password) { m_Password = Password; } CString CPop::GetPassword() { return m_Password; } BOOL CPop::CheckResponse(int ResponseType) { char buf [1000]; for (int i=0;i<512;i++) buf[i]='\0'; m_PopServer.Receive(buf, sizeof(buf)); switch (ResponseType) { case CONNECTION_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Bad Connection"); return FALSE; } break; case USER_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Bad User Name"); return FALSE; } break; case PASSWORD_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Bad Password Name"); return FALSE; } break; case QUIT_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Error occured during QUIT"); return FALSE; } break; case DELETE_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Error occured during DELE"); return FALSE; } break; case RSET_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Error occured during RSET"); return FALSE; } break; case STAT_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Error occured during STAT"); return FALSE; } else { BOOL EmailNumber = TRUE; for (char *p = buf; *p != '\0'; p++) { if (*p == '\t' || *p == ' ') { if(EmailNumber == TRUE) { m_NumberMail = atoi(p); EmailNumber = FALSE; } else { m_TotalSize = atoi(p); return TRUE; } } } } break; case NOOP_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Error occured during NOOP"); return FALSE; } break; case LIST_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Error occured during LIST"); return FALSE; } else { m_PopServer.Receive(buf, sizeof(buf)); for (char *p = buf; *p != '.'; p++) if (*p == '\t' || *p == ' ') m_SizeOfMsg.Add(atoi(p)); } break; case RETR_CHECK: if (strnicmp(buf,"-ERR", 4) == 0) { m_ErrorMessage = _T("Error occured during RETR"); return FALSE; } else { char temp[9000]; m_PopServer.Receive(temp, sizeof(temp)); m_MsgContents = temp; } break; } return TRUE; } CString CPop::GetErrorMessage() { return m_ErrorMessage; } BOOL CPop::List() { char buf [512]; wsprintf (buf, "LIST \r\n"); m_PopServer.Send(buf, strlen (buf)); if (CheckResponse(LIST_CHECK)==FALSE) return FALSE; else return TRUE; } |
void OnServerOpen() //开启服务器功能 { WSADATA wsaData; int iErrorCode; char chInfo[64]; if (WSAStartup(WINSOCK_VERSION, &wsaData)) //调用Windows Sockets DLL { MessageBeep(MB_ICONSTOP); MessageBox("Winsock无法初始化!", AfxGetAppName(), MB_OK|MB_ICONSTOP); WSACleanup(); return; } else WSACleanup(); if (gethostname(chInfo, sizeof(chInfo))) { ReportWinsockErr("\n无法获取主机!\n "); return; } CString csWinsockID = "\n==>>服务器功能开启在端口:No. "; csWinsockID += itoa(m_pDoc->m_nServerPort, chInfo, 10); csWinsockID += "\n"; PrintString(csWinsockID); //在程序视图显示提示信息的函数,读者可自行创建 m_pDoc->m_hServerSocket=socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL); //创建服务器端Socket,类型为SOCK_STREAM,面向连接的通信 if (m_pDoc->m_hServerSocket == INVALID_SOCKET) { ReportWinsockErr("无法创建服务器socket!"); return;} m_pDoc->m_sockServerAddr.sin_family = AF_INET; m_pDoc->m_sockServerAddr.sin_addr.s_addr = INADDR_ANY; m_pDoc->m_sockServerAddr.sin_port = htons(m_pDoc->m_nServerPort); if (bind(m_pDoc->m_hServerSocket, (LPSOCKADDR)&m_pDoc->m_sockServerAddr, sizeof(m_pDoc->m_sockServerAddr)) == SOCKET_ERROR) //与选定的端口绑定 {ReportWinsockErr("无法绑定服务器socket!"); return;} iErrorCode=WSAAsyncSelect(m_pDoc->m_hServerSocket,m_hWnd, WM_SERVER_ACCEPT, FD_ACCEPT); //设定服务器相应的网络事件为FD_ACCEPT,即连接请求, // 产生相应传递给窗口的消息为WM_SERVER_ACCEPT if (iErrorCode == SOCKET_ERROR) { ReportWinsockErr("WSAAsyncSelect设定失败!"); return;} if (listen(m_pDoc->m_hServerSocket, QUEUE_SIZE) == SOCKET_ERROR) //开始监听客户连接请求 {ReportWinsockErr("服务器socket监听失败!"); m_pParentMenu->EnableMenuItem(ID_SERVER_OPEN, MF_ENABLED); return;} m_bServerIsOpen = TRUE; //监视服务器是否打开的变量 return; } |
LRESULT OnClientRead(WPARAM wParam, LPARAM lParam) { int iRead; int iBufferLength; int iEnd; int iRemainSpace; char chInBuffer[1024]; int i; for(i=0;(i {} if(i==MAXClient) return 0L; iBufferLength = iRemainSpace = sizeof(chInBuffer); iEnd = 0; iRemainSpace -= iEnd; iBytesRead = recv(m_aClientSocket[i], (LPSTR)(chInBuffer+iEnd), iSpaceRemaining, NO_FLAGS); //用可控缓冲接收函数recv()来接收字符 iEnd+=iRead; if (iBytesRead == SOCKET_ERROR) ReportWinsockErr("recv出错!"); chInBuffer[iEnd] = '\0'; if (lstrlen(chInBuffer) != 0) {PrintString(chInBuffer); //服务器端文字显示 OnServerBroadcast(chInBuffer); //自己编写的函数,向所有连接的客户广播这个客户的聊天文字 } return(0L); } |
void OnSocketConnect() { WSADATA wsaData; DWORD dwIPAddr; SOCKADDR_IN sockAddr; if(WSAStartup(WINSOCK_VERSION,&wsaData)) //调用Windows Sockets DLL {MessageBox("Winsock无法初始化!",NULL,MB_OK); return; } m_hSocket=socket(PF_INET,SOCK_STREAM,0); //创建面向连接的socket sockAddr.sin_family=AF_INET; //使用TCP/IP协议 sockAddr.sin_port=m_iPort; //客户端指定的IP地址 sockAddr.sin_addr.S_un.S_addr=dwIPAddr; int nConnect=connect(m_hSocket,(LPSOCKADDR)&sockAddr,sizeof(sockAddr)); //请求连接 if(nConnect) ReportWinsockErr("连接失败!"); else MessageBox("连接成功!",NULL,MB_OK); int iErrorCode=WSAAsyncSelect(m_hSocket,m_hWnd,WM_SOCKET_READ,FD_READ); //指定响应的事件,为服务器发送来字符 if(iErrorCode==SOCKET_ERROR) MessageBox("WSAAsyncSelect设定失败!"); } |
难题就是:给一个计算机的名字,或者一台计算机的IP地址,怎么样去PING它,而且得到它的响应时间。所以我们用了ICMP DLL,而对于一些SOCKET结构来说,在CSocket中已经全部都有定义了。所以,下面的类是一个CSocket的子类,它将会有更好的功能来解决更多的问题。不过你得先得到ICMPAPI.H,ICMAPI.CPP,ICMP.LIB和IPEXPORT.H,IPEXPORT.CPP这些文件,这些文件应该加在你的工程中。这些文件你可以在Microsoft Developers Network的光盘上得到,不过在微软公司的主页上也能拿到,而且更新。
类中有4个公共函数,如下:
unsigned long ResolveIP(CString strIP)
unsigned long ResolveName(CString strHostName)
CString GetIP(unsigned long ulIP)
DWORD PingHost(unsigned long ulIP, int iPingTimeout)
ResolveIP(CString strIP)函数使用一个IP地址的字符串来作为参数,返回值是IP地址值。
ResolveName(CString strHostName)函数使用一计算机名的字符串来作为参数,经过DNS或者WINS的解析,返回值是被PING计算机的IP 地址,注意它使用了GetHostByName模块化。
GetIP(unsigned long ulIP)函数是以IP地址作为参数(注意是IP地址),返回值是一个表示IP地址的字符串。
PingHost(unsigned long ulIP, int iPingTimeout)函数,第1个参数是IP地址(注意是IP地址,而不是IP地址的字符串),第2个参数是表示时间值的,表示间隔时间的。函数功能是,去PING一台计算机,返回值是PING的响应时间。
以下是代码:
//*
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//icmpecho.h
//-------------------------------------------------------------------------
//------------------------------------------------------------------------
//*
class CIcmpEcho : public CSocket
{
// Attributes
public:
// Operations
public:
CIcmpEcho();
virtual ~CIcmpEcho();
unsigned long ResolveIP(CString strIP);
unsigned long ResolveName(CString strHostName);
DWORD PingHost(unsigned long ulIP, int iPingTimeout);
CString GetIP(unsigned long ulIP);
// Overrides
public:
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CIcmpEcho)
//}}AFX_VIRTUAL
// Generated message map functions
//{{AFX_MSG(CIcmpEcho)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
// Implementation
protected:
};
/////////////////////////////////////////////////////////////////////////////
//*
//------------------------------------------------------------------------------------------- -----------------------
//icmpecho.cpp
//------------------------------------------------------------------------------------------- -----------------------
//*
// IcmpEcho.cpp : implementation file
//
#include "IcmpEcho.h"
extern "C" {
#include "ipexport.h"
#include "icmpapi.h"
}
#define PING_TIMEOUT 100
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CIcmpEcho
CIcmpEcho::CIcmpEcho()
{
}
CIcmpEcho::~CIcmpEcho()
{
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CIcmpEcho, CSocket)
//{{AFX_MSG_MAP(CIcmpEcho)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/////////////////////////////////////////////////////////////////////////////
// CIcmpEcho member functions
unsigned long CIcmpEcho::ResolveIP(CString strIP)
{
//Task 1: Given IP Address i.e. "111.111.111.111",
// Return Network byte ordered address (ulIP)
unsigned long ulIP;
ulIP =(IPAddr)inet_addr(strIP);
return ulIP;
}
unsigned long CIcmpEcho::ResolveName(CString strHostName)
{
//Task 1: Resolve HostName (through DNS or WINS, whichever appropriate)
//Task 2: Return network byte ordered address (ulIP)
unsigned long ulIP;
hostent* phostent;
phostent = gethostbyname(strHostName);
if (phostent == NULL)
return 0;
ulIP = *(DWORD*)(*phostent->h_addr_list);
return ulIP;
}
DWORD CIcmpEcho::PingHost(unsigned long ulIP, int iPingTimeout)
{
//Task 1: Open ICMP Handle
//Task 2: Create Structure to receive ping reply
//Task 3: SendPing (SendEcho)
//Task 4: Close ICMP Handle
//Task 5: Return RoundTripTime
unsigned long ip = ulIP;
HANDLE icmphandle = IcmpCreateFile();
char reply[sizeof(icmp_echo_reply)+8];
icmp_echo_reply* iep=(icmp_echo_reply*)&reply;
iep->RoundTripTime = 0xffffffff;
IcmpSendEcho(icmphandle,ip,0,0,NULL,reply,sizeof(icmp_echo_reply)+8,iPingTimeout);
IcmpCloseHandle(icmphandle);
return iep->RoundTripTime;
}
CString CIcmpEcho::GetIP(unsigned long ulIP)
{
//Task 1: Given a host order ulIP Address
// Return a IP address in format of xxx.xxx.xxx.xxx
LPSTR szAddr;
struct in_addr inetAddr;
inetAddr.s_addr = (IPAddr)ulIP;
szAddr = inet_ntoa(inetAddr);
CString csIP = szAddr;
return csIP;
}
用VC++实现上网拨号功能
现在的时代是网络的时代,网络应用程序的应用越来越广泛,在自己设计的应用程序中实现直接拨号上网,给应用程序的使用者带了很多方便,本应用程序就是在中文windows95操作系统上,用VC++5.0开发的实现拨号上网的一个小程序,希望能对大家有所帮助。下面是实现这一功能的源程序:
---- 首先创建一个以对话框为主的VC++工程,以下是本应用程序用的对话框(图略)
---- 然后在对话框这个类的头文件中加入几个新定义函数: public:
void SetupDialupCombo(void); protected: //{{AFX_MSG(CDialprocDlg) virtual BOOL OnInitDialog(); afx_msg void OnConnectbutton(); //响应对话框中BUTTON按钮“接续”的事件 afx_msg void OnSelchangeDialupCombo(); //响应对话中COMBO控件选择改变的事件 //}}AFX_MSG |
{ m_DialCombo.ResetContent(); //m_DialCombo是对话框中Combox控件的命名 DWORD n; DWORD dwSize=0; //枚举出本地电脑中的所有拨号连接。 DWORD ret=RasEnumEntries (NULL, NULL, NULL, &dwSize,&n); n=dwSize/sizeof(RASENTRYNAME); RASENTRYNAME *entry=new RASENTRYNAME[n]; if (entry) { entry[0].dwSize=sizeof(RASENTRYNAME); DWORD ret=RasEnumEntries (NULL, NULL, entry, &dwSize,&n); for (DWORD i=0;i< n;i++) { m_DialCombo.AddString(entry[i].szEntryName); } delete[] entry; } } void CDialprocDlg::OnConnectbutton() { CWaitCursor wait; RASDIALPARAMS params; memset(?ms, '\0', sizeof(params)); params.dwSize=sizeof(params); m_DialCombo.GetWindowText(params.szEntryName, sizeof(params.szEntryName)-1); strcpy(params.szPhoneNumber,""); m_UserId.GetWindowText(params.szUserName, sizeof(params.szUserName)-1); m_PasswordEdit.GetWindowText(params.szPassword, sizeof(params.szPassword)-1); //m_UserId 和m_PasswordEdit是对话框中两个Edit控件的命名 HRASCONN handle; //指定的拨号连接。 if (RasDial(NULL, NULL, ?ms, NULL, NULL, &handle)==0) { MessageBox("已联接,如果按“确定”就会切断电话"); } else { MessageBox("正在拨打的计算机没有应答,稍后请再试"); } wait.Restore(); RasHangUp(handle); } void CDialprocDlg::OnSelchangeDialupCombo() { RASDIALPARAMS params; memset(?ms, '\0',sizeof(params)); params.dwSize=sizeof(RASDIALPARAMS); int sel=m_DialCombo.GetCurSel(); if (sel >=0) { m_DialCombo.GetLBText(sel, params.szEntryName); TRACE("%s\n",params.szEntryName); BOOL p; RasGetEntryDialParams(NULL, ?ms, &p); m_UserId.SetWindowText(params.szUserName); m_PasswordEdit.SetWindowText(params.szPassword); } } BOOL CDialprocDlg::OnInitDialog() {CDialog::OnInitDialog(); SetupDialupCombo(); return TRUE; // return TRUE unless you set the focus to a control } |
1.MainFrm.cpp: …… CMainFrame::CMainFrame() { //初始化指针 m_ctrlconn=NULL; m_dataconn=NULL; m_recvconn=NULL; } //选择菜单项“快速连接” void CMainFrame::OnQuickconnect() { if(!Makeconn()) MessageBox(“FTP控制链路建立失败!”,“提示”,MB_ICONWARNING); if(!MakeRemoteDir()) MessageBox(“FTP数据链路建立失败!”,“提示”,MB_ICONWARNING); } //建立控制链路 BOOL CMainFrame::Makeconn() { …… Quickconn dlg; //输入服务器名,用户名,口令 if (dlg.DoModal()==IDOK) { fservername=dlg.m_servername; fusername=dlg.m_username; fpassword=dlg.m_password; } m_ctrlconn=new ctrlsocket(); //建立一个SOCKET If(!m_ctrlconn->Create(0,SOCK_STREAM,NULL) { delete m_ctrlconn; m_ctrlconn=NULL; MessageBox(“Socket()建立失败!”,“提示”,MB_ICONWARNING); return FALSE; } //申请网络事件通知 If(!m_ctrlconn->AsyncSelect(FD_READ|FD_WRITE|FD_ACCEPT|FD_CONNECT|FD_CLOSE)) { MessageBox(“AsyncSelect()错误!”,“提示”,MB_ICNWARNING); return FALSE; } BeginWaitCursor(); //向由fservername指定的主机发出连接请求 if(!m_ctrlconn->Connect(fservername,IPPORT_FTP)) { delete m_ctrlconn; m_ctrlconn=NULL; MessageBox(远端服务器连接失败!”,“提示”,MB_ICONWARNING); return FALSE; } EndWaitCursor(); …… return TRUE; } |
BOOL CMainFrame::MakeRemoteDir() { if(m_ctrlconn==NULL) { (“请连接到服务器!”,“提示”,MB_iconwarwing); retrurn FALSE; } if(!MakeDataListen()) { MessageBox(“数据链路建立失败!”,“提示”,MB_ICONWARNING); return FALSE; } …… return TRUE; } //处理来自远端服务器的响应 BOOL CMainFrame::Processftp() { ……… } //建立数据连接“监听”函数 BOOL CMainFrame::MakeDataListen() { …… m_dataconn=new Listensocket(); /* 建立一个SOCKET,并与本地主机地址捆绑。作为一个例子,直接插入了本机的IP地址“90.0.0.8”,而在实际应用中应首先通过相应的函数调用取得本地主机地址。 |
if(!m_dataconn->Create(0,SOCK_STREAM,“90.0.0.8”)) { delete m_dataconn; m_dataconn=NULL;MessageBox(“Socket()建立失败!”,“提示”,MB_ICONWARNING); return FALSE; } //申请网络事件通知 if(!m_dataconn->AsyncSelect(FD_ACCEPT)) { delete m_dataconn; m_dataconn=NULL; MessageBox(“AsyncSelect()错误!”,“提示”,MB_ICONWARNING); return FALSE; } …… if(!m_dataconn->Listen(5)) { MessageBox(“listen()错误!”,“提示”,MB_ICONWARNING); return FALSE; } …… } //接受数据连接请求的函数 BOOL CMainFrame::AcceptDataConn() { int num,nRet; SOCKADDR_IN RemoteDataAddr; num=sizeof(SOCKADDR_IN); if(m_recvconn==NULL) { m_recvconn=new Datasocket(); } if(!m_dataconn->Accept(*m_recvconn,(LPSOCKADDR)&RemoteDataAddr,(int FAR*)&num)) { MessageBox(“accept()错误!”,“提示”,MB_ICONWARWING); return FALSE; } …… m_dataconn->Close(); //申请网络事件通知 if(!m_recvconn->AsyncSelect(FD_READ|FD_WRITE|FD_CLOSE) { MessageBox(“Asyncselect()错误!”,“提示”,MB_ICONWARNING); return FALSE; } return TRUE; } //数据接受函数,接受来自远端服务器的数据 int CMainFrame::RecvData() { …… int nRet=m_recvconn->Receive(…); if (nRet>0) { … } else { … } …… } 2.ctrlsocket.cpp …… void ctrlsocket::OnReceive(int nErrorCode) { //处理网络事件FD_READ CSocket::OnReceive(nErrorCode); ((CmainFrame *)(AfxGetApp()->m_pMainWnd))->Processftp(); } 3.Listensocket.cpp …… void Listensocket::OnAccept(int nErrorCode) { //处理网络事件FD_ACCEPT CSocket::OnAccept(nErrorCode); ((CmainFrame*)AfxGetApp()->m_pMainWnd))->AcceptDataConn(); } 4.Datasocket.cpp …… Datasocket::OnReceive(int nErrorCode) { //处理网络事件FD_READ CAsyncSocket::OnReceive(nErrorCode); ((CMainFrame*)(AfxGetApp()->m_pMainWnd))->RecvData(); } void Datasocket::OnSend(int nErrorCode) { //处理网络事件FD_WRITE CAsyncSocket::OnSend(nErrorCode); ((CMainFrame*)(AfxGetApp()->m_pMainWnd))->SendData(); } |
extern CString bb; void CAboutDlg::OnKillFocus(CWnd* pNewWnd) { // TODO: Add your message handler code here CDialog::OnKillFocus(pNewWnd); bb=m_edit; } 对于OICQVIEW类 char aa[100]; CString mm; CDC* pdc; class mysock:public CSocket //派生mysock类,此类既有接受功能 {public:void OnReceive(int nErrorCode) //可以随时接收信息 { CSocket::Receive((void*)aa,100,0); mm=aa; CString ll=" ";//在显示消息之前,消除前面发送的消息 pdc->TextOut(50,50,ll); pdc->TextOut(50,50,mm); } }; mysock sock1; CString bb; BOOL COicqView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { CView::OnSetFocus(pOldWnd); // TODO: Add your message handler code here and/or call default bb="besting:"+bb; //确定发送者身份为besting sock1.SendTo(bb,100,1060,"192.168.0.255",0); //获得焦点以广播形式发送信息,端口号为1060 return CView::OnSetCursor(pWnd, nHitTest, message); } int COicqView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; sock1.Create(1060,SOCK_DGRAM,NULL);//以数据报形式发送消息 static CClientDC wdc(this); //获得当前视类的指针 pdc=&wdc; // TODO: Add your specialized creation code here return 0; } |
void CHelloWebExtension::Default(CHttpServerContext* pCtxt) { StartContent(pCtxt); WriteTitle(pCtxt); *pCtxt << _T("This default message was produced by the Internet"); *pCtxt << _T("Server DLL Wizard. Edit your CHelloWebExtension::Default()"); *pCtxt << _T("implementation to change it.\r\n"); EndContent(pCtxt); } |
void CHelloWebExtension::Default(CHttpServerContext* pCtxt) { StartContent(pCtxt); WriteTitle(pCtxt); *pCtxt << _T("Hello Web!"); EndContent(pCtxt); } |
typedef struct tagCOM_CONFIG { int nPort; file://端口号,从COM1到COM4 int nBaud; file://波特率,从1200bps到57600bps(对应的宏为CBR_1200到CBR_57600) int nData; file://数据位个数,7位或是8位 int nStop; file://停止位个数,可以是1位、1.5位、2位。 int nParity;//采取的校验方式,有无校验(NOPARITY)、 file://奇校验(ODDPARITY)和偶校验(EVENPARITY)等。 }COM_CONFIG; |
//以创建文件的形式打开文件,并将返回的端口句柄保存于句柄idComDev之中。 idComDev =CreateFile( g_szCom_Port[g_com_config.nPort], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); …… file://cfg为COMMCONFIG结构的实例对象,获取当前通讯口的状态。 cfg.dcb.DCBlength = sizeof( DCB ) ; GetCommState( idComDev, &(cfg.dcb) ) ; file://设置发送、接收缓存大小 SetupComm( idComDev, 4096, 4096 ) ; // PurgeComm()是一个清除函数,它可以用来中止任何未决的后台读或写,并且可以冲掉I/O file://缓冲区.其中:PURGE_TXABORT 用于中止后台写操作;PRUGE_RXABORT用于中止后台 file://读操作 ;PRUGE_TXCLEAR用于清除发送缓冲区;PRUGE_RXCLEAR用于清除接收缓冲区 PurgeComm(idComDev,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); file://根据设置的参数填充DCB结构对象dcb的各个数据成员变量 dcb.DCBlength = sizeof( DCB ) ; GetCommState( idComDev, &dcb ) ; file://设置端口通讯参数 dcb.BaudRate =g_Com_Baud[g_com_config.nBaud]; dcb.ByteSize =g_Com_ByteSize[g_com_config.nData]; dcb.Parity =g_Com_Parity[g_com_config.nParity] ; dcb.StopBits =g_Com_StopBits[g_com_config.nStop]; file://硬件流控制 dcb.fDtrControl = DTR_CONTROL_DISABLE ; dcb.fOutxCtsFlow = FALSE ; dcb.fRtsControl = RTS_CONTROL_DISABLE ; file://软件流控制 dcb.fInX = dcb.fOutX = FALSE ; dcb.XonChar = (char)0xFF ; dcb.XoffChar = (char)0XFF ; dcb.XonLim = 100 ; dcb.XoffLim = 100 ; dcb.EvtChar=0x0d; dcb.fBinary = TRUE ; dcb.fParity = TRUE ; file://超时控制的设置。超时有两种:区间超时:(仅对从端口中读取数据有用)它指定在读取两个字符之间要经历的时间;总超时: 当读或写特定的字节数需要的总时间超过某一阈值时,超时触发。计算超时可以根据公式: file://ReadTotalTimeout = (ReadTotalTimeoutMultiplier * bytes_to_read)+ // ReadToTaltimeoutConstant file://WriteTotalTimeout = (WriteTotalTimeoutMuliplier * bytes_to_write)+ // WritetoTotalTimeoutConstant file://如果在设置超时时参数为0则为无限等待,即无超时。 CommTimeOuts.ReadIntervalTimeout =MAXDWORD; CommTimeOuts.ReadTotalTimeoutMultiplier =0; CommTimeOuts.ReadTotalTimeoutConstant = 0 ; CommTimeOuts.WriteTotalTimeoutMultiplier =2*9600/dcb.BaudRate ; CommTimeOuts.WriteTotalTimeoutConstant = 25 ; SetCommTimeouts(idComDev , &CommTimeOuts ) ; file://根据设置好的dcb结构设置好通讯口的状态,并开启用于侦听端口,监视从外设传来的数 file://据的线程COMReadThreadProc。 if (SetCommState( idComDev, &dcb )) { m_bComPortOpen=TRUE; g_hCom=idComDev; AfxBeginThread(COMReadThreadProc,NULL,THREAD_PRIORITY_NORMAL); return; } |
UINT COMReadThreadProc(LPVOID pParam) { …… file://设置读端口线程执行标志的标识 g_comthread.SetReadThreadKillFlag(FALSE); while(1) { file://读取端口开启状态的标识 if(TRUE==g_comthread.GetCloseCOMFlag()) { g_comthread.SetReadThreadKillFlag(TRUE); return 0;//正常关闭 } file://读端口操作 dwNeedRead=500; file://从端口读取数据到缓存中 if(!ReadFile(g_hCom,buf,dwNeedRead,&dwActRead,NULL)) { ClearCommError(g_hCom,&dwErrorMask,&comstat); PurgeComm(g_hCom,PURGE_RXCLEAR); continue; } file://读字符加入到全局缓冲 g_comreadbuf.Add(buf,dwActRead); Sleep(1); } …… return 0; } |
BOOL CCOMReadBuf::GetOneByte(BYTE *cb) { m_Lock.Lock(); if(m_nHead==m_nTail) { m_Lock.Unlock(); return FALSE;//空 } *cb=m_readbuf[m_nTail]; if(m_nTail < m_nBufSize-1) m_nTail++; else m_nTail=0; m_Lock.Unlock(); return TRUE;//空 } void CCOMReadBuf::Add(BYTE buf[],int nBytes) { int nt,i; m_Lock.Lock(); for(i=0;i nt=(m_nHead-m_nTail); if(nt<0) nt+=m_nBufSize; if(nt+1==m_nBufSize) break;//缓冲区满 m_readbuf[m_nHead]=buf[i]; if(m_nHead < m_nBufSize-1) m_nHead++; else m_nHead=0; } m_Lock.Unlock(); } |
…… file://从对话条获取命令行 nRead=m_wndDlgBar.GetDlgItemText(IDC_EDIT_SEND,buf,500); file://向端口发送命令 if(nRead>0) { buf[nRead]=0x0d; buf[nRead+1]=0x00; ::WriteFile(g_hCom,buf,nRead+1,&dwActWrite,NULL); } …… |
…… file://获取列表视相关的列表空间的句柄 CListCtrl &ListCtrl=GetListCtrl(); file://列表有两列:收到字符的时间和对应的信息 CTime t = CTime::GetCurrentTime(); CString szTemp; szTemp.Format("%02d:%02d:%02d",t.GetHour(),t.GetMinute(),t.GetSecond()); file://向列表添加信息 int nIndex=ListCtrl.InsertItem(0, szTemp); if(-1!=nIndex) { m_Buf[m_nCurPoint]=0; ListCtrl.SetItemText(nIndex,1,LPTSTR(m_Buf)); } …… |
BOOL InitComm file://串口初始化,这里只给出关键步骤的代码,下同 { HANDLE m_hComm; COMMTIMEOUTS m_CommTimeouts; m_hComm = CreateFile("COM1", file://在这里只使用串口1 GENERIC_READ | GENERIC_WRITE, file://打开类型为可读写 0, file://以独占模式打开串口 NULL, file://不设置安全属性 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, file://重叠I/O方式 0); if (m_hComm == INVALID_HANDLE_VALUE) file://打开不成功 {return FALSE;} m_CommTimeouts.ReadIntervalTimeout = 1000; file://进行超时设置,读者应根据自己的实际需要设置 m_CommTimeouts.ReadTotalTimeoutMultiplier = 500; m_CommTimeouts.ReadTotalTimeoutConstant = 5000; m_CommTimeouts.WriteTotalTimeoutMultiplier = 500; m_CommTimeouts.WriteTotalTimeoutConstant = 5000; if (!SetCommTimeouts(m_hComm, &m_CommTimeouts)) {CloseHandle(m_hComm); return FALSE;} PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); file://清缓冲 return TRUE; } |
UINT CommThread(LPVOID pParam) file://用于监控串口的工作者线程 { BOOL bResult = FALSE; if (m_hComm) file://查看端口是否打开,这里m_hComm同上,作者在这里做了简化 PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); for (;;) file://只要线程运行,就处于监视端口行为的无限循环 { bResult = WaitCommEvent(m_hComm, &Event, &m_ov); file://m_ov是OVERLAPPED类型的成员变量 if (!bResult) { file://进行出错处理} else { Event = WaitForMultipleObjects(4, m_hEvent, FALSE, INFINITE); file://无限等待设定的事件发生,数组m_hEvent根据需要定义了须响应的接收,发送,关闭端口事件和OVERLAPPED类型的hEvent事件 switch (Event) { file://读写事件的响应处理过程,在此略} } return 0; } |
#define MESSAGELENGTH 100 class CMyDialog : public CDialog { protected: VARIANT InBuffer; VARIANT OutBuffer; CMSComm m_Com; public: ...... } BOOL CMyDiaLog::OnInitDialog() { CDialog::OnInitDialog(); m_Com.SetCommPort(1); if (!m_Com.GetPortOpen()) { m_Com.SetSettings("57600,N,8,1"); m_Com.SetPortOpen(true); m_Com.SetInBufferCount(0); SetTimer(1,10,NULL); InBuffer.bstrVal=new unsigned short[MESSAGELENGTH]; OutBuffer.bstrVal=new unsigned short[MESSAGELENGTH]; OutBuffer.vt=VT_BSTR; } return true; } void CMyDiaLog::OnTimer(UINT nIDEvent) { if (m_Com.GetInBufferCount()>=MESSAGELENGTH) { InBuffer=m_Com.GetInput(); // handle the InBuffer. // Fill the OutBuffer. m_Com.SetOutput(OutBuffer); } CDialog::OnTimer(nIDEvent); } |
typedef struct _COMMTIMEOUTS { DWORD ReadIntervalTimeout; DWORD ReadTotalTimeoutMultiplier; DWORD ReadTotalTimeoutConstant; DWORD WriteTotalTimeoutMultiplier; DWORD WriteTotalTimeoutConstant; } COMMTIMEOUTS,*LPCOMMTIMEOUTS; |
typedef struct _OVERLAPPED { DWORD Internal; DWORD InternalHigh; DWORD Offset; DWORD OffsetHigh; HANDLE hEvent; } OVERLAPPED; |
BOOL CSerial::Open( int nPort, int nBaud ) { if( m_bOpened ) return( TRUE ); char szPort[15]; DCB dcb; wsprintf( szPort, "COM%d", nPort ); m_hComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL ); if( m_hComDev == NULL ) return( FALSE ); memset( &m_OverlappedRead, 0, sizeof( OVERLAPPED ) ); memset( &m_OverlappedWrite, 0, sizeof( OVERLAPPED ) ); COMMTIMEOUTS CommTimeOuts; CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF; CommTimeOuts.ReadTotalTimeoutMultiplier = 0; CommTimeOuts.ReadTotalTimeoutConstant = 0; CommTimeOuts.WriteTotalTimeoutMultiplier = 0; CommTimeOuts.WriteTotalTimeoutConstant = 5000; SetCommTimeouts( m_hComDev, &CommTimeOuts ); m_OverlappedRead.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); m_OverlappedWrite.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); dcb.DCBlength = sizeof( DCB ); GetCommState( m_hComDev, &dcb ); dcb.BaudRate = nBaud; dcb.ByteSize = 8; if( !SetCommState( m_hComDev, &dcb ) || !SetupComm( m_hComDev, 10000, 10000 ) || m_OverlappedRead.hEvent == NULL || m_OverlappedWrite.hEvent == NULL ){ DWORD dwError = GetLastError(); if( m_OverlappedRead.hEvent != NULL ) CloseHandle( m_OverlappedRead.hEvent ); if( m_OverlappedWrite.hEvent != NULL ) CloseHandle( m_OverlappedWrite.hEvent ); CloseHandle( m_hComDev ); return FALSE; } m_bOpened = TRUE; return m_bOpened; } int CSerial::InBufferCount( void ) { if( !m_bOpened || m_hComDev == NULL ) return( 0 ); DWORD dwErrorFlags; COMSTAT ComStat; ClearCommError( m_hIDComDev, &dwErrorFlags, &ComStat ); return (int)ComStat.cbInQue; } DWORD CSerial::ReadData( void *buffer, DWORD dwBytesRead) { if( !m_bOpened || m_hComDev == NULL ) return 0; BOOL bReadStatus; DWORD dwErrorFlags; COMSTAT ComStat; ClearCommError( m_hComDev, &dwErrorFlags, &ComStat ); if( !ComStat.cbInQue ) return 0; dwBytesRead = min(dwBytesRead,(DWORD) ComStat.cbInQue); bReadStatus = ReadFile( m_hComDev, buffer, dwBytesRead, &dwBytesRead, &m_OverlappedRead ); if( !bReadStatus ){ if( GetLastError() == ERROR_IO_PENDING ){ WaitForSingleObject( m_OverlappedRead.hEvent, 2000 ); return dwBytesRead; } return 0; } return dwBytesRead; } DWORD CSerial::SendData( const char *buffer, DWORD dwBytesWritten) { if( !m_bOpened || m_hComDev == NULL ) return( 0 ); BOOL bWriteStat; bWriteStat = WriteFile( m_hComDev, buffer, dwBytesWritten, &dwBytesWritten, &m_OverlappedWrite ); if( !bWriteStat){ if ( GetLastError() == ERROR_IO_PENDING ) { WaitForSingleObject( m_OverlappedWrite.hEvent, 1000 ); return dwBytesWritten; } return 0; } return dwBytesWritten; } |
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style =WS_POPUP;//使主窗口不可见
cs.dwExStyle |=WS_EX_TOOLWINDOW;//不显示任务按钮
return CFrameWnd::PreCreateWindow(cs);
}
其次,利用系统函数Shell_NotifyIcon将一个图标显示在 任务栏的通告区中。该函数的原型为:
WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( DWORD dwMessage,PNOTIFYICONDATA pnid );
下例中被显示的是主边框窗口的图标,实际上可以显示任何图标:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
…
NOTIFYICONDATA tnd;
tnd.cbSize=sizeof(NOTIFYICONDATA);
tnd.hWnd=this-$#@62;m_hWnd;
tnd.uID=IDR_MAINFRAME;
tnd.uFlags=NIF_MESSAGE|NIF_ICON|NIF_TIP;
tnd.uCallbackMessage=WM_LIBEN;
tnd.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
strcpy(tnd.szTip,"提示信息");
Shell_NotifyIcon(NIM_ADD,&tnd);
…
}
在调用该函数之前,需要确定其参数的取值,其中之一为一个具有NOTIFYICONDATA类型的结构。其原型为:
typedef struct _NOTIFYICONDATA { // nidWORD cbSize; HWND hWnd; UINT uID; UINT uFlags; UINT uCallbackMessage; HICON hIcon; charszTip[64]; }
NOTIFYICONDATA, *PNOTIFYICONDATA;
在该结构的成员中,cbSize为该结? 占的字节数,hWnd为接受该图标所发出的消息的窗口的句柄,uID为被显示图标的ID,uFlags指明其余的几个成员(hIcon、uCallBackMessage和szTip)的值是否有效,uCallbackMessage为一个自定义的消息,当用户在该图标上作用一些鼠标动作时,将向hWnd成员中指定的窗口发出该消息,可以定义该消息为WM_USER+100。hIcon为被显示图标的句柄,szTip为一字符数组,当鼠标停留在该图标上时,将其内容显示在浮动的提示信息框中。Shell_NotifyIcon函数的另一个参数是一个预定义的消息,可以取如下值之一:NIM_ADD、NIM_DELETE或NIM_MODIFY,分别表示添加图标、删除图标 或修改图标。
最后,要与用户进行交互,也就是当用户在该图标上单击或双击鼠标左键或右键时要执行相应的操作,至少也要响应用户终止该程序的意愿。上面已经提到,当用户在图标上进行鼠标动作时,将向hWnd成员中指定的窗口发出自定义的消息,该消息由uCallbackMessage成员指定(在上例中为 WM_LIBEN,取值为WM_USER+100)。因此,我们的任务就是在hWnd窗口中响应该自定义消息:
void CMainFrame::OnLiben(WPARAM wParam,LPARAM lParam)
{
UINT uID;//发出该消息的图标的ID
UINT uMouseMsg;//鼠标动作
POINT pt;
uID=(UINT) wParam;
uMouseMsg=(UINT) lParam;
if(uMouseMsg==WM_RBUTTONDOWN)//如果是单击右键{
switch(uID)
{
case IDR_MAINFRAME://如果是我们的图标
GetCursorPos(&pt);//取得鼠标位置
…//执行相应操作
break;
…
default:
…
}}
return;
}
需要注意的是,首先要在该窗口类的头文件中给出该消息映射函数的原型说明:
afx_msg void OnLiben(WPARAM wParam,LPARAM lParam);
并且要在CPP文件中的消息映射中加入相应的条目,注意一定要加在//{{AFX_MSG_MAP(CMainFrame)和//}}AFX_MSG_MAP之外:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_COMMAND(ID_APP_EXIT, OnAppExit)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_LIBEN,OnLiben)
END_MESSAGE_MAP()
当程序结束时,需要删去通告区中的图标,这时同样应该调用Shell_NotifyIcon函数,只不过第一个参数是表示删除图标的NIM_DELETE 了:
void CMainFrame::OnAppExit()
{
// TODO: Add your command handler code here
NOTIFYICONDATA tnid;
tnid.cbSize=sizeof(NOTIFYICONDATA);
tnid.hWnd=this-$#@62;m_hWnd;
tnid.uID=IDR_MAINFRAME;//保证删除的是我们的图标
Shell_NotifyIcon(NIM_DELETE,&tnid);
AfxPostQuitMessage(0);
}
用Winsock实现语音全双工通信使用
一、引言?
Windows 95作为微机的操作系统,已经完全融入了网络与通信功能,不仅可以建立纯Windows 95环境下的“对等网络”,而且支持多种协议,如TCP/IP、IPX/SPX、NETBUI等。在TCP/IP协议组中,TPC是一种面向连接的协义,为用户提供可靠的、全双工的字节流服务,具有确认、流控制、多路复用和同步等功能,适于数据传输。UDP协议则是无连接的,每个分组都携带完整的目的地址,各分组在系统中独立传送。它不能保证分组的先后顺序,不进行分组出错的恢复与重传,因此不保证传输的可靠性,但是,它提供高传输效率的数据报服务,适于实时的语音、图像传输、广播消息等网络传输。?
Winsock接口为进程间通信提供了一种新的手段,它不但能用于同一机器中的进程之间通信,而且支持网络通信功能。随着Windows 95的推出。Winsock已经被正式集成到了Windows系统中,同时包括了16位和32位的编程接口。而Winsock的开发工具也可以在Borland C++4.0、Visual C++2.0这些C编译器中找到,主要由一个名为winsock.h的头文件和动态连接库winsock.dll或wsodk32.dll组成,这两种动态连接库分别用于Win16和Win32的应用程序。?
本文针对话音的全双工传输要求,采用UDP协议实现了实时网络通信。使用VisualC++2.0编译环境,其动态连接库名为wsock32.dll。?二、主要函数的使用要点?
通过建立双套接字,可以很方便地实现全双工网络通信。?
1.套接字建立函数:?
SOCKET socket(int family,int type,int protocol)?
对于UDP协议,写为:?
SOCKRET s;?
s=socket(AF_INET,SOCK_DGRAM,0);?
或s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)?
为了建立两个套接字,必须实现地址的重复绑定,即,当一个套接字已经绑定到某本地地址后,为了让另一个套接字重复使用该地址,必须为调用bind()函数绑定第二个套接字之前,通过函数setsockopt()为该套接字设置SO_REUSEADDR套接字选项。通过函数getsockopt()可获得套接字选项设置状态。需要注意的是,两个套接字所对应的端口号不能相同。?
此外,还涉及到套接字缓冲区的设置问题,按规定,每个区的设置范围是:不小于512个字节,大大于8k字节,根据需要,文中选用了4k字节。?
2.套接字绑定函数?
int bind(SOCKET s,struct sockaddr_in*name,int namelen)?
s是刚才创建好的套接字,name指向描述通讯对象的? 体的指针,namelen是该结构体的长度。该结构体中的分量包括:IP地址(对应name.sin_addr.s_addr)、端口号(name.sin_port)、地址类型(name.sin_family,一般都赋成AF_INET,表示是internet地址)。?
(1)IP地址的填写方法:在全双工通信中,要把用户名对应的点分表示法地址转换成32位长整数格式的IP地址,使用inet_addr()函数。?
(2)端口号是用于表示同一台计算机不同的进程(应用程序),其分配方法有两种:1)进程可以让系统为套接字自动分配一端口号,只要在调用bind前将端口号指定为0即可。由系统自动分配的端口号位于1024~5000之间,而1~1023之间的任一TCP或UDP端口都是保留的,系统不允许任一进程使用保留端口,除非其有效用户ID是零(超级用户)。?
(2)进程可为套接字指定一特定端口。这对于需要给套接字分配一众所端口的服务器是很有用的。指定范围为1024和65536之间。可任意指定。?
在本程序中,对两个套接字的端口号规定为2000和2001,前者对应发送套接字,后者对应接收套接字。?
端口号要从一个16位无符号数(u_short类型数)从主机字节顺序转换成网络字节顺序,使用
htons()函数。?
根据以上两个函数,可以给出双套接字建立与绑定的程序片断;?
//设置有关的全局变量?
SOCKET sr,ss;?
HPSTR sockBufferS,sockBufferR;?
HANDLE hSendData,hReceiveData;?
DWROD dwDataSize=1024*4;?
struct sockaddr_in therel.there2;?
#DEFINE LOCAL_HOST_ADDR 200.200.200.201?
#DEFINE REMOTE_HOST-ADDR 200.200.200.202?
#DEFINE LOCAL_HOST_PORT 2000?
#DEFINE LOCAL_HOST_PORT 2001?
//套接字建立函数?
BOOL make_skt(HWND hwnd)?
{?struct sockaddr_in here,here1;?
ss=socket(AF_INET,SOCK_DGRAM,0);?
sr=socket(AF_INET,SOCK_DGRAM,0);?
if((ss==INVALID_SOCKET)||(sr==INVALID_SOCKET))?
{?MessageBox(hwnd,“套接字建立失败!”,“”,MB_OK);?
return(FALSE);?}?
here.sin_family=AF_INET;?
here.sin_addr.s_addr=inet_addr(LOCAL_HOST_ADDR);?
here.sin_port=htons(LICAL_HOST_PORT);?
//another socket?
herel.sin_family=AF_INET;?
herel.sin_addr.s_addr(LOCAL_HOST_ADDR);?
herel.sin_port=htons(LOCAL_HOST_PORT1);?
SocketBuffer();//套接字缓冲区的锁定设置?
setsockopt(ss,SOL_SOCKET,SO_SNDBUF,(char FAR*)sockBufferS,dwDataSize);
if(bind(ss,(LPSOCKADDR)&here,sizeof(here)))
{MessageBox(hwnd,“发送套接字绑定失败!”,“”,MB_OK);
return(FALSE);}
setsockopt(sr SQL_SOCKET,SO_RCVBUF|SO_REUSEADDR,(char FAR*)
sockBufferR,dwDataSize);
if(bind(sr,(LPSOCKADDR)&here1,sizeof(here1)))
{MessageBox(hwnd,“接收套接字绑定失败!”,“”,MB_OK);
return(FALSE);}
return(TRUE);}
//套接字缓冲区设置
void sockBuffer(void)
{hSendData=GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,dwDataSize);
if(!hSendData)
{MessageBox(hwnd,“发送套接字缓冲区定位失败!”,NULL,
MB_OK|MB_ICONEXCLAMATION);
return;}
if((sockBufferS=GlobalLock(hSendData)==NULL)
{MessageBox(hwnd,“发送套接字缓冲区锁定失败!”,NULL,
MB_OK|MB_ICONEXCLAMATION);
GlobalFree(hRecordData[0];
return;}
hReceiveData=globalAlloc(GMEM_MOVEABLE|GMEM_SHARE,dwDataSize);
if(!hReceiveData)
{MessageBox(hwnd,"“接收套接字缓冲区定位败!”,NULL
MB_OK|MB_ICONEXCLAMATION);
return;}
if((sockBufferT=Globallock(hReceiveData))=NULL)
MessageBox(hwnd,"发送套接字缓冲区锁定失败!”,NULL,
MB_OK|MB_ICONEXCLAMATION);
GlobalFree(hRecordData[0]);
return;}
3.数据发送与接收函数;?
- int sendto(SOCKET s.char*buf,int len,int flags,struct sockaddr_in to,int
tolen);?
- int recvfrom(SOCKET s.char*buf,int len,int flags,struct sockaddr_in
fron,int*fromlen)其中,参数flags一般取0。?recvfrom()函数实际上是读取sendto()函数发过来的一个数据包,当读到的数据字节少于规定接收的数目时,就把数据全部接收,并返回实际接收到的字节数;当读到的数据多于规定值时,在数据报文方式下,多余的数据将被丢弃。而在流方式下,剩余的数据由下recvfrom()读出。为了发送和接收数据,必须建立数据发送缓冲区和数据接收缓冲区。规定:IP层的一个数据报最大不超过64K(含数据报头)。当缓冲区设置得过多、过大时,常因内存不够而导致套接字建立失败。在减小缓冲区后,该错误消失。经过实验,文中选用了4K字节。?
此外,还应注意这两个函数中最后参数的写法,给sendto()的最后参数是一个整数值,而recvfrom()的则是指向一整数值的指针。?
4.套接字关闭函数:closesocket(SOCKET s)?
通讯结束时,应关闭指定的套接字,以释与之相关的资源。?
在关闭套接字时,应先对锁定的各种缓冲区加以释放。其程序片断为:?
void CloseSocket(void)?
{?GlobalUnlock(hSendData);?
GlobalFree(hSenddata);?
GlobalUnlock(hReceiveData);?
GlobalFree(hReceiveDava);?
if(WSAAysncSelect(ss,hwnd,0,0)=SOCKET_ERROR)?
{?MessageBos(hwnd,“发送套接字关闭失败!”,“”,MB_OK);?
return;?}?
if(WSAAysncSelect(sr,hwnd,0,0)==SOCKET_ERROR)?
{?MessageBox(hwnd,“接收套接字关闭失败!”,“”,MB_OK);?
return;?
}?
WSACleanup();?
closesockent(ss);?
closesockent(sr);?
return;?}
}
三、Winsock的编程特点与异步选择机制?
1 阻塞及其处理方式?
在网络通讯中,由于网络拥挤或一次发送的数据量过大等原因,经常会发生交换的数据在短时间内不能传送完,收发数据的函数因此不能返回,这种现象叫做阻塞。Winsock对有可能阻塞的函数提供了两种处理方式:阻塞和非阻塞方式。在阻塞方式下,收发数据的函数在被调用后一直要到传送完毕或者出错才能返回。在阻塞期间,被阻的函数不会断调用系统函数GetMessage()来保持消息循环的正常进行。对于非阻塞方式,函数被调用后立即返回,当传送完成后由Winsock给程序发一个事先约定好的消息。?
在编程时,应尽量使用非阻塞方式。因为在阻塞方式下,用户可能会长时间的等待过程中试图关闭程序,因为消息循环还在起作用,所以程序的窗口可能被关闭,这样当函数从Winsock的动态连接库中返回时,主程序已经从内存中删除,这显然是极其危险的。?
2 异步选择函数WSAAsyncSelect()的使用?
Winsock通过WSAAsyncSelect()自动地设置套接字处于非阻塞方式。使用Windows Sockets实现Windows网络程序设计的关键就是它提供了对网络事件基于消息的异步存取,用于注册应用程序感兴趣的网络事件。它请求Windows Sockets DLL在检测到套接字上发生的网络事件时,向窗口发送一个消息。对UDP协议,这些网络事件主要为:?
- FD_READ 期望在套接字收到数据(即读准备好)时接收通知;?
- FD_WRITE 期望在套接字可发送数(即写准备好)时接收通知;?
- FD_CLOSE 期望在套接字关闭时接电通知?
消息变量wParam指示发生网络事件的套接字,变量1Param的低字节描述发生的网络事件,高字包含错误码。如在窗口函数的消息循环中均加一个分支:?
int ok=sizeof(SOCKADDR);?
case wMsg;?
switch(1Param)?
{?case FD_READ:?
//套接字上读数据?
if(recvfrom(sr.lpPlayData[j],dwDataSize,0,(struct sockaddr FAR*)&there1,
?
(int FAR*)&ok)==SOCKET_ERROR0?
{?MessageBox)hwnd,“数据接收失败!”,“”,MB_OK);?
return(FALSE);?}?
case FD_WRITE:?
//套接字上写数据?}?
break;?
在程序的编制中,应根据需要灵活地将WSAAsyncSelect()函灵敏放在相应的消息循环之中,其它说明可参见文献[1]。此外,应该指出的是,以上程序片断中的消息框主要是为程序调试方便而设置的,而在正式产品中不再出现。同时,按照程序容错误设计,应建立一个专门的容错处理函数。程序中可能出现的各种错误都将由该函数进行处理,依据错误的危害程度不同,建立几种不同的处理措施。这样,才能保证双方通话的顺利和可靠。?
四、结论?
本文是多媒体网络传输项目的重要内容之一,目前,结合硬件全双工语音卡等设备,已经成功地实现了话音的全双工的通信。有关整个多媒体传输系统设计的内容,将有另文叙述。