WindowsSocket网络编程.docx

上传人:b****8 文档编号:10506605 上传时间:2023-02-17 格式:DOCX 页数:18 大小:43.42KB
下载 相关 举报
WindowsSocket网络编程.docx_第1页
第1页 / 共18页
WindowsSocket网络编程.docx_第2页
第2页 / 共18页
WindowsSocket网络编程.docx_第3页
第3页 / 共18页
WindowsSocket网络编程.docx_第4页
第4页 / 共18页
WindowsSocket网络编程.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

WindowsSocket网络编程.docx

《WindowsSocket网络编程.docx》由会员分享,可在线阅读,更多相关《WindowsSocket网络编程.docx(18页珍藏版)》请在冰豆网上搜索。

WindowsSocket网络编程.docx

WindowsSocket网络编程

WindowsSocket网络编程

(二)——套接字编程原理

作者:

冰点工作室小鹰

一、客户机/服务器模式

在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Servermodel)。

该模式的建立基于以下两点:

1、非对等作用;2、通信完全是异步的。

客户机/服务器模式在操作过程中采取的是主动请示方式:

首先服务器方要先启动,并根据请示提供相应服务:

(过程如下)

1、打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。

2、等待客户请求到达该端口。

3、接收到重复服务请求,处理该请求并发送应答信号。

4、返回第二步,等待另一客户请求

5、关闭服务器。

客户方:

1、打开一通信通道,并连接到服务器所在主机的特定端口。

2、向服务器发送服务请求报文,等待并接收应答;继续提出请求……

3、请求结束后关闭通信通道并终止。

二、基本套接字

为了更好说明套接字编程原理,给出几个基本的套接字,在以后的篇幅中会给出更详细的使用说明。

1、创建套接字——socket()

功能:

使用前创建一个新的套接字

格式:

SOCKETPASCALFARsocket(intaf,inttype,intprocotol);

参数:

af:

通信发生的区域

type:

要建立的套接字类型

procotol:

使用的特定协议

2、指定本地地址——bind()

功能:

将套接字地址与所创建的套接字号联系起来。

格式:

intPASCALFARbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);

参数:

s:

是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。

其它:

没有错误,bind()返回0,否则SOCKET_ERROR

地址结构说明:

structsockaddr_in

{

shortsin_family;//AF_INET

u_shortsin_port;//16位端口号,网络字节顺序

structin_addrsin_addr;//32位IP地址,网络字节顺序

charsin_zero[8];//保留

}

3、建立套接字连接——connect()和accept()

功能:

共同完成连接工作

格式:

intPASCALFARconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);

SOCKETPASCALFARaccept(SOCKETs,structsockaddrFAR*name,intFAR*addrlen);

参数:

同上

4、监听连接——listen()

功能:

用于面向连接服务器,表明它愿意接收连接。

格式:

intPASCALFARlisten(SOCKETs,intbacklog);

5、数据传输——send()与recv()

功能:

数据的发送与接收

格式:

intPASCALFARsend(SOCKETs,constcharFAR*buf,intlen,intflags);

intPASCALFARrecv(SOCKETs,constcharFAR*buf,intlen,intflags);

参数:

buf:

指向存有传输数据的缓冲区的指针。

6、多路复用——select()

功能:

用来检测一个或多个套接字状态。

格式:

intPASCALFARselect(intnfds,fd_setFAR*readfds,fd_setFAR*writefds,

fd_setFAR*exceptfds,conststructtimevalFAR*timeout);

参数:

readfds:

指向要做读检测的指针

writefds:

指向要做写检测的指针

exceptfds:

指向要检测是否出错的指针

timeout:

最大等待时间

select()*执行同步I/O多路复用。

select函数的参数(intnfds,fd_setreadfds,fd_setwritefds,fd_setexceptfds,conststructtimevaltimeout)

我记得是:

第一个是个较为次要的值,设成0就行了。

后面的几个FD_SET类型的参数才是最重要的;

第一个FD_SET型的参数readfds是表示要被检查是否可读的Sockets,把你想要接收数据的那个套接字放在这里;

第二个FD_SET参数ritefds是表示要被检查是否可写的Sockets,将你要发送数据的套接字放在这里;

还有个FD_SET参数exceptfds是表示要被检查是否有错误的Socketsselect()

函数的第五个参数timeout,是让我们用来设定select函数要等待(block)多久。

兹述说如下:

(1)如果timeout设为「NULL」,那么select()就会一直等到「至少」某一个socket的事件成立了才会return,这和其他的blocking函数一样。

select(...,NULL)

(2)如果timeout的值设为{0,0}(秒,微秒),那么select()在检查后,不管有没有socket的事件成立,都会马上return,而不会停留。

timeout.tv_sec=timeout.tv_usec=0;select(...,&timeout)

(3)如果timout设为{m,n},那么就会等到至少某一个socket的事件发生,或是时间到了(m秒n微秒),才会return。

