荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: cay (忧郁的年头), 信区: Program
标  题: 用套接口实现UDP协议的网络通信
发信站: 荔园晨风BBS站 (Thu Nov 29 07:46:24 2001), 转信


大连开发区捷通电脑技术有限公司(116600) 王淼

套接字是支持TCP/IP协议的网络通信的基本操作单元。可以将套接字看做不同主机
间的进程进行双向通信的端点。在网络编程中最常用的方案便是客户机/服务器模
型。本文主要讨论C/S模型下用套接口实现UDP协议的网络通信。利用Socket而自定
义了CSockAddr类(与地址有关的机能都封装在这里)和CBlockingSocket类(里面封
装了Socket),通过实践证明,通过使用这两个自定义类,可使应用程序非常简练、易
读。

一、Socket类型

根据传输数据类型的不同,套接字可分为面向连接的字节流套接字
(streamsockets)和无连接的数据报套接字(datagramsockets)两种类型。

1. 字节流套接字

字节流不按记录定界,在TCP/IP协议簇中对应TCP协议,即传输控制协议。它是一个
提供给用户进程可靠的全双工的面向连接的协议,大多数Internet应用程序如ftp、
telnet使用TCP协议。

2. 数据报套接字

数据报对应记录型数据流,在TCP/IP协议簇中对应UDP协议,即用户数据报协议
(UserDatagramProtocol)。由于不建立连接,数据报协议比连接协议快。但不能保
证所有数据都准确有序地到达目的地,不保证顺序性、可靠性和无重复性。它是无
连接的服务,以独立的信包进行传输,通信端点使用UDP对应的Internet地址。双方
不需互连,按固定的最大长度进行传输,因而适用于单个报文传输。

二、数据报套接字的工作过程

不论何种套接字编程,均采用客户机/服务器方式,数据报套分别生成服务进程和客
户进程,在通信前必须创建各自的套接字以建立连接,然后对相应的套接字进行"读
""写"操作,实现信息的交换。如下图无连接协议的Socket编程模型。图1

三、编程示例

利用SOCKET而自定义了CSockAddr类(与地址有关的机能都封装在里面)和CBlocking
 Socket类(里面封装了SOCKET),然后用VC++生成两个对话框工程,一个是服务器程
序SV,另一个是客户端程序CL。实践证明,通过使用这两个自定义类,可使应用程序
非常简练、易读。

以下是CSockAddr类 和CBlockingSocket类这两个类的源程序。服务器程序SV和客
户端程序CL 只列出了相关的部分。

/*** bsock.cpp :

** Block Socket class*/

// Socket地址类 只跟AF_INET对应

// 与地址有关的机能都在这里封装了。

typedef SOCKADDR *LPSOCKADDR;

class CSockAddr {

SOCKADDR_IN m_SockAddrIN;

public:

CSockAddr(LPCSTR address=NULL, int port=0, int

family=AF_INET); //构造CSockAddr

// 向LPSOCKADDR变换的运算

operator LPSOCKADDR (void) {return (LPSOCKADDR) &m_SockAddrIN;}

int GetSize(void) {return sizeof(m_SockAddrIN);} // IP情报的大小

BOOL IsIPAddress(LPCSTR address); // IP地址的检查nnn.nnn.nnn.nnn(n是
[0-9])的形式检查

// IP 地址的设定

voidSetIPAddrees(LPCSTR ddress) {m_SockAddrIN. sin_addr.s_addr=::
inet_addr(address);}

// 由主机名的IP地址的设定

BOOL SetIPAddressByHost(LPCSTR hostname)

{

struct hostent* host=gethostbyname(hostname);

if (host==NULL) {

TRACE("SERVER %s 没有找到 code=%d", hostname, WSAGetLastError());

return FALSE;

}

m_SockAddrIN.sin_addr.s_addr=*(int*)host->h_addr;

return TRUE;

}

// IP地址的文字的取得

CString GetIPString(void)

{

CString str;

 str.Format("%d.%d.%d.%d",

m_SockAddrIN.sin_addr.S_un.S_un_b.s_b1,

m_SockAddrIN.sin_addr.S_un.S_un_b.s_b2,

m_SockAddrIN.sin_addr.S_un.S_un_b.s_b3,

m_SockAddrIN.sin_addr.S_un.S_un_b.s_b4

 );

 return str;

}

int GetPort(void) { return ntohs(m_SockAddrIN.sin_port);} // PORT 的取得


void Dump(void) // DEBUG 用

