您的位置:首页 > 理论基础 > 计算机网络

TCP/IP协议的套接字类型及异步winsock编程例子

2009-07-21 17:16 477 查看
TCP/IP协议的套接字类型及异步winsock编程例子
流套接字(SOCK_STREAM):流套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。流套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP(The Transmission Control Protocol)协议。
数据报套接字(SOCK_DGRAM):数据报套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP(User Datagram Protocol)协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。
原始套接字(SOCK_RAW):原始套接字与标准套接字(标准套接字指的是前面介绍的流套接字和数据报套接字)的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送数据必须使用原始套接字。

表12. 3 Linux 支持的套接字地址族
套接字地址族
描述
UNIX
UNIX 域套接字
INET
通过 TCP/IP 协议支持的 Internet 地址族
AX25
Amater radio X25
APPLETALK
Appletalk DDP
IPX
Novell IPX
X25
X25
表12.4 Linux 所支持的 BSD 套接字类型
BSD 套接字类型
描述
流(stream)
这种套接字提供了可靠的双向顺序数据流,可保证数据不会在传输过程中丢失、破坏或重复出现。流套接字通过 INET 地址族的 TCP 协议实现。
数据报(datagram)
这种套接字也提供双向的数据传输,但是并不对数据的传输提供担保,也就是说,数据可能会以错误的顺序传递,甚至丢失或破坏。这种类型的套接字通过 INET 地址族的 UDP 协议实现。
原始(raw)
利用这种类型的套接字,进程可以直接访问底层协议(因此称为原始)。例如,可在某个以太网设备上打开原始套接字,然后获取原始的 IP 数据传输信息。
可靠发送的消息
和数据报套接字类似,但保证数据被正确传输到目的端。
顺序数据包
和流套接字类似,但数据包大小是固定的。
数据包(packet)
这并不是标准的 BSD 套接字类型,它是 Linux 专有的 BSD 套接字扩展,可允许进程直接在设备级访问数据包。
例子1:(winsock异步编程
//这是一个winsock异步编程的例子
//这个服务监听1000端口,可以使用 telnet localhost 1000进行测试,最大允许20个连接
#include <stdio.h>
#include <Winsock2.h>
#pragma comment(lib, "WS2_32.LIB")
#define MAX_SOCKETS 20
#define MAX_BACKLOG 1
#define PORT 1000

SOCKET Startlisten(int port)
{

WORD wVersion=MAKEWORD(2,0);
WSADATA wsData;
int nResult= WSAStartup(wVersion,&wsData);
if(nResult !=0)
{
return 0;
}
SOCKET sock=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(sock==INVALID_SOCKET)
{
return 0;
}
sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port= htons(port); //保证字节顺序
addr.sin_addr.s_addr= htonl(INADDR_ANY);
nResult=bind(sock,(sockaddr*)&addr,sizeof(sockaddr));
if(nResult==SOCKET_ERROR)
{
return 0;
}
nResult=listen(sock,MAX_BACKLOG); //最多 MAX_BACKLOG 个 Pending 连接
if(nResult==SOCKET_ERROR)
{
return 0;
}
printf("Please try: telnet localhost 1000/n");
return sock;
}
int main()
{
//init winsock env
WORD wVersion=MAKEWORD(2,0);
WSADATA wsData;
int nResult= WSAStartup(wVersion,&wsData);

//create the first event
WSAEVENT eventList[MAX_SOCKETS+1];
int eventIndex=0;
eventList[eventIndex]=WSACreateEvent();

//start listen on 1000
SOCKET sockList[MAX_SOCKETS+1];
sockList[eventIndex]=Startlisten(PORT);

//use WSAEventSelect to set the server to running on async mode
int rc = WSAEventSelect(sockList[eventIndex], eventList[eventIndex], FD_READ|FD_WRITE|FD_ACCEPT|FD_CLOSE|FD_CONNECT);
int index;
sockaddr_in client;
//waiting on the events to deal with connect requests or network read event
while( (index=WSAWaitForMultipleEvents(eventIndex+1,eventList,false,WSA_INFINITE,true)) != WSA_WAIT_FAILED)
{
index -= WSA_WAIT_EVENT_0;
WSANETWORKEVENTS type;
WSAEnumNetworkEvents(sockList[index],eventList[index],&type);
switch(type.lNetworkEvents)
{
case FD_ACCEPT:
{
int len=sizeof(sockaddr);
if (eventIndex < MAX_SOCKETS)
{
++eventIndex;
sockList[eventIndex] =accept(sockList[index],(sockaddr*)&client,&len);
eventList[eventIndex]=WSACreateEvent();
rc = WSAEventSelect(sockList[eventIndex], eventList[eventIndex], FD_READ|FD_WRITE|FD_CLOSE|FD_CONNECT);
printf("connected from %s:%d/n",inet_ntoa( client.sin_addr ),client.sin_port);
}
}
break;
case FD_READ:
{
char mess;
rc =recv(sockList[index],&mess,1,0);

//ctrl+c == 3
if (mess == 3 || rc ==SOCKET_ERROR)
{
shutdown(sockList[index],SD_SEND);
break;
}

rc=send(sockList[index],&mess,sizeof(mess),0);
printf("%c",mess);

}
break;
case FD_WRITE:
{
char buf[256];
sprintf(buf,"hello,you are the %d client./n",eventIndex);
rc=send(sockList[index],buf,strlen(buf),0);
}
break;
case FD_CLOSE:
{
int len=sizeof(sockaddr);
getpeername(sockList[index],(sockaddr*)&client,&len);
printf("Closed from %s:%d/n",inet_ntoa( client.sin_addr ),client.sin_port);
closesocket(sockList[index]);
}
break;
}
}

return 0;
}

例子2
//////////////////////////////////////////////////
// WSAEventSelect文件
include "initsock.h"
#include <stdio.h>
#include <iostream.h>
#include <windows.h>

// 初始化Winsock库
CInitSock theSock;

int main()
{
// 事件句柄和套节字句柄表
WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];
SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS];
int nEventTotal = 0;

