• Cocos网络篇[3.2](3) ——Socket连接(1)


    【唠叨】

        在客户端游戏开发中,使用HTTP进行网络通信的比较少,一般使用的都是Socket进行通信。而HTTP一般用于网页或者网页游戏。

        使用第三方Socket通信库:ODSocket


    【参考】

        http://blog.csdn.net/sight_/article/details/8138802 (Socket详解)

        http://blog.csdn.net/hguisu/article/details/7444092 (Socket编程原理)


    【源码下载】

        ODSocket库源码https://github.com/shahdza/Cocos_LearningTest/tree/master/ODSocket 

        本节Demo源码https://github.com/shahdza/Cocos_LearningTest/tree/master/ODSocket_Demo1 




    【Socket简介】


    1、套接字(socket)概念

        套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

    套接字: { IP地址 : 端口号 }

        应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。


    2、套接字类型  

        TCP/IP的socket提供下列三种类型套接字。 

      2.1、流式套接字(SOCK_STREAM)

        提供了一个面向连接(TCP)、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。文件传送协议(FTP)即使用流式套接字。 

      2.2、数据报式套接字(SOCK_DGRAM)

        提供了一个无连接服务(UDP)。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。网络文件系统(NFS)使用数据报式套接字。 

      2.3、原始式套接字(SOCK_RAW) 

        该接口允许对较低层协议,如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。


    3、建立socket连接

        建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。

        套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

        (a)服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。

        (b)客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

        (c)连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。


    4、典型套接字调用过程举例

        TCP/IP协议的应用一般采用客户/服务器模式,因此在实际应用中,必须有客户和服务器两个进程,并且首先启动服务器,其系统调用时序图如下。 

        面向连接的协议(TCP)的套接字系统调用如下:

            > 服务器必须首先启动,直到它执行完accept()调用,进入等待状态后,方能接收客户请求。

            > 假如客户在此前启动,则connect()将返回出错代码,连接不成功。

    wKioL1T4VXzzq03rAADiotwOa-I426.jpg

        无连接的协议(UDP)的套接字调用如下:

            > 无连接服务器也必须先启动,否则客户请求传不到服务进程。

            > 无连接客户不调用connect()。因此在数据发送之前,客户与服务器之间尚未建立完全相关,但各自通过socket()和bind()建立了半相关。

            > 发送数据时,发送方除指定本地套接字号外,还需指定接收方套接字号,从而在数据收发过程中动态地建立了全相关。 

    wKioL1T4XJujlOgTAAEmgJegDDQ728.jpg




    【Socket连接】

        使用的是面向连接的TCP套接字系统调用API。


    0、将ODSocket源码放在Classes目录下

    wKiom1T4YG3Q7SI3AACCGDQxWCs165.jpg


    1、客户端

        使用ODSocket的API实现与服务端的网络连接。

            > 创建ODSocket:ODSocket socket;

            > 初始化:Init() 、 Create();

            > 设置需要连接的服务器的 IP地址和端口号:ip 、 port;

            > 连接服务器:Connet(ip, port);

            > 发送数据:Send(string, lenght);

            > 接收数据:Recv(string, lenght, 0);

            > 关闭连接:Close();

        代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    //
    // 引入头文件
    #include "ODSocket/ODSocket.h"
     
    // Socker连接
    void HelloWorld::connectServer()
    {
        // 初始化
        // ODSocket socket;
        socket.Init();
        socket.Create(AF_INET, SOCK_STREAM, 0);
         
        // 设置服务器的IP地址,端口号
        // 并连接服务器 Connect
        const char* ip = "127.0.0.1";
        int port = 12345;
        bool result = socket.Connect(ip, port);
         
        // 发送数据 Send
        socket.Send("login", 5);
         
        if (result) {
            CCLOG("connect to server success!");
            // 开启新线程,在子线程中,接收数据
            std::thread recvThread = std::thread(&HelloWorld::receiveData, this);
            recvThread.detach(); // 从主线程分离
        }
        else {
            CCLOG("can not connect to server");
            return;
        }
    }
     
    // 接收数据
    void HelloWorld::receiveData()
    {
        // 因为是强联网
        // 所以可以一直检测服务端是否有数据传来
        while (true) {
            // 接收数据 Recv
            char data[512] = "";
            int result = socket.Recv(data, 512, 0);
            printf("%d", result);
            // 与服务器的连接断开了
            if (result <= 0) break;
             
            CCLOG("%s", data);
        }
        // 关闭连接
        socket.Close();
    }
    //


    2、服务端

        使用Eclipse开发环境,Java语言,服务端使用的是 ServerSocket 来监听端口。

        2.1、Server类

        用于创建ServerSocket,监听端口,等待客户连接。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //
    public class Server {
     
        public static void main(String[] args) throws IOException {
             
            // 创建ServerSocket,监听端口号:12345
            ServerSocket ss = new ServerSocket(12345);
            // 创建用于管理客户端的收发数据的子线程类
            ClientThread clientThread = new ClientThread();
            clientThread.start();
             
            System.out.println("服务器开启啦");
     
            // 监听端口号:12345
            // 等待客户连接 accept()
            while (true) {
                // 开始接收客户端的连接
                Socket socket = ss.accept();
                System.out.println("有新客户连接~");
                clientThread.addClient(socket);
            }
        }
    }
    //

        2.2、ClientThread类

        用于管理、处理客户端的收发数据请求。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    //
    // 继承Thread线程类
    public class ClientThread extends Thread {
        // 客户端连接的socket列表
        private ArrayList<Socket> clients = new ArrayList<Socket>();
     
        // 添加客户
        public void addClient(Socket socket) {
            clients.add(socket);
        }
        // 删除客户
        public void removeClient(Socket socket) {
            clients.remove(socket);
        }
        // 向客户发送数据
        public void sendMessage(Socket socket, String data) throws IOException {
            // 给玩家发送数据
            OutputStream os = socket.getOutputStream();
            os.write(data.getBytes("UTF-8"));
        }
     
        @Override
        public void run() {
            while (true) {
                try {
                    for (Socket socket : clients) {
                        // 获取客户端发来的数据
                        InputStream is = socket.getInputStream();
                        int len = is.available() + 1;
                        byte[] buff = new byte[len];
                        int flag = is.read(buff);
     
                        // read()返回-1,说明客户端的socket已断开
                        if (flag == -1) {
                            System.out.println("有客户断开连接~");
                            this.removeClient(socket);
                            break;
                        }
     
                        // 输出接收到的数据
                        String read = new String(buff);
                        System.out.println("收到数据:" + read);
     
                        // 给玩家发送数据
                        String data = "恭喜你,连接成功啦~~";
                        sendMessage(socket, data);
                    }
                    sleep(10);
                catch (IOException e) {
                    e.printStackTrace();
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //


    3、运行服务器

        在Eclipse开发工具中,运行服务器程序。

    wKioL1T4ZPSzVwF1AACwrBtVgZk631.jpg


    4、运行客户端

        然后运行客户端试试。

        客户端输出:

    wKioL1T4ZV7wQOXOAAC90bbKLnk301.jpg

        服务端输出:

    wKiom1T4ZFahJIbHAACGKVdOmg0347.jpg


    5、关闭客户端程序

        服务端输出:

    wKioL1T4ZafwYHPVAACkPM2wI_c458.jpg


  • 相关阅读:
    HDU 5213 分块 容斥
    HDU 2298 三分
    HDU 5144 三分
    HDU 5145 分块 莫队
    HDU 3938 并查集
    HDU 3926 并查集 图同构简单判断 STL
    POJ 2431 优先队列
    HDU 1811 拓扑排序 并查集
    HDU 2685 GCD推导
    HDU 4496 并查集 逆向思维
  • 原文地址:https://www.cnblogs.com/Anzhongliu/p/6091757.html
Copyright © 2020-2023  润新知