{

TRACE("famiry=%d\n",m_SockAddrIN.sin_family);

TRACE("port=%d\n", ntohs(m_SockAddrIN.sin_port));

TRACE("addr=%s\n", GetIPString());

}

};

// CBlockingSocket class

class CBlockingSocket {

protected:

public:

int m_BufferPointer; // 1文字接受BUFFER

int m_BufferLength;

BYTE m_RecvBuffer[1024];

int m_ReceiveTimeout; // 收数据TIMEOUT

// SOCKET HANDLE

SOCKET m_hSocket;

CBlockingSocket(void)//构造CBlockingSocket

{

 m_ReceiveTimeout=INFINITE;

 m_hSocket=INVALID_SOCKET;

 m_BufferPointer=0;

 m_BufferLength=0;

}

~CBlockingSocket(void) {} // DESTORY

// SCOKET的作成

BOOL Socket(int nSocketType=SOCK_STREAM, int

nProtocolType=IPPROTO_IP, int

nAddressFormat=PF_INET);

BOOL Bind(CSockAddr& addr)// SCOKET的绑定

{

if (bind(m_hSocket, addr, addr.GetSize())==SOCKET_ERROR) {

TRACE("bind() error\n");

return FALSE;

} else {

return TRUE;

}

}

void Attach(SOCKET sock) {m_hSocket=sock;} //

SCOKET的HANDLE设定

void Detach(void) {m_hSocket=INVALID_SOCKET;} // SCOKET的HANDLE切断

virtual void Close();// SCOKET的CLOSE

BOOL Listen(int nConnectionBacklog=5);//连接监听

virtual BOOL Accept(CBlockingSocket& rConnectedSocket CSockAddr*
lpSockAddr=NULL);//连接认可

BOOL Connect(CSockAddr& addr)//请求连接

{

if (connect(m_hSocket, addr, addr.GetSize())==SOCKET_ERROR) {

return FALSE;

} else {

return TRUE;

}

}

// 接受数据TIMEOUT的设定

virtual void SetReceiveTimeout(int msec)

{m_ReceiveTimeout=msec;}

virtual int Receive(void* lpBuf, int nBufLen, int nFlags = 0); // 数据接


virtual int Receive(void);

// 一个文字接受

virtual int Send(const void* lpBuf, int nBufLen, int nFlags = 0); // 送


// 数据块送信

virtual int SendTo(const void* lpBuf, int nBufLen, CSockAddr& addr,
int nFlags=0)

{

return sendto(m_hSocket, (const char*)lpBuf, nBufLen, nFlags, addr,
addr.GetSize());

}

// 数据块接收

virtual int ReceiveFrom(void* lpBuf, int nBufLen, CSockAddr& addr, int
nFlags=0)

{

SOCKADDR *sa=addr;

int len=addr.GetSize();

int ret=recvfrom(m_hSocket, (char*)lpBuf, nBufLen, nFlags, sa, &len);

 return ret;

}

};

// end of file

/*** bsock.cpp :

** Block Socket class*/

#include "stdafx.h"

#include "winsock.h"

#include "bsock.h"

// SOCKADDR_IN构造体的作成CSockAddr::CSockAddr(LPCSTR address, int port,
 int family)

{

SOCKADDR_IN& sockAddr=m_SockAddrIN;

memset(&sockAddr,0,sizeof(sockAddr));

sockAddr.sin_family = AF_INET;

//

if (address == NULL) {

sockAddr.sin_addr.s_addr=htonl(INADDR_ANY);

} else {

 // IP 的数字或文字的判断

BOOL ipaddr=IsIPAddress(address);

 if (!ipaddr) {

SetIPAddressByHost(address);

} else {

SetIPAddrees(address);

}

}

sockAddr.sin_port = htons((u_short)port);

}

// IP 地址的检查// nnn.nnn.nnn.nnn(n是[0-9])的形式检查

BOOL CSockAddr::IsIPAddress(LPCSTR address)

{

BOOL name=FALSE;

for (int i=0;address[i];i++) {

if ((!isdigit(address[i]))&&address[i]!=.') {

name=TRUE;

}

}

return (!name);

}

// SOCKET 作成

BOOL CBlockingSocket::Socket(int nSocketType, int nProtocolType, int
nAddressFormat)

{

m_hSocket = socket(nAddressFormat, nSocketType, nProtocolType);

if (m_hSocket != INVALID_SOCKET)

 return TRUE;

TRACE("Socket() Error\n");

return FALSE;

}

// SOCKET 关闭

void CBlockingSocket::Close()