timeout.tv_sec=m;timeout.tv_usec=n;select(...,&timeout)

返回值:

成功-符合条件的Sockets总数(若Timeout发生,则为0)失败-SOCKET_ERROR(呼叫WSAGetLastError()可得知原因)

说明:

使用者可利用此函式来检查Sockets是否有资料可被读取,或是有空间可以写入,或是有错误发生。

关于对FD_SET类型的操作,有几个比较重要的宏:

FD_ZERO(*set)--将set的值清乾净FD_SET(s,*set)--将s加到set中FD_CLR(s,*set)--将s从set中删除FD_ISSET(s,*set)--检查s是否存在於set中参数readfds、writefds、及exceptfds都是「calledbyvalue-result」;而「calledbyvalue-result」的意思就是说,我们在将参数传给系统时,要先设启始值,并将这些参数的位址(address)告诉系统;而系统则会利用到这些值来做些运算或其他用途,最后并将结果再写回这些参数的位址中。

因此这些参数的值在传入前和函数返回后,可能会不同;所以每次调用select()前,对这些参数一定要重新设定它们的值。

假设我们要检查socket1和2目前是否可以用来传送资料,以及socket3是否有资料可读;我们不打算检查sockets是否有错误发生,所以exceptfds设为NULL。

步骤大致如下:

FD_ZERO(&writefds);FD_ZERO(&readfds);FD_SET(1,&writefds);FD_SET(2,&writefds);FD_SET(3,&readfds);select(...,&readfds,&writefds,NULL,...)if(FD_ISSET(1,&writefds))send(1,data);if(FD_ISSET(2,&writefds))send(2,data);if(FD_ISSET(3,&readfds))recv(3,data);

7、关闭套接字——closesocket()

功能:

关闭套接字s

格式:

BOOLPASCALFARclosesocket(SOCKETs);

三、典型过程图

2.1面向连接的套接字的系统调用时序图

2.2无连接协议的套接字调用时序图

2.3面向连接的应用程序流程图

FD_ZERO,FD_ISSET这些都是套节字结合操作宏

看看MSDN上的select函数,

这是在selectio模型中的核心,用来管理套节字IO的,避免出现无辜锁定.

intselect(intnfds,fd_setFAR*readfds,fd_setFAR*writefds,

fd_setFAR*exceptfds,

conststructtimevalFAR*timeout

);

第一个参数不管,是兼容目的,最后的是超时标准,select是阻塞操作

当然要设置超时事件.

接着的三个类型为fd_set的参数分别是用于检查套节字的可读性,可写性,和列外数据性质.

我举个例子

比如recv(),在没有数据到来调用它的时候,你的线程将被阻塞

如果数据一直不来,你的线程就要阻塞很久.这样显然不好.

所以采用select来查看套节字是否可读(也就是是否有数据读了)

步骤如下

sockets;

.....

fd_setset;

while

(1)

{

FD_ZERO(&set);//将你的套节字集合清空

FD_SET(s,&set);//加入你感兴趣的套节字到集合,这里是一个读数据的套节字s

select(0,&set,NULL,NULL,NULL);//检查套节字是否可读,

//很多情况下就是是否有数据(注意,只是说很多情况)

//这里select是否出错没有写

if(FD_ISSET(s,&set)//检查s是否在这个集合里面,

{//select将更新这个集合,把其中不可读的套节字去掉

//只保留符合条件的套节字在这个集合里面

recv(s,...);

}

//dosomethinghere

}

不知道你现在明白没有.另,由于这段时间没忙这,有错误不负责任.呵呵.

1、Socket服务器端:

Socket服务器端流程如下:

加载套接字->创建监听的套接字->绑定套接字->监听套接字->处理客户端相关请求。

下面是孙鑫VC详解里面的服务器端的例子:

C++代码

#include

#include

voidmain()

{

//加载套接字

WORDwVersionRequested;

WSADATAwsaData;

interr;

wVersionRequested=MAKEWORD(1,1);

err=WSAStartup(wVersionRequested,&wsaData);

if(err!

=0)

{

return;

}

if(LOBYTE(wsaData.wVersion)!

=1||

HIBYTE(wsaData.wVersion)!

=1)

{

WSACleanup();

return;

}

//创建监听的套接字

SOCKETsockSrv=socket(AF_INET,SOCK_STREAM,0);

SOCKADDR_INaddrSrv;

addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//把U_LONG的主机字节顺序转换为TCP/IP网络字节顺序

addrSrv.sin_family=AF_INET;

addrSrv.sin_port=htons(6000);

//绑定套接字

bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

//将套接字设置为监听模式,准备接受用户请求

listen(sockSrv,5);

SOCKADDR_INaddrClient;

intlen=sizeof(SOCKADDR);

printf("%s\n","welcome,theserveisstarted...");

while

(1)

{

//等待用户请求到来

SOCKETsockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);

charsendBuf[100];

sprintf(sendBuf,"welcome%sto",inet_ntoa(addrClient.sin_addr));

//发送数据

send(sockConn,sendBuf,100,0);

charrevBuf[100];

//接收数据

recv(sockConn,revBuf,100,0);

//打印接受数据

printf("%s\n",revBuf);

//关闭套接字

closesocket(sockConn);

}

}

