网络编程实验二 Windows下socket编程通信

时间:2024.5.2

天津理工大学实验报告 学院(系)名称: 计算机与通信工程学院

网络编程实验二Windows下socket编程通信

网络编程实验二Windows下socket编程通信

网络编程实验二Windows下socket编程通信

网络编程实验二Windows下socket编程通信

网络编程实验二Windows下socket编程通信

网络编程实验二Windows下socket编程通信

网络编程实验二Windows下socket编程通信


第二篇:Windows Socket 网络编程


Windows Socket 网络编程() —— 套接字编程原理
作者: 冰点工作室 小鹰

一、客户机/服务器模式
在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Server model)。该模式的建立基于以下两点:1、非对等作用;2、通信完全是异步的。客户机/服务器模式在操作过程中采取的是主动请示方式:

首先服务器方要先启动,并根据请示提供相应服务:(过程如下)
1、打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。
2、等待客户请求到达该端口。
3、接收到重复服务请求,处理该请求并发送应答信号。
4、返回第二步,等待另一客户请求
5、关闭服务器。
客户方:
1、打开一通信通道,并连接到服务器所在主机的特定端口。
2、向服务器发送服务请求报文,等待并接收应答;继续提出请求……
3、请求结束后关闭通信通道并终止。

二、基本套接字
为了更好说明套接字编程原理,给出几个基本的套接字,在以后的篇幅中会给出更详细的使用说明。
1、创建套接字——socket()
功能:使用前创建一个新的套接字
格式:SOCKET PASCAL FAR socket(int af,int type,int procotol);
参数:af: 通信发生的区域
type: 要建立的套接字类型
procotol: 使用的特定协议

2、指定本地地址——bind()
功能:将套接字地址与所创建的套接字号联系起来。
格式:int PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen);
参数:s: 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
其它:没有错误,bind()返回0,否则SOCKET_ERROR
地址结构说明:
struct sockaddr_in
{
short sin_family;//AF_INET
u_short sin_port;//16位端口号,网络字节顺序
struct in_addr sin_addr;//32位IP地址,网络字节顺序
char sin_zero[8];//保留
}

3、建立套接字连接——connect()和accept()
功能:共同完成连接工作
格式:int PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen);
SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR * name,int FAR * addrlen);
参数:同上

4、监听连接——listen()
功能:用于面向连接服务器,表明它愿意接收连接。
格式:int PASCAL FAR listen(SOCKET s, int backlog);

5、数据传输——send()与recv()
功能:数据的发送与接收
格式:int PASCAL FAR send(SOCKET s,const char FAR * buf,int len,int flags);
int PASCAL FAR recv(SOCKET s,const char FAR * buf,int len,int flags);
参数:buf:指向存有传输数据的缓冲区的指针。

6、多路复用——select()
功能:用来检测一个或多个套接字状态。
格式:int PASCAL FAR select(int nfds, fd_set FAR * readfds,fd_set FAR * writefds,
fd_set FAR * exceptfds,const struct timeval FAR * timeout);
参数:readfds:指向要做读检测的指针
writefds:指向要做写检测的指针
exceptfds:指向要检测是否出错的指针
timeout:最大等待时间

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

select函数的参数( int nfds, fd_set readfds, fd_set writefds, fd_set exceptfds, const struct timeval timeout )

我记得是:第一个是个较为次要的值,设成0就行了。 后面的几个FD_SET类型的参数才是最重要的;

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

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

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

函数的第五个参数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 都是 「called by value- result」;而「called by value-result」的意思就是说,我们在将参数传给系统时,要先设启始值,并将这些参数的位址(address)告诉系统;而系统则会利用到这些值来做些运算或其他用途,最后并将结果再写回这些参数的位址中。 因此这些参数的值在传入前和函数返回后,可能会不同;所以每次调用 select() 前,对这些参数一定要重新设定它们的值。 假设我们要检查 socket 1 和 2 目前是否可以用来传送资料,以及 socket 3 是 否有资料可读;我们不打算检查 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
格式:BOOL PASCAL FAR closesocket(SOCKET s);