{

if (m_hSocket != INVALID_SOCKET) {

 closesocket(m_hSocket);

 m_hSocket = INVALID_SOCKET;

}

}

// 连接认可

BOOL CBlockingSocket::Accept(CBlockingSocket& rConnectedSocket,
CSockAddr* lpSockAddr)

{

SOCKET hTemp;

if (lpSockAddr) {

SOCKADDR* addr=*lpSockAddr;

int addrlen=lpSockAddr->GetSize();

hTemp=accept(m_hSocket, addr,&addrlen);

} else {

hTemp=accept(m_hSocket, NULL, NULL);

}

rConnectedSocket.Attach(INVALID_SOCKET);

if (hTemp == INVALID_SOCKET)

{

DWORD dwProblem = GetLastError();

TRACE("GetLastError()=%d\n", dwProblem);

SetLastError(dwProblem);

}

rConnectedSocket.Attach(hTemp);

return (hTemp != INVALID_SOCKET);

}

// 连接监听

BOOL CBlockingSocket::Listen(int nConnectionBacklog)

{

if (listen(m_hSocket, nConnectionBacklog)==SOCKET_ERROR) {

 return FALSE;

} else {

 return TRUE;

}

}

// 数据接收

int CBlockingSocket::Receive(void* lpBuf, int nBufLen, int nFlags)

{

if (m_ReceiveTimeout==INFINITE) {

return recv(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags);

} else {

fd_set rfds;

struct timeval tv;

int retval;

FD_ZERO(&rfds);

FD_SET(m_hSocket, &rfds);

//

tv.tv_sec = m_ReceiveTimeout/1000;

tv.tv_usec=(m_ReceiveTimeout % 1000)*1000;

retval=select(1,&rfds,NULL,NULL, &tv);

if (retval) {

return recv(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags);

} else {

return 0;// Timeout

}

}

}

// 一个文字接受

int CBlockingSocket::Receive()

{

int ret=0;

if (m_BufferPointer>=m_BufferLength) {

m_BufferPointer=0;

m_BufferLength=Receive((char*)m_RecvBuffer, sizeof(m_RecvBuffer));

if (m_BufferLength==0) return -1;

}

return m_RecvBuffer[m_BufferPointer++];

}

// 送信

int CBlockingSocket::Send(const void* lpBuf, int nBufLen, int nFlags)

{

return send(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags);

}

// End if file.

服务器程序SV

// SVDlg.h 中定义

HANDLE m_hThread;

static DWORD WINAPI ThreadProc(CSVDlg* dlg);

// SVDlg.CPP 加入

BOOL CSVDlg::OnInitDialog()

{

....

DWORD threadID;

m_hThread=CreateThread(NULL, 0,

(LPTHREAD_START_ROUTINE)

CSVDlg::ThreadProc,

(LPVOID)this, 0, &threadID);

return TRUE; // return TRUE unless you set the focus to a control

}

DWORD WINAPI CSVDlg::ThreadProc(CSVDlg* dlg)

{

CBlockingSocket socket;

socket.Socket(SOCK_DGRAM, IPPROTO_UDP);

CSockAddr addr(NULL, 10000);

addr.Dump();

socket.Bind(addr);

while (1) {

CSockAddr from;

char buf[128];

int len=socket.ReceiveFrom(buf, sizeof(buf), from);

if (len>0) {

buf[len]=0;

CString str;

str.Format("%s %s %d\n", buf, from.GetIPString(), from.GetPort());

::MessageBox(dlg->m_hWnd,str,"SV",MB_OK);

socket.SendTo(buf, len, from);

} else

break;

}

socket.Close();

return 0;

}

客户端程序CL 中加入:

void CCLDlg::OnSendButton()

{

CString ip;

m_IPEdit.GetWindowText(ip);

CBlockingSocket socket;

socket.Socket(SOCK_DGRAM, IPPROTO_UDP);

CSockAddr addr(NULL, 10001);

socket.Bind(addr);

CSockAddr sendaddr(ip, 10000);

socket.SendTo("test", 4, sendaddr);

char buf[128];

int len=socket.ReceiveFrom(buf, sizeof(buf), addr);

buf[len]=0;

CString str;

str.Format("%s %s %d\n", buf, addr.GetIPString(), addr.GetPort());

MessageBox(str, "CL", MB_OK);

socket.Close();

}

程序在VC++ 6.0 下编译通过,在使用 TCP/IP 协议的 Windows 95/98对等局域网和
使用 TCP/IP 协议的 Windows NT 局域网上运行良好。

--

        放弃是一件最容易的事

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.1.168]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店