免费socket详细资料.docx
《免费socket详细资料.docx》由会员分享,可在线阅读,更多相关《免费socket详细资料.docx(51页珍藏版)》请在冰豆网上搜索。
免费socket详细资料
使用VC++的网络编程总结
Whj20110906
目录:
1.套接字编程原理
1.1Client/server通信模型
1.2WindowsSockets规范
1.3套接字
1.3.1套接字定义
1.3.2分类
1.3.3套接字的作用
1.3.4端口与地址
1.3.5套接口属性
2.基本的WindowsSocketsAPI编程
2.1常用函数
2.2TCP实例
2.3UDP实例
2.4Socket通信阻塞的解决方法
3.MFC下的Socket编程的类
3.1CAsyncSocket类
3.2CSocket类
3.3WindowsSockets:
带存档的套接字的工作方式
3.4流式套接字通信的操作顺序
3.5使用CAsyncSocket类
3.6从套接字类派生
3.7套接字通知
3.8一个使用CSocket类的网络通信实例
3.8.1服务器端应用程序设计(ServerDemo)
3.8.2客户端应用程序设计(项目名称ClientDemo)
4.套接字的托管实现
4.1System:
:
Net:
:
Sockets命名空间
4.2实例:
一个新邮件检查器
1.套接字编程原理
一个完整的网间通信进程需要由两个进程组成,并且只能用同一种高层协议。
也就是说,不可能通信的一端用TCP,而另一端用UDP。
一个完整的网络信需要一个五元组来标识:
协议、本地地址、本地端口号、远端地址、远端端口号。
1.1Client/server通信模型
在客户/服务器模式中我们将请求服务的一方称为客户(client),将提供某种服务的一方称为服务器(server)。
一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。
在这个时刻,服务程序被“惊醒”并且为客户提供服务—对客户的请求作出适当的反应。
虽然基于连接的服务是设计客户机/服务器应用程序时的标准,但有些服务也是可以通过无连接的接口提供的。
客户机/服务器的请求/响应过程示意图如下所示。
图1客户/服务器通信模型
通过上面的分析,我们不难理解一个一个完整的网络应用程序包括客户端和服务器两个部分。
客户与服务器进程的作用是非对称的,因此编码不同。
服务进程一般是等待客户请求而启动的,只要系统运行,该服务进程一直存在,直到终止或强迫终止。
1.2WindowsSockets规范
WindowsSockets规范是90年代初Microsoft公司联合其他几家大公司共同制定的一套在Windows下的二进制兼容网络编程接口规范。
它以U.C.Berkeley大学BSDUNIX中流行的Socket接口为基础,主要在其上扩充了一组针对Windows的扩展库函数,增加了符合Windows消息驱动特性的网络事件异步选择机制,以使程序员能够充分利用Windows消息驱动机制进行编程。
WindowsSockets的用途是将基础网络抽象出来,这样,您不必对网络非常了解,并且您的应用程序可在任何支持套接字的网络上运行。
它为应用程序开发者定义了一套简单统一的API,并让各家网络软件供应商共同遵守。
WindowsSockets规范从90年代初的1.0版本开始,经过不断的完善和发展,目前已经有了WindowsSockets2版本。
值得注意的是,Microsoft的MFC库现在只支持WindowsSockets1版本,不支持WindowsSockets2版本。
MFC提供了两个类用以封装WindowsSocketsAPI。
一个是CAsyncSocket类,它主要是提供给那些具有一定网络编程经验,希望同时拥有SocketAPI编程的灵活性和类库编程便利性的开发者的。
另一个是CSocket类,它由CAsyncSocket类派生,它具有更高的抽象化,致力于简化网络编程所需的操作。
1.3套接字
1.3.1套接字定义
套接字是一个通信终结点,它是Sockets应用程序用来在网络上发送或接收数据包的对象。
套接字具有类型,与正在运行的进程相关联,并且可以有名称。
目前,套接字一般只与使用网际协议组的同一“通信域”中的其他套接字交换数据。
使用套接字的应用程序间通信模型如图2所示。
1.3.2分类
可用的套接字类型有以下两种:
1.3.2.1流式套接字
流式套接字提供没有记录边界的数据流,即字节流。
字节流能确保以正确的顺序无重复地被送达。
1.3.2.2数据报套接字
数据报套接字支持面向记录的数据流,但不能确保能被送达,也无法确保按照发送顺序或不重复。
“有序”指数据包按发送的顺序送达。
“不重复”指一个特定的数据包只能获取一次。
这两种套接字都是双向的,是可以同时在两个方向上(全双工)进行通信的数据流。
注意 在某些网络协议下(如XNS),流可以面向记录,即作为记录流而非字节流。
但在更常用的TCP/IP协议下,流为字节流。
WindowsSockets提供与基础协议无关的抽象化级别。
1.3.3套接字的作用
套接字的作用非常大,至少在下面三种通信上下文中如此:
●客户端/服务器模型。
●对等网络方案,如聊天应用程序。
●通过让接收应用程序将消息解释为函数调用来进行远程过程调用(RPC)。
1.3.4端口与地址
在网络上,一个套接字的标识主要借助于地址和端口来描述。
套接字的地址指该套接字所在计算机的网络地址,可以为域名或IP地址的形式。
通常,创建套接字时不必指明网络地址,只有在拥有多个网络地址的机器时,才需要显式指定一个网络地址。
同一机器上可以运行多个网络应用程序,每个应用程序都有自己的套接字用以进行网络通信,此时如果只有地址标识套接字,则当一个通信包到达机器时,将无法确定究竟是哪个应用程序的套接字需要接收此信息。
由此增加了端口的概念,以协助区分同一机器上不同应用程序的套接字。
端口用于标识进程,同一机器上不同的网络应用程序各有不同的端口,这样,通过“网络地址+端口号”的标识方法,便唯一标识了机器上的应用程序了。
某些端口是专门为公共服务保留的(Ftp:
21,http:
80),除非程序是要提供这些服务,否则应尽量避免使用这些端口。
一般来说,端口1024以前的端口号都是系统保留的或是作为公共服务的,应尽量选择大于1024的端口号,以避免冲突。
1.3.5套接口属性
套接口有一系列的属性用于标识套接口的状态等信息,它们的属性如表1所示。
表1套接口属性
选项
类型
含义
缺省值
SO_ACCEPTCON
BOOL
套接口正在监听
FALSE
SO_BROADCAST
BOOL
套接口设置为可发送广播数据
FALSE
SO_DEBUG
BOOL
允许Debug
FALSE
SO_DONTLINGER
BOOL
是否禁止SO_LINGER选项
TRUE
SO_DONTROUTE
BOOL
路由被禁止
FALSE
SO_ERROR
int
得到并且清除错误状态
0
SO_KEEPALIVE
BOOL
活跃信息正在被发送
FALSE
SO_LINGER
struct
返回目前的linger信息
1_onoff
SO_OOBINLINE
BOOL
带外数据正在普通数据流中被接收
FALSE
SO_RCVBUF
int
接收缓冲区大小
与具体实现有关
SO_REUSEADDR
BOOL
该套接口捆绑的地址可否被他人使用
FALSE
SO_SNDBUF
int
发送缓冲区大小
与具体实现有关
SO_TYPE
int
套接口类型
与接口类型有关
TCP_NODELAY
BOOL
禁止采用Nagle进行合并传送
与具体实现有关
可以通过getsockopt()函数获取套接口的属性,也可以通过setsockopt()函数设置套接口的属性。
2.基本的WindowsSocketsAPI编程
●需要在程序中添加下面的包含语句:
#include
●使用vc++编译时需添加编译链接依赖项ws2_32.lib库
●协议寻址
在winsock中,应用程序通过sockaddr_in结构来指定IP地址和服务端口信息
sockaddr_ininternetAddr;
intnPortID=5320;
internetAddr.sin_family=AF_INET;
internet.sin_addr.s_addr=inet_addr(“202.202.42.88”);//INADDR_ANY
internet.sin_port=htons(nPortID);
ip地址不容易记忆,还提供了许多地址和名称解析函数如gethostbyname,gethostbyaddr等。
2.1常用函数
1)WSAStartup调用windowsSocketDLL
函数原型intWSAStartup(
WORDwVersionRequested,//应用程序要求的sockets版本
LPWSADATAlpWSAData//指向数据结构WSDATA的指针,
//得到windowsSocket的具体信息
);
WSDATA定义如下:
typedefstructWSAData{
WORDwVersion;
WORDwHighVersion;
#ifdef_WIN64
unsignedshortiMaxSockets;
unsignedshortiMaxUdpDg;
charFAR*lpVendorInfo;
charszDescription[WSADESCRIPTION_LEN+1];
charszSystemStatus[WSASYS_STATUS_LEN+1];
#else
charszDescription[WSADESCRIPTION_LEN+1];
charszSystemStatus[WSASYS_STATUS_LEN+1];
unsignedshortiMaxSockets;
unsignedshortiMaxUdpDg;
charFAR*lpVendorInfo;
#endif
}WSADATA,FAR*LPWSADATA;
2)WSACleanup结束对WindowsSocketsDLL的调用
函数原型:
intWSACleanup(void);
3)socket用于建立Sockets。
函数原型:
SOCKETsocket(
intaf,//地址族,一般是AF_INET
inttype,//socket类型,SOCK_STREAM或SOCK_DGRAM
intprotocol//协议类型,通常取值0
);
4)closesocket关闭套接字
函数原型:
intclosesocket(
SOCKETs//要关闭的套接字
);
5)bind将一个本地地址和一个SOCKET描述字连接起来
函数原型:
intbind(
SOCKETs,//要绑定的套接字
conststructsockaddrFAR*name,//指向SOCKADDR结构的地址
intnamelen//地址结构的sizeof
)
Tcp/ipSOCKADDR结构
structsockaddr{
unsignedshortsa_family;
charsa_data[4];
};
structsockaddr_in{
shortsin_family;
unsignedshortsin_port;
structin_addrsin_addr;
charsin_zero[8];
};
6)listen设定socket为监听状态
函数原型:
intlisten(
SOCKETs,//进行监听的socket
intbacklog//客户端可以连接的请求个数
);
7)accept接受一个socket的连接请求,同时返回一个新的socket,新的socket用来在服务器与客户端之间传递和接收信息。
函数原型:
SOCKETaccept(
SOCKETs,//处于监听状态的socket
structsockaddrFAR*addr,//将要接受地址的sockaddr指针
intFAR*addrlen//地址的长度
);
8)connect连接客户端的socket到指定的网络服务器。
连接成功后,客户端用此socket与服务器通信。
函数原型:
intconnect(
SOCKETs,//将要连接的socket
conststructsockaddrFAR*name,//目标socket地址
intnamelen//地址结构sizeof
);
9)recv用于接收已经建立连接的socket数据信息
函数原型:
intrecv(
SOCKETs,
charFAR*buf,//接收数据缓冲区
intlen,//缓冲区长度
intflags//有MSG_PEEK和MSG_OOB
);
返回值:
接收到的字节数
10)send对已经建立连接的socket发送数据信息
函数原型:
intsend(
SOCKETs,
charFAR*buf,//发送数据缓冲区
intlen,//缓冲区长度
intflags//有MSG_PEEK和MSG_OOB
);
返回值:
发送的字节数
11)WSAAsyncSelect要求socket在有事件发生时通知使用者,本函数将套接口设置成为非阻塞方式。
函数原型:
intWSAAsyncSelect(
SOCKETs,
HWNDhWnd,//接收网络事件的窗口句柄
unsignedintwMsg,//发送给窗口的网络事件消息
longlEvent//网络消息
);
12)sendto向目标地址发送数据信息
intsendto(
SOCKETs,
constcharFAR*buf,
intlen,
intflags,
conststructsockaddrFAR*to,
inttolen
);
13)recvfrom接收目标地址传来的数据信息
intrecvfrom(
INSOCKETs,
OUTcharFAR*buf,
INintlen,
INintflags,
OUTstructsockaddrFAR*from,
INOUTintFAR*fromlen
);
2.2TCP实例
服务器端需要建立两个套接字,一个用于监听连接请求,另一个用来与请求连接的套接字建立连接,实际的数据传送是通过后一个套接字。
而客户端只需要一个套接字即可。
客户端代码实例(命令行)
服务器端代码实例(命令行)
#include
#include
voidmain()
{
SOCKETsock;
structsockaddr_insa;
interr;
intservport=5555;
charbuff[256];
intlen;
WSADATAws;
//初始化Winsock
if(WSAStartup(0x0101,&ws)!
=0)
{
printf("WSAStartup()failed!
\n");
exit(-1);
}
//创建套接字
sock=socket(AF_INET,SOCK_STREAM,0);
//定义服务器地址结构
memset(&sa,0,sizeof(sa));
sa.sin_family=AF_INET;
sa.sin_port=htons(servport);sa.sin_addr.s_addr=inet_addr("202.202.42.88");
//连接服务器
err=connect(sock,(constsockaddr*)&sa,sizeof(sa));
//接收欢迎词
memset(buff,0,sizeof(buff));
len=recv(sock,buff,sizeof(buff),0);
printf("%s\n",buff);
//关闭连接
closesocket(sock);
WSACleanup();
}
#include
#include
voidmain()
{
SOCKETservsock,clisock;
structsockaddr_insa;
structsockaddr_incliaddr;
intservport=5555;
charbuff[256];
WSADATAws;
intlen,err;
//初始化Winsock
if(WSAStartup(0x0101,&ws)!
=0)
{
printf("WSAStartup()failed!
\n");
exit(-1);
}
//创建套接字
printf("CreateSocket...\n");
servsock=socket(AF_INET,SOCK_STREAM,0);
//填充服务器地址结构
memset(&sa,0,sizeof(sa));
sa.sin_family=AF_INET;
sa.sin_port=htons(servport);
sa.sin_addr.s_addr=inet_addr("202.202.42.88");
//绑定套接字到服务器地址结构
printf("Binding...\n");
err=bind(servsock,(constsockaddr*)&sa,sizeof(sa));
if(err!
=0)
{
fprintf(stderr,"Bindfailed:
%d\n",WSAGetLastError());
exit(-2);
}
//监听套接字
printf("Listening...\n");
err=listen(servsock,5);
if(err!
=0)
{
fprintf(stderr,"Listenfailed:
%d\n",WSAGetLastError());
exit(-3);
}
//等待连接请求
printf("WaittingRequest...\n");
len=sizeof(cliaddr);
clisock=accept(servsock,(structsockaddr*)&cliaddr,&len);
printf("AcceptClient:
%s:
%d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
sprintf(buff,"Welcomeyou%s",inet_ntoa(cliaddr.sin_addr));
//发送欢迎词
send(clisock,buff,strlen(buff),0);
//关闭连接
closesocket(clisock);
closesocket(servsock);
WSACleanup();
}
窗体版TCPserver(阻塞式)
在stdafx.h文件中加入#include
//启动TCPserver按钮事件处理
voidCTcpServerDlg:
:
OnBnClickedButton1()
{
//TODO:
在此添加控件通知处理程序代码
if(WSAStartup(0x0101,&ws)!
=0)
{
m_edit1="WSAStartup()failed!
";
UpdateData(false);
return;
}
//创建套接字
servsock=socket(AF_INET,SOCK_STREAM,0);
//填充服务器地址结构
servport=5555;
memset(&sa,0,sizeof(sa));
sa.sin_family=AF_INET;
sa.sin_port=htons(servport);
sa.sin_addr.s_addr=inet_addr("202.202.42.88");
//绑定套接字到服务器地址结构
err=bind(servsock,(constsockaddr*)&sa,sizeof(sa));
if(err!
=0)
{
m_edit1="Bindfailed!
";
UpdateData(false);
return;
}
//监听套接字
err=listen(servsock,5);
if(err!
=0)
{
m_edit1="Listenfailed!
";
UpdateData(false);
return;
}
m_edit1.SetString("Waitingrequest...");
UpdateData(false);
this->RedrawWindow();//如不调用此句,则在阻塞Socket方式下窗体无法正常刷新
//等待连接请求
len=sizeof(cliaddr);
clisock=accept(servsock,(structsockaddr*)&cliaddr,&len);
m_edit1.Format("AcceptClient:
%s:
%d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
UpdateData(false);
}
//发送消息事件处理
voidCTcpServerDlg:
:
OnBnClickedButton2()
{
//TODO:
在此添加控件通知处理程序代码
sprintf(buff,"Welcomeyou%s",inet_ntoa(cliaddr.sin_addr));
//发送欢迎词
send(clisock,buff,strlen(buff),0);
}
//关闭连接事件处理
voidCTcpServerDlg:
:
OnBnClickedButton3()
{
//TODO:
在此添加控件通知处理程序代码
//关闭连接
m_edit1="ConnectionClosed!
";
UpdateData(false);
closesocket(clis