三、典型过程图
2.1 面向连接的套接字的系统调用时序图



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



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


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

  看看MSDN上的select函数,  

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

  int   select(     int   nfds,fd_set   FAR   *readfds,     fd_set   FAR   *writefds,                              

      fd_set   FAR   *exceptfds,                  

      const   struct   timeval   FAR   *timeout      

  );  

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

  当然要设置超时事件.  

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

   

  我举个例子  

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

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

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

  步骤如下  

  socket   s;  

  .....  

  fd_set   set;  

  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,...);  

       

      }  

      //do   something   here  

  }  

   

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


1、Socket服务器端:

Socket服务器端流程如下:加载套接字->创建监听的套接字->绑定套接字->监听套接字->处理客户端相关请求。

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

C++代码

#include <Winsock2.h>  

#include <stdio.h>  

 

void main()  

{  

    //加载套接字  

    WORD wVersionRequested;  

    WSADATA wsaData;  

    int err;  

      

    wVersionRequested=MAKEWORD(1,1);  

      

    err=WSAStartup(wVersionRequested,&wsaData);  

    if (err!=0)  

    {  

        return;  

    }  

      

    if (LOBYTE(wsaData.wVersion)!=1||  

        HIBYTE(wsaData.wVersion)!=1)  

    {  

        WSACleanup();  

        return;  

    }  

      

    //创建监听的套接字  

    SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);  

      

    SOCKADDR_IN addrSrv;  

    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_IN addrClient;  

    int len=sizeof(SOCKADDR);  

      

    printf("%s\n","welcome,the serve is started...");  

    while (1)  

    {  

        //等待用户请求到来  

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

        char sendBuf[100];  

        sprintf(sendBuf,"welcome %s to http://unblue2008.javaeye.com",inet_ntoa(addrClient.sin_addr));  

          

        //发送数据  

        send(sockConn,sendBuf,100,0);  

        char revBuf[100];  

          

        //接收数据  

        recv(sockConn,revBuf,100,0);  

        //打印接受数据  

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

          

        //关闭套接字  

        closesocket(sockConn);  

    }     

#include <Winsock2.h>

#include <stdio.h>

void main()

{

         //加载套接字

         WORD wVersionRequested;

         WSADATA wsaData;

         int err;

        

         wVersionRequested=MAKEWORD(1,1);

        

         err=WSAStartup(wVersionRequested,&wsaData);

         if (err!=0)

         {

                   return;

         }

        

         if (LOBYTE(wsaData.wVersion)!=1||

                   HIBYTE(wsaData.wVersion)!=1)

         {

                   WSACleanup();

                   return;

         }

        

         //创建监听的套接字

         SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);

        

         SOCKADDR_IN addrSrv;

         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_IN addrClient;

         int len=sizeof(SOCKADDR);

        

         printf("%s\n","welcome,the serve is started...");

         while (1)

         {

                   //等待用户请求到来

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

                   char sendBuf[100];

                   sprintf(sendBuf,"welcome %s to http://unblue2008.javaeye.com",inet_ntoa(addrClient.sin_addr));

                  

                   //发送数据

                   send(sockConn,sendBuf,100,0);

                   char revBuf[100];

                  

                   //接收数据

                   recv(sockConn,revBuf,100,0);

                   //打印接受数据

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

                  

                   //关闭套接字

                   closesocket(sockConn);

         }       

}

注意:需要包含头文件<Winsock2.h>,并且在工程设置的link里面加上ws32_2.dll

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

C++代码

if (!AfxSocketInit())  

    {  

        AfxMessageBox("套接字加载失败!");  

        return false;  

    } 

if (!AfxSocketInit())

         {

                   AfxMessageBox("套接字加载失败!");

                   return false;

         }

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

2、Socket客户端:

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

同样贴出孙鑫VC详解里面的客户端的例子:(不是我偷懒,是人家实在写的太好,无法超越 )

C++代码

#include <Winsock2.h>  

#include <stdio.h>  

 

void main()  

