zoukankan      html  css  js  c++  java
  • C++ Socket编程

    在很多底层网络应用开发者的眼里一切编程都是Socket,话虽然有点夸张,但却也几乎如此了,现在的网络编程几乎都是用Socket来编程。你想过这些情景么?我们每天打开浏览器浏览网页时,浏览器进程怎么和Web服务器进行通信的呢?当你用QQ聊天时,QQ进程怎么和服务器或者是你的好友所在的QQ进程进行通信的呢?当你打开PPstream观看视频时,PPstream进程如何与视频服务器进行通信的呢? 如此种种,都是靠Socket来进行通信的,以一斑窥全豹,可见Socket编程在现代编程中占据了多么重要的地位,这一节我们将介绍C++语言中如何进行Socket编程。

    什么是Socket?

    Socket起源于Unix,而Unix基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用:Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

    Socket类型有三种:流式Socket(SOCK_STREAM),数据报式Socket(SOCK_DGRAM)和原始套接字(SOCK_RAW)。

    • 流式套接字(SOCK_STREAM):流套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。流套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP(The Transmission Control Protocol)协议。
    • 数据包套接字(SOCK_DGRAM):数据包套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据包套接字使用UDP(User Datagram Protocol)协议进行数据的传输。由于数据包套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。
    • 原始套接字(SOCK_RAW):允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW

    Socket如何通信

    网络中的进程之间如何通过Socket通信呢?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中需要互相通信的进程,就可以利用这个标志在他们之间进行交互。请看下面这个TCP/IP协议结构图

    使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是为什么说“一切皆Socket”。

    Windows Sockets API (WSA), 简短记为Winsock, 是Windows的TCP/IP网络编程接口(API)。后面都会以winsock进行举例说明。

    应用场景

    基于TCP的Socket编程最常见的应用场景是在C/S架构下的分布式应用,针对客户端和服务器端提供不同的Socket系统调用。

      1. 服务器端编程的步骤:

      1.1:加载套接字库,创建套接字(WSAStartup()/socket());

      1.2:绑定套接字到一个IP地址和一个端口上(bind());

      1.3:将套接字设置为监听模式等待连接请求(listen());

      1.4:请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());

      1.5:用返回的套接字和客户端进行通信(send()/recv());

      1.6:返回,等待另一连接请求;

      1.7:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

      2. 客户端编程的步骤:

      2.1:加载套接字库,创建套接字(WSAStartup()/socket());

      2.2:向服务器发出连接请求(connect());

      2.3:和服务器端进行通信(send()/recv());

      2.4:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

      3. 示例代码

      3.1 Servers端

    #include <Winsock2.h>
    #include <cstdio>
    #pragma comment(lib,"ws2_32.lib")
    void main()
    {
        WSADATA wsaData;
        SOCKET sockServer;
        SOCKADDR_IN addrServer;
        SOCKET sockClient;
        SOCKADDR_IN addrClient;
        WSAStartup(MAKEWORD(2,2),&wsaData);
        sockServer=socket(AF_INET,SOCK_STREAM,0);
        addrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//INADDR_ANY表示任何IP
        addrServer.sin_family=AF_INET;
        addrServer.sin_port=htons(6000);//绑定端口6000
        bind(sockServer,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
    
        //Listen监听端
        listen(sockServer,5);//5为等待连接数目
        printf("服务器已启动:
    监听中...
    ");
        int len=sizeof(SOCKADDR);
        charsendBuf[100];//发送至客户端的字符串
        charrecvBuf[100];//接受客户端返回的字符串
    
        //会阻塞进程,直到有客户端连接上来为止
        sockClient=accept(sockServer,(SOCKADDR*)&addrClient,&len);
        //接收并打印客户端数据
        recv(sockClient,recvBuf,100,0);
        printf("%s
    ",recvBuf);
    
        //关闭socket
        closesocket(sockClient);
        WSACleanup();
    }
    

      3.2 Client 端

    #include <Winsock2.h>
    #include <cstdio>
    #pragma comment(lib,"ws2_32.lib")
    void main()
    {
        WSADATA wsaData;
        SOCKET sockClient;//客户端Socket
        SOCKADDR_IN addrServer;//服务端地址
        WSAStartup(MAKEWORD(2,2),&wsaData);
        //新建客户端socket
        sockClient=socket(AF_INET,SOCK_STREAM,0);
        //定义要连接的服务端地址
        addrServer.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//目标IP(127.0.0.1是回送地址)
        addrServer.sin_family=AF_INET;
        addrServer.sin_port=htons(6000);//连接端口6000
        //连接到服务端
        connect(sockClient,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
        //发送数据
        char message[20]="HelloSocket!";
        send(sockClient,message,strlen(message)+1,0);
        //关闭socket
        closesocket(sockClient);
        WSACleanup();
    }
    

      

  • 相关阅读:
    cesium图形上加载图片
    cesium可视化空间数据2
    linux命令之用户和用户组
    YARN应用程序开发和设计流程
    Yarn中几个专用名称
    break、continue、return之间的区别与联系
    kafka的相关操作脚本
    scala函数进阶篇
    scala的基础部分
    视图
  • 原文地址:https://www.cnblogs.com/zhanghu52030/p/9150211.html
Copyright © 2011-2022 走看看