USHORT nPort = 4567; // 此服务器监听的端口号
// 创建监听套节字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(nPort);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if(::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf(" Failed bind() /n");
return -1;
}
::listen(sListen, 5);

// 创建事件对象,并关联到新的套节字
WSAEVENT event = ::WSACreateEvent();
::WSAEventSelect(sListen, event, FD_ACCEPT|FD_CLOSE);
// 添加到表中
eventArray[nEventTotal] = event;
sockArray[nEventTotal] = sListen;
nEventTotal ;

// 处理网络事件
while(TRUE)
{
// 在所有事件对象上等待
int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);
// 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态
nIndex = nIndex - WSA_WAIT_EVENT_0;
for(int i=nIndex; i<nEventTotal; i )
{
nIndex = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE);
if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
{
continue;
}
else
{
// 获取到来的通知消息,WSAEnumNetworkEvents函数会自动重置受信事件
WSANETWORKEVENTS event;
::WSAEnumNetworkEvents(sockArray[i], eventArray[i], &event);
if(event.lNetworkEvents & FD_ACCEPT) // 处理FD_ACCEPT通知消息
{
if(event.iErrorCode[FD_ACCEPT_BIT] == 0)
{
if(nEventTotal > WSA_MAXIMUM_WAIT_EVENTS)
{
printf(" Too many connections! /n");
continue;
}
SOCKET sNew = ::accept(sockArray[i], NULL, NULL);
WSAEVENT event = ::WSACreateEvent();
::WSAEventSelect(sNew, event, FD_READ|FD_CLOSE|FD_WRITE);
// 添加到表中
eventArray[nEventTotal] = event;
sockArray[nEventTotal] = sNew;
nEventTotal ;
}
}
else if(event.lNetworkEvents & FD_READ) // 处理FD_READ通知消息
{
if(event.iErrorCode[FD_READ_BIT] == 0)
{
char szText[256];
int nRecv = ::recv(sockArray[i], szText, strlen(szText), 0);
if(nRecv > 0)
{
szText[nRecv] = '/0';
printf("接收到数据:%s /n", szText);
}
}
}
else if(event.lNetworkEvents & FD_CLOSE) // 处理FD_CLOSE通知消息
{
if(event.iErrorCode[FD_CLOSE_BIT] == 0)
{
::closesocket(sockArray[i]);
for(int j=i; j<nEventTotal-1; j )
{
sockArray[j] = sockArray[j 1];
sockArray[j] = sockArray[j 1];
}
nEventTotal--;
}
}
else if(event.lNetworkEvents & FD_WRITE) // 处理FD_WRITE通知消息
{
}
}
}
}
return 0;
}