{  

    //加载套接字  

    WORD wVersionRequested;  

    WSADATA wsaData;  

    int err;  

      

    wVersionRequested=MAKEWORD(1,1);  

      

    err=WSAStartup(wVersionRequested,&wsaData);  

    if (err!=0)  

    {  

        return;  

    }  

      

    if (LOBYTE(wsaData.wVersion)!=1||  

        HIBYTE(wsaData.wVersion)!=1)  

    {  

        WSACleanup();  

        return;  

    }   

 

    //创建套接字  

    SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);  

      

    SOCKADDR_IN addrSrv;  

    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));  

      

    //接受数据  

    char recBuf[100];  

    recv(sockClient,recBuf,100,0);  

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

      

    //发送数据  

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

    //关闭套接字  

    closesocket(sockClient);  

    WSACleanup();  

#include <Winsock2.h>

#include <stdio.h>

void main()

{

         //加载套接字

         WORD wVersionRequested;

         WSADATA wsaData;

         int err;

        

         wVersionRequested=MAKEWORD(1,1);

        

         err=WSAStartup(wVersionRequested,&wsaData);

         if (err!=0)

         {

                   return;

         }

        

         if (LOBYTE(wsaData.wVersion)!=1||

                   HIBYTE(wsaData.wVersion)!=1)

         {

                   WSACleanup();

                   return;

         }

         //创建套接字

         SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);

        

         SOCKADDR_IN addrSrv;

         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));

        

         //接受数据

         char recBuf[100];

         recv(sockClient,recBuf,100,0);

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

        

         //发送数据

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

         //关闭套接字

         closesocket(sockClient);

         WSACleanup();

}

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


更多相关推荐:
《飘》读后感

飘读后感20xx年09月22日一口气再次看完米切尔的飘还有根据它改编的电影乱世佳人好多的感触涌上心头看完它们我似乎明白更多理解更透是的正如思嘉所说明天是另一天是啊明天是新的开始飘以19世纪60年代美国南北战争和...

《世界是平的》the world is flat读后感20xx字

世界是平的读后感最近一段时间我拜读了美国作家托马斯弗里德曼所著的世界是平的一书说实在的刚开始这本书吸引我眼球的只是书的题目我急切想弄清楚的是作者为什么认为世界是平的这个奇特观点随着阅读的继续我不仅找到了问题的答...

NOVEL Far from the Madding Crowd 英语小说远离尘嚣读后感

FarfromtheMaddingCrowdFarfromtheMaddingCrowdtellsusastorythathappenedinavillageofBritainTheyoungfarmerOakhadfalleni...

Far from the Madding Crowd远离尘嚣读后感

FarfromtheMaddingCrowdFarfromtheMaddingCrowdtellsusastorythathappenedinavillageofBritainTheyoungfarmerOakhadfalleni...

Peter pan in London 彼得潘读后感

PeterpanisanightinLondon39skensingtondistrictoutsidetheopenwindowtoMrsDarlingbeforebedtimestoryOneeveninghewasfound...

《西游记》读后感 Impression on Journey to The West

西游记读后感ImpressiononJourneytoTheWestDuringthesummerholidayIspentmuchtimeinreadingIfinishedoneofthefourmaste...

the old man and the sea英文读后感

TheOldManandtheSeaTheOldManandtheSeaisoneofthebestknownnovelsbythefamousAmericanfictionwriterErnestMillerHemingwayi...

《西游记》读后感 Impression on Journey to The West

西游记读后感ImpressiononJourneytoTheWestDuringthesummerholidayIspentmuchtimeinreadingIfinishedoneofthefourmaste...

The little prince英文读后感

ThelittleprinceAfairytaleofloveandresponsibilityThelittleprincewhichwaswrittenbyAntoinedeSaintExuperyisoneofthegrea...

Waiting for Goldie

WaitingforGoldieDannysgrandfatherhasmanypigeonsoneofthemhasapairofgoldeneyesandsheisquiterareBecauseofhereyesshesca...

white fang读书笔记

BookReportofWhiteFangWhitefangwrittenbyJackLondonisaboutawolfcubmakingitswayintheworldandthebookdescribeshowthiscub...

The call of the wild读后感

TheCallOfTheWild21DiaryThecallofthewildisafamousnovelwrittenbyJackLondonanAmericanauthorwholivedfrom1876to19xxHeals...

gone with the wind读后感(1篇)