#include

#include

voidmain()

{

//加载套接字

WORDwVersionRequested;

WSADATAwsaData;

interr;

wVersionRequested=MAKEWORD(1,1);

err=WSAStartup(wVersionRequested,&wsaData);

if(err!

=0)

{

return;

}

if(LOBYTE(wsaData.wVersion)!

=1||

HIBYTE(wsaData.wVersion)!

=1)

{

WSACleanup();

return;

}

//创建监听的套接字

SOCKETsockSrv=socket(AF_INET,SOCK_STREAM,0);

SOCKADDR_INaddrSrv;

addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//把U_LONG的主机字节顺序转换为TCP/IP网络字节顺序

addrSrv.sin_family=AF_INET;

addrSrv.sin_port=htons(6000);

//绑定套接字

bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

//将套接字设置为监听模式,准备接受用户请求

listen(sockSrv,5);

SOCKADDR_INaddrClient;

intlen=sizeof(SOCKADDR);

printf("%s\n","welcome,theserveisstarted...");

while

(1)

{

//等待用户请求到来

SOCKETsockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);

charsendBuf[100];

sprintf(sendBuf,"welcome%sto",inet_ntoa(addrClient.sin_addr));

//发送数据

send(sockConn,sendBuf,100,0);

charrevBuf[100];

//接收数据

recv(sockConn,revBuf,100,0);

//打印接受数据

printf("%s\n",revBuf);

//关闭套接字

closesocket(sockConn);

}

}

注意:

需要包含头文件,并且在工程设置的link里面加上ws32_2.dll

如果在VC中还有一个简单的加载套接字的方法:

C++代码

if(!

AfxSocketInit())

{

AfxMessageBox("套接字加载失败!

");

returnfalse;

}

if(!

AfxSocketInit())

{

AfxMessageBox("套接字加载失败!

");

returnfalse;

}

这个不需要包含上面注里面的头文件和ws2_32.lib库就可以实现加载套接字。

2、Socket客户端:

Socket客户端同样需要先加载套接字,然后创建套接字,不过之后不用绑定和监听了,而是直接连接服务器,发送相关请求。

同样贴出孙鑫VC详解里面的客户端的例子:

(不是我偷懒,是人家实在写的太好,无法超越)

C++代码

#include

#include

voidmain()

{

//加载套接字

WORDwVersionRequested;

WSADATAwsaData;

interr;

wVersionRequested=MAKEWORD(1,1);

err=WSAStartup(wVersionRequested,&wsaData);

if(err!

=0)

{

return;

}

if(LOBYTE(wsaData.wVersion)!

=1||

HIBYTE(wsaData.wVersion)!

=1)

{

WSACleanup();

return;

}

//创建套接字

SOCKETsockClient=socket(AF_INET,SOCK_STREAM,0);

SOCKADDR_INaddrSrv;

addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//把U_LONG的主机字节顺序转换为TCP/IP网络字节顺序

addrSrv.sin_family=AF_INET;

addrSrv.sin_port=htons(6000);

//向服务器发送请求

connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

//接受数据

charrecBuf[100];

recv(sockClient,recBuf,100,0);

printf("%s\n",recBuf);

//发送数据

send(sockClient,"thisis马亚南1",strlen("thisis马亚南")+1,0);

//关闭套接字

closesocket(sockClient);

WSACleanup();

}

#include

#include

voidmain()

{

//加载套接字

WORDwVersionRequested;

WSADATAwsaData;

interr;

wVersionRequested=MAKEWORD(1,1);

err=WSAStartup(wVersionRequested,&wsaData);

if(err!

=0)

{

return;

}

if(LOBYTE(wsaData.wVersion)!

=1||

HIBYTE(wsaData.wVersion)!

=1)

{

WSACleanup();

return;

}

//创建套接字

SOCKETsockClient=socket(AF_INET,SOCK_STREAM,0);

SOCKADDR_INaddrSrv;

addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//把U_LONG的主机字节顺序转换为TCP/IP网络字节顺序

addrSrv.sin_family=AF_INET;

addrSrv.sin_port=htons(6000);

//向服务器发送请求

connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

//接受数据

charrecBuf[100];

recv(sockClient,recBuf,100,0);

printf("%s\n",recBuf);

//发送数据

send(sockClient,"thisis扈修非",strlen("thisis扈修非")+1,0);

//关闭套接字

closesocket(sockClient);

WSACleanup();

}

需要加载的头文件和库同上

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 管理学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1