以上特殊颜色的是不懂的地方.
【1】第一个 [int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);]
中nIndex返回的是什么呀,是像FD_READ 这样的吗
【2】那为什么还要 [nIndex = nIndex - WSA_WAIT_EVENT_0;]呀,这是做什么的.为什么要减WSA_WAIT_EVENT_0
【3】减过之后为什么还要 [for(int i=nIndex; i <nEventTotal; i )]循环,循环的时候为什么不是从i=0开始呀.而是从i=nIndex开始

网友回复:#include "initsock.h"
中就包含了
::WSAStartup
::WSACleanup()
所以不必考虑这些了
网友回复:1、返回值是从WSA_WAIT_EVENT_0 到 (WSA_WAIT_EVENT_0 cEvents - 1),表示WSAWaitForMultipleEvents()函数在等待的某一个事件已经触发
2、nIndex = nIndex - WSA_WAIT_EVENT_0是因为获得的索引要去访问eventArray[WSA_MAXIMUM_WAIT_EVENTS]数组,当然得从0开始
3、访问数组当然从0开始索引
网友回复:这个是因为WSAWaitForMultipleEvents返回的时候有可能是多个event被signal了
而返回的是第一个signaled的event的index,这个时候需要详细的检查之后的event。同时对这些signaled的event进行处理.........而nindex之前的event可以确定是没有singaled,可以不考虑............
网友回复:那为什么要【nIndex = nIndex - WSA_WAIT_EVENT_0;】呢,
为什么不直接[nIndex =nIndex 1]这样不就可以检查后面的是否也有事件发生吗

【nIndex = nIndex - WSA_WAIT_EVENT_0;】代表什么,是什么意思呀.
就这点不明白了

网友回复:【1】第一个 [int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);]
中nIndex返回的是什么呀,是像FD_READ 这样的吗
如果等待成功,返回的是大于等于WSA_WAIT_EVENT_0的一个值,表示所等待的事件中哪一个触发了。

【2】那为什么还要 [nIndex = nIndex - WSA_WAIT_EVENT_0;]呀,这是做什么的.为什么要减WSA_WAIT_EVENT_0
减去WSA_WAIT_EVENT_0,等到的就是触发的事件在eventArray数组中的下标。

【3】减过之后为什么还要 [for(int i=nIndex; i <nEventTotal; i )]循环,循环的时候为什么不是从i=0开始呀.而是从i=nIndex开始
从这开始循环,目的是检查还有哪些事件也触发了,因为WSAWaitForMultipleEvents的返回值是数组中第1个(按下标)触发的事件。
网友回复:nIndex是表示eventArray中的第几个事件被等到,与FD-READ无关了,然后通过调用WSAEnumNetworkEvents获取具体的网络事件
至于第2个nIndex = nIndex - WSA_WAIT_EVENT_0其实我觉得一般无关紧要的,因为通常都是从0开始的数组,更正确的说nIndex - WSA_WAIT_EVENT_0才是第几个事件被等到,也就是eventArray下标

for(int i=nIndex; i <nEventTotal; i )接着下面的事件等呀,总不能等到一个以后又从头开始吧,那后面总没机会了……

网友回复:恩,不太明白,感兴趣话题
网友回复:WSA_WAIT_EVENT_0 是不是0呀
网友回复:是0,但不要管它是多少,该减的时候就减,这是好的编程习惯。
网友回复:我明白了.谢谢各位了
网友回复:结贴:
1.2楼的是不是有些地方说的不对呀
2.3楼的给我的启示最大,也就明白为什么了,所以在4楼我就把我不懂的说了下
3.5,6楼说得不错,只是,我在2楼时已经明白了,也就还有一个就是4楼那个问题了.
4.9楼确定了我的问题
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: