zoukankan      html  css  js  c++  java
  • 网络编程

      网络程序的实现可以有多种方式,Windows Socket就是其中一种比较简单的实现方法.Socket是连接应用程序与网络驱动程序的桥梁,Socket在应用程序中创建.通过绑定操作与驱动程序建立关系。此后应用程序送给Socket的数据,由Socket交给驱动程序向网络上发送出去.计算机从网络上收到与该Socket绑定的IP地址和端口号相关的数据后,由驱动程序交给Socket,应用程序便可从该Socket中提取接收到的数据。网络应用程序就是这样通过Socket进行数据的发送与接收的。
      两台主机要进行通信,要遵循约定的规则,我们把这种规则称之为协议。为了标识在计算机上运行的每一个网络通信程序,为他们分别分配一个端口号。在发送数据时,除了指定接收数据的主机IP地址外,还要指定相应的端口号。这样,在指定IP地址的计算机上,将会由在指定端口号上等待数据的网络应用程序接收数据。
    传输层:传输控制协议(TCP),用户数据报协议(UDP)
    TCP:面向连接的可靠的传输协议。利用TCP协议进行通信时,首先要通过三步握手,以建立通信双方的连接。一旦连接建立好,就可以进行通信了。TCP提供了数据确认和数据重传的机制,保证了发送的数据一定能到达通信的对方。
    UDP:是无连接的、不可靠的传输协议。采用UDP进行通信时,不需要建立连接,可以直接向一个IP地址发送数据,但是对方能否收到,就不敢保证了。我们知道在网络上传输的是电信号,既然是电信号,在传输过程中就会有衰减,因此数据有可能在网络上就消失了,也有可能我们所指定的IP地址还没有分配,或者该IP地址所对应的主机还没有运行,这些情况都有可能导致发送的数据接收不到。
      端口是一种抽象的软件结构(包括一些数据结构和I/O缓冲区).应用程序通过系统调用与端口建立连接(binding)后,传输层传给该端口的数据都被相应的进程所接收,相应进程发给传输层的数据都通过该端口输出。端口用一个整数型标识符来表示,即端口号。端口号跟协议相关,TCP/IP传输层的两个协议TCP和UDP是完全独立的两个软件模块,因此各自的端口号也相互独立。端口使用一个16位的数字来表示,它的范围是0-65535,1024以下的端口号保留给预定义的服务。例如,http使用80端口。
      不同的计算机存放多字节值的顺序不同,有的机器在起始地址存放低位字节(低位先存),有的机器在起始地址存放高位字节(高位先存)。基于Intel的CPU,即我们常用的PC机采用的是低位先存。为保证数据的正确性,在网络协议中需要指定网络字节顺序,TCP/IP协议使用16位整数和32位整数的高位先存格式。由于不同的计算机存放数据字节的顺序不同,这样发送方发送数据后,即使接收方接收到该数据,也有可能无法查看所接收到的数据。所以在网络中不同主机间进行通信时,要统一采用网络字节顺序。
      在TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户机/服务器模式(CliendServer),即客户向服务器提出请求,服务器接收到请求后,提供相应的服务。
    客户机/服务器模式的建立基于以下两点:首先,建立网络的起因是网络中软硬件资源、运算能力和信息不均等,需要共享,从而造就拥有众多资源的主机提供服务,资源较少的客户请求服务这一非对等作用。其次,网间进程通信完全是异步的,相互通信的进程间既不存在父子关系,又不共享内存缓冲区,因此需要一种机制为希望通信的进程间建立联系,为二者的数据交换提供同步,这就是基于客户机/服务器模式的TCP/IP.
    一、套接字的类型
    1.流式套接字(SOCK_STREAM)
    提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。流式套接字实际上是基于TCP协议实现的。
    2.数据报式套接字(SOCK_DGRAM )
    提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。数据报式套接字实际上是基于UDP协议实现的。
    3.原始套接字(SOCK_RAW)
    二、基于TCP(面向连接)的socket编程
    服务器端程序流程如下:
    1.创建套接字(socket)。
    2.将套接字绑定到一个本地地址和端口上(bind)。
    3.将套接字设为监听模式,准备接收客户请求(listen)。
    4.等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。
    5.用返回的套接字和客户端进行通信(send/recv)。
    6.返回,等待另一客户请求。
    7.关闭套接字。
    基于TCP(面向连接)的socket编程的客户端程序流程如下:
    1.创建套接字(socket)。
    2.向服务器发出连接请求(connect)。
    3.和服务器端进行通信(send/recv )。
    4.关闭套接字。
      在服务器端,当调用accept函数时,程序就会等待,等待客户端调用Connect函数发出连接请求,然后服务器端接受该请求,于是双方就建立了连接。之后,服务器端和客户端就可以利用send和recv函数进行通信了。读者应注意,在客户端并不需要调用bind函数。因为服务器需要接收客户端的请求,所以必须告诉本地主机它打算在哪个IP地址和哪个端口上等待客户请求,因此必须调用bind函数来实现这一功能。而对客户端来说,当它发起连接请求,服务器端接受该请求后,在服务器端就保存了该客户端的IP地址和端口的信息。这样,对服务器端来说,一旦建立连接之后,实际上它己经保存了客户端的IP地址和端口号的信息,因此就可以利用所返回的套接字调用send/recv函数与客户端进行通信。
    三、基于UDP(面向无连接)的socket编程
    服务器端也叫接收端,对于基于UDP(面向无连接)的套接字编程来说,它的服务器端和客户端这种概念不是很强化,我们也可以把服务器端,即先启动的一端称为接收端,发送数据的一端称为发送端,也称为客户端。
    我们先看一下接收端程序的编写:
    1.创建套接字(socket )。
    2.将套接字绑定到一个本地地址和端口上(bind )。
    3.等待接收数据(recvfrom )。
    4.关闭套接字。
    对于基于UDP的套接字编程,为什么仍然需要调用bind函数进行绑定呢?应注意,虽然面向无连接的socket编程无须建立连接,但是为了完成这次通信,对于接收端来说,它必须先启动以接收客户端发送的数据,因此接收端必须告诉主机它是在哪个地址和端口上等待数据的到来,也就是说,接收端(服务器端)必须调用bind函数将套接字绑定到一个本地地址和端口上。
    对于客户端程序的编写非常简单:
    1.创建套接字(socket)。
    2.向服务器发送数据(sendto)。
    3.关闭套接字。
    注意,在基于UDP的套接字编程时,利用的是sendto和recvfrom这两个函数实现数据的发送和接收,而基于TCP的套接字编程时,发送数据是调用send函数,接收数据调用recv函数。
    套接字表示了通信的端点。我们利用套接字进行通信与利用电话机进行通信是一样的,套接字相当于电话机,IP地址相当于总机号码,而端口号则相当于分机。

    Win32Tcp:

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

      while(1)
      {
        SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
        char sendBuf[100];
        sprintf_s(sendBuf,"Welcome %s to http://www.sunxin.org",
        inet_ntoa(addrClient.sin_addr));
        send(sockConn,sendBuf,strlen(sendBuf)+1,0);
        char recvBuf[100];
        recv(sockConn,recvBuf,100,0);
        printf("%s ",recvBuf);
        closesocket(sockConn);
      }
    }

    Win32Client:

    #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");
      addrSrv.sin_family=AF_INET;
      addrSrv.sin_port=htons(6000);
      connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

      char recvBuf[100];
      recv(sockClient,recvBuf,100,0);
      printf("%s ",recvBuf);
      send(sockClient,"This is lisi",strlen("This is lisi")+1,0);

      closesocket(sockClient);
      WSACleanup();
    }

    UDPSrv:

    #include <WinSock2.h>
    #include <stdio.h>
    void main()
    {
      WORD wVersionRequested;
      WSADATA wsaData;
      int err = 0;
      wVersionRequested = MAKEWORD(2,2);
      err = WSAStartup(wVersionRequested,&wsaData);
      if (0 != err)
      {
        return;
      }
      if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
      {
        WSACleanup();
        return;
      }

      SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM,0);
      SOCKADDR_IN addrSvr;
      addrSvr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
      addrSvr.sin_family = AF_INET;
      addrSvr.sin_port = htons(6000);

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

      SOCKADDR_IN addrClient;
      int len = sizeof(SOCKADDR);
      char recvBuf[100] = "";
      recvfrom(sockSrv,recvBuf,sizeof(recvBuf),0,(SOCKADDR*)&addrClient,&len);
      printf(recvBuf);
      closesocket(sockSrv);
      WSACleanup();
    }

    UDPClient:

    #include <WinSock2.h>
    #include <stdio.h>

    void main()
    {
      WORD wVersionRequested;
      WSADATA wsaData;
      int nErr = 0;
      wVersionRequested = MAKEWORD(2,2);
      nErr = WSAStartup(wVersionRequested,&wsaData);
      if (0 != nErr)
      {
        return;
      }
      if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
      {
        WSACleanup();
        return;
      }

      SOCKET sockClient = socket(AF_INET,SOCK_DGRAM,0);
      SOCKADDR_IN sockSrc;
      sockSrc.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
      sockSrc.sin_family = AF_INET;
      sockSrc.sin_port = htons(6000);

      sendto(sockClient,"Hello",strlen("Hello")+1,0,(SOCKADDR*)&sockSrc,sizeof(sockSrc));
      closesocket(sockClient);
      WSACleanup();
    }

  • 相关阅读:
    设计模式入门--设计模式原则(总纲)
    Chart图形 [GDI+] OWCChart统计图的封装类 (转载)
    Chart图形 [功能帮助类] Assistant创建显示图像的标签和文件 (转载)
    [GDI+] 生成缩略图的类文件SmallImage (转载)
    [Mime] 在c#程序中放音乐的帮助类 (转载)
    [访问系统] Api_Win32_Mac类工具包 (转载)
    [访问系统] C#计算机信息类ComputerInfo (转载)
    [Jquery] jQuery.cookie帮助类 (转载)
    [综合|基础|语法] 最新的皮肤帮助类 UI_Misc_Helper (转载)
    [JS] JavascriptHelp (转载)
  • 原文地址:https://www.cnblogs.com/fenghuan/p/4867871.html
Copyright © 2011-2022 走看看