国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

從零開始搭建游戲服務(wù)器 第一節(jié) 創(chuàng)建一個(gè)簡單的服務(wù)器架構(gòu)

這篇具有很好參考價(jià)值的文章主要介紹了從零開始搭建游戲服務(wù)器 第一節(jié) 創(chuàng)建一個(gè)簡單的服務(wù)器架構(gòu)。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

引言

由于現(xiàn)在java web太卷了,所以各位同行可以考慮換一個(gè)賽道,做游戲還是很開心的。

本篇教程給新人用于學(xué)習(xí)游戲服務(wù)器的基本知識(shí),給新人們一些學(xué)習(xí)方向,有什么錯(cuò)誤的地方歡迎各位同行進(jìn)行討論。

技術(shù)選型

本篇教程預(yù)計(jì)使用Java+Redis+Mongo

正文

本著先完成再完美的原則,從最簡單的echo服務(wù)器開始。

從零開始搭建游戲服務(wù)器 第一節(jié) 創(chuàng)建一個(gè)簡單的服務(wù)器架構(gòu)

Echo服務(wù)器就是,客戶端發(fā)什么數(shù)據(jù),服務(wù)端就原樣返回回去。

創(chuàng)建基礎(chǔ)架構(gòu)

IDEA創(chuàng)建項(xiàng)目

從零開始搭建游戲服務(wù)器 第一節(jié) 創(chuàng)建一個(gè)簡單的服務(wù)器架構(gòu)

我這邊用Gradle進(jìn)行依賴管理,使用的版本為 gradle8.0.2, openjdk19.

修改build.gradle導(dǎo)入幾個(gè)基礎(chǔ)開發(fā)包。

dependencies {
    //網(wǎng)絡(luò)
    implementation group: 'io.netty', name: 'netty-all', version: '4.1.90.Final'
    //spring
    implementation group: 'org.springframework', name: 'spring-context', version: '6.0.6'
    //log
    implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.36'
    implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.2.11'
    implementation group: 'ch.qos.logback', name: 'logback-access', version: '1.2.11'
    implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.11'
    //lombok
    compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.24'
}

創(chuàng)建Bean配置類

@Configuration
@ComponentScan(basePackages = {"com.wfgame"})
public class GameBeanConfiguration {
}

創(chuàng)建主類

@Component
@Slf4j
public class GameMain {
    public static void main(String[] args) {
        // 初始化Spring
        AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext(GameBeanConfiguration.class);
        springContext.start();
        log.info("server start!");
    }
}

運(yùn)行一下,正常輸出server start!

我們會(huì)發(fā)現(xiàn),程序執(zhí)行后馬上停止了,對于游戲服務(wù)器來說,我們需要保持運(yùn)行狀態(tài),等待玩家接入進(jìn)行游戲。所以我們main中增加一個(gè)循環(huán),不停讀取控制臺(tái)輸入,當(dāng)讀取到控制臺(tái)輸入stop時(shí),我們再進(jìn)行停服。

修改main方法如下:

    public static void main(String[] args) {
        // 初始化Spring
        AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext(GameBeanConfiguration.class);
        springContext.start();

        log.info("server start!");

        //region 處理控制臺(tái)輸入,每秒檢查一遍 stopFlag,為true就跳出循環(huán),執(zhí)行關(guān)閉操作
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // 設(shè)置循環(huán)使服務(wù)器不立刻停止
        while (true) {
            if (stopFlag) {
                log.info("receive stop flag, server will stop!");
                break;
            }
            // 每次循環(huán)停止一秒,避免循環(huán)頻率過高
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //處理控制臺(tái)指令
            try {
                if (br.ready()) {
                    String cmd = br.readLine().trim();
                    if (cmd.equals("stop")) {//正常關(guān)服
                        stopFlag = true;
                        log.info("Receive stop flag, time to stop.");
                    } else {
                        log.info("Unsupported cmd:{}", cmd);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //停掉虛擬機(jī)
        System.exit(0);
    }

這樣我們就獲得了一個(gè)可以控制停服的服務(wù)器。當(dāng)我們控制臺(tái)輸入stop時(shí),程序結(jié)束運(yùn)行。

添加Netty監(jiān)聽端口

要與客戶端進(jìn)行TCP連接,需要建立socket通道,然后通過socket通道進(jìn)行數(shù)據(jù)交互。

傳統(tǒng)BIO一個(gè)線程一個(gè)連接,有新的連接進(jìn)來時(shí)就要?jiǎng)?chuàng)建一個(gè)線程,并持續(xù)讀取數(shù)據(jù)流,當(dāng)這個(gè)連接發(fā)送任何請求時(shí),會(huì)對性能造成嚴(yán)重浪費(fèi)。

NIO一個(gè)線程通過多路復(fù)用器可以監(jiān)聽多個(gè)連接,通過輪詢判斷連接是否有數(shù)據(jù)請求。

Netty對java原生NIO進(jìn)行了封裝,簡化了代碼,便于我們的使用。

Netty的包我們之前已經(jīng)導(dǎo)入過了,直接拿來用即可。

首先我們創(chuàng)建一個(gè)Netty自定義消息處理類。

@Sharable
public class NettyMessageHandler extends SimpleChannelInboundHandler<Object> {
    /**
     * 讀取數(shù)據(jù)
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        this.doRead(ctx, msg);
    }

    private void doRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("received msg = : " + msg);
        // 馬上將原數(shù)據(jù)返回
        ctx.writeAndFlush(msg);
    }
}

然后編寫Netty服務(wù)器啟動(dòng)代碼,我們修改GameMain類的代碼

@Component
@Slf4j
public class GameMain {

    // 停服標(biāo)志
    private static boolean stopFlag = false;

    public static void main(String[] args) {
        // 初始化Spring
        AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext(GameBeanConfiguration.class);
        springContext.start();

        // 啟動(dòng)Netty服務(wù)器
        try {
            startNetty();
            log.info("Netty server start!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        log.info("server start!");

        //region 處理控制臺(tái)輸入,每秒檢查一遍 stopFlag,為true就跳出循環(huán),執(zhí)行關(guān)閉操作
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // 設(shè)置循環(huán)使服務(wù)器不立刻停止
        while (true) {
            if (stopFlag) {
                log.info("receive stop flag, server will stop!");
                break;
            }
            // 每次循環(huán)停止一秒,避免循環(huán)頻率過高
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //處理控制臺(tái)指令
            try {
                if (br.ready()) {
                    String cmd = br.readLine().trim();
                    if (cmd.equals("stop")) {//正常關(guān)服
                        stopFlag = true;
                        log.info("Receive stop flag, time to stop.");
                    } else {
                        log.info("Unsupported cmd:{}", cmd);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //停掉虛擬機(jī)
        System.exit(0);
    }

    /**
     * 啟動(dòng)netty服務(wù)器
     */
    private static void startNetty() throws InterruptedException {
        int port = 2333;
        log.info("Netty4SocketServer start---Normal, port = " + port);

        final NioEventLoopGroup bossGroup = new NioEventLoopGroup(2);
        final NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup);
        bootstrap.channel(NioServerSocketChannel.class);
        bootstrap.option(ChannelOption.SO_REUSEADDR, true);//允許重用端口
        bootstrap.option(ChannelOption.SO_BACKLOG, 512);//允許多少個(gè)新請求進(jìn)入等待
        bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);//是否使用內(nèi)存池
        bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);    // 保持連接活動(dòng)
        bootstrap.childOption(ChannelOption.TCP_NODELAY, false);    // 禁止Nagle算法等待更多數(shù)據(jù)合并發(fā)送,提高信息及時(shí)性
        bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);//是否使用內(nèi)存池

        final NettyMessageHandler handler = new NettyMessageHandler();
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline cp = ch.pipeline();
                cp.addLast(new StringDecoder());
                cp.addLast(new StringEncoder());
                cp.addLast("handler", handler);
            }
        });
        // 綁定并監(jiān)聽端口
        bootstrap.bind(port).sync();//線程同步阻塞等待服務(wù)器綁定到指定端口

        // 優(yōu)雅停機(jī)
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }));

        log.info("Netty4SocketServer ok,bind at :" + port);
    }

我們先創(chuàng)建了一個(gè)startNetty()方法,用于啟動(dòng)Netty服務(wù)器,同時(shí)綁定了端口2333

我們要注意一下initChannel這塊代碼,我們注冊了String編碼解碼器,他們是用換行符作為一個(gè)消息的結(jié)束標(biāo)志,因此我們等下通過客戶端發(fā)送消息過來需要在行尾添加換行符。同時(shí)將我們自定義的消息處理類也注冊進(jìn)pipeline中,當(dāng)客戶端發(fā)送消息過來,先通過StringDecoder進(jìn)行解碼,然后流入自定義處理類中進(jìn)行下一步處理。

至此服務(wù)端Netty接入完畢,我們下面編寫一個(gè)客戶端進(jìn)行測試。

編寫客戶端進(jìn)行測試

我們增加了ClientMain類,用socket與服務(wù)器進(jìn)行連接,讀取控制臺(tái)輸入上行到服務(wù)器,同時(shí)接受服務(wù)器下行的消息。

public class ClientMain {

    private static Socket socket = null;
    private static BufferedReader br = null;
    private static BufferedWriter writer = null;
    private static BufferedReader receivedBufferedReader = null;
    public static void main(String[] args) {
        // 新增連接到服務(wù)器
        startSocket();
    }

    /**
     * 啟動(dòng)socket連接
     */
    private static void startSocket() {
        try {
            socket = new Socket("127.0.0.1", 2333);
            br = new BufferedReader(new InputStreamReader(System.in));
            writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            receivedBufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            new Thread(() -> {
                try {
                    while (true) {
                        Thread.sleep(1000L);
                        String s = receivedBufferedReader.readLine();
                        if (s!=null && !s.equals("")) {
                            System.out.println("receive: " + s);
                        }
                    }
                } catch (IOException | InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            while (true) {
                Thread.sleep(1000L);
                if (br.ready()) {
                    writer.write(br.readLine().trim() + "\n");
                    writer.flush();
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                if (receivedBufferedReader != null) {
                    receivedBufferedReader.close();
                }
                if (writer != null) {
                    writer.close();
                }
                if (br != null) {
                    br.close();
                }
                if (socket != null) {
                    socket.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

測試一下,我們先運(yùn)行服務(wù)器,再運(yùn)行客戶端。

在客戶端控制臺(tái)下輸入測試信息。

從零開始搭建游戲服務(wù)器 第一節(jié) 創(chuàng)建一個(gè)簡單的服務(wù)器架構(gòu)

可以成功進(jìn)行信息交互

總結(jié)

本節(jié)一共做了這么幾件事:

  1. 項(xiàng)目的初步創(chuàng)建,通過build.gradle進(jìn)行依賴包的管理。
  2. Netty服務(wù)器的啟動(dòng),并且不斷監(jiān)聽控制臺(tái)輸入,客戶端上行數(shù)據(jù)的讀取。
  3. 編寫測試用客戶端,與服務(wù)器進(jìn)行數(shù)據(jù)交互。

下一節(jié)將進(jìn)行注冊登錄的開發(fā),內(nèi)容將會(huì)比較多,感興趣的點(diǎn)點(diǎn)關(guān)注或者留言評論。文章來源地址http://www.zghlxwxcb.cn/news/detail-497691.html

到了這里,關(guān)于從零開始搭建游戲服務(wù)器 第一節(jié) 創(chuàng)建一個(gè)簡單的服務(wù)器架構(gòu)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 從零開始搭建高效的文件服務(wù)器:FastDFS與Nginx完美結(jié)合,內(nèi)網(wǎng)穿透實(shí)現(xiàn)公網(wǎng)訪問

    從零開始搭建高效的文件服務(wù)器:FastDFS與Nginx完美結(jié)合,內(nèi)網(wǎng)穿透實(shí)現(xiàn)公網(wǎng)訪問

    目錄 前言 1. 本地搭建FastDFS文件系統(tǒng) 1.1 環(huán)境安裝 1.2 安裝libfastcommon 1.3 安裝FastDFS 1.4 配置Tracker 1.5 配置Storage 1.6 測試上傳下載 1.7 與Nginx整合 1.8 安裝Nginx 1.9 配置Nginx 2. 局域網(wǎng)測試訪問FastDFS 3. 安裝cpolar內(nèi)網(wǎng)穿透 4. 配置公網(wǎng)訪問地址 5. 固定公網(wǎng)地址 5.1 保留二級(jí)子域名 5.2 配置

    2024年02月03日
    瀏覽(36)
  • 從零開始用Nodejs搭建一個(gè)MQTT服務(wù)器,并且用stm32通過esp8266進(jìn)行消息訂閱和發(fā)布

    從零開始用Nodejs搭建一個(gè)MQTT服務(wù)器,并且用stm32通過esp8266進(jìn)行消息訂閱和發(fā)布

    最近在做一個(gè)物聯(lián)網(wǎng)項(xiàng)目,需要用到服務(wù)器進(jìn)行數(shù)據(jù)的存儲(chǔ)和數(shù)據(jù)的請求和發(fā)送,之前我用過onenet平臺(tái)上的http服務(wù),雖然能通過get和post請求進(jìn)行數(shù)據(jù)的提交和發(fā)送,但是平臺(tái)上的數(shù)據(jù)發(fā)生改變卻不能主動(dòng)推送給esp8266,與我此次的項(xiàng)目不符合,所以pass。然后我了解了下mqtt協(xié)

    2024年02月04日
    瀏覽(32)
  • 服務(wù)器從零開始配環(huán)境

    服務(wù)器從零開始配環(huán)境

    寶塔,xftp,xshell 阿里云的話就是在實(shí)例那里點(diǎn)遠(yuǎn)程連接,輸入: yum install -y wget wget -O install.sh http://download.bt.cn/install/install_6.0.sh sh install.sh 中途輸入y確定安裝 安裝成功就是這樣的頁面: 在軟件商店下好常用的軟件 服務(wù)器買的核數(shù)小,每次在寶塔安裝mysql都會(huì)崩,得自己手

    2024年01月22日
    瀏覽(42)
  • 華為云云服務(wù)器評測 | 從零開始:云耀云服務(wù)器L實(shí)例的全面使用解析指南

    華為云云服務(wù)器評測 | 從零開始:云耀云服務(wù)器L實(shí)例的全面使用解析指南

    本文收錄在專欄:#云計(jì)算入門與實(shí)踐 - 華為云 專欄中,本系列博文還在更新中 相關(guān)華為云云耀云服務(wù)器L實(shí)例評測文章列表如下: 華為云云耀云服務(wù)器L實(shí)例評測 | 從零開始:云耀云服務(wù)器L實(shí)例的全面使用解析指南 華為云云耀云服務(wù)器L實(shí)例評測|輕量級(jí)應(yīng)用服務(wù)器對決:基

    2024年02月10日
    瀏覽(92)
  • 從零開始寫一個(gè)RTSP服務(wù)器(一)RTSP協(xié)議講解

    從零開始寫一個(gè)RTSP服務(wù)器(一)RTSP協(xié)議講解

    為什么要寫這個(gè)系列? 因?yàn)槲易约涸趯W(xué)習(xí)rtsp協(xié)議想自己從零寫一個(gè)rtsp服務(wù)器的時(shí)候,由于rtsp比較復(fù)雜,所以覺得這個(gè)過程非常的困難,網(wǎng)上許多相關(guān)文章或模棱兩可,或是復(fù)制粘貼。所以想寫這樣一個(gè)系列,來幫助想要學(xué)習(xí)rtsp協(xié)議或者想要從零寫一個(gè)rtsp服務(wù)器的初學(xué)者

    2024年04月17日
    瀏覽(32)
  • 從零開始寫一個(gè)RTSP服務(wù)器(二)RTSP協(xié)議的實(shí)現(xiàn)

    此系列只追求精簡,旨在學(xué)習(xí)RTSP協(xié)議的實(shí)現(xiàn)過程,不追求復(fù)雜完美,所以這里要實(shí)現(xiàn)的RTSP服務(wù)器為了簡單,實(shí)現(xiàn)上同一時(shí)間只能有一個(gè)客戶端,下面開始介紹實(shí)現(xiàn)過程 在寫一個(gè)RTSP服務(wù)器之前,我們必須知道一個(gè)RTSP服務(wù)器最簡單的包含兩部分,一部分是RTSP的交互,一部分是

    2024年04月17日
    瀏覽(25)
  • 從零開始實(shí)現(xiàn)一個(gè)C++高性能服務(wù)器框架----環(huán)境變量模塊

    此項(xiàng)目是根據(jù)sylar框架實(shí)現(xiàn),是從零開始重寫sylar,也是對sylar豐富與完善 項(xiàng)目地址:https://gitee.com/lzhiqiang1999/server-framework 項(xiàng)目介紹 :實(shí)現(xiàn)了一個(gè)基于協(xié)程的服務(wù)器框架,支持多線程、多協(xié)程協(xié)同調(diào)度;支持以異步處理的方式提高服務(wù)器性能;封裝了網(wǎng)絡(luò)相關(guān)的模塊,包括

    2024年02月02日
    瀏覽(26)
  • 從零開始實(shí)現(xiàn)一個(gè)C++高性能服務(wù)器框架----Socket模塊

    此項(xiàng)目是根據(jù)sylar框架實(shí)現(xiàn),是從零開始重寫sylar,也是對sylar豐富與完善 項(xiàng)目地址:https://gitee.com/lzhiqiang1999/server-framework 項(xiàng)目介紹 :實(shí)現(xiàn)了一個(gè)基于協(xié)程的服務(wù)器框架,支持多線程、多協(xié)程協(xié)同調(diào)度;支持以異步處理的方式提高服務(wù)器性能;封裝了網(wǎng)絡(luò)相關(guān)的模塊,包括

    2023年04月08日
    瀏覽(32)
  • 從零開始實(shí)現(xiàn)一個(gè)C++高性能服務(wù)器框架----Hook模塊

    此項(xiàng)目是根據(jù)sylar框架實(shí)現(xiàn),是從零開始重寫sylar,也是對sylar豐富與完善 項(xiàng)目地址:https://gitee.com/lzhiqiang1999/server-framework 項(xiàng)目介紹 :實(shí)現(xiàn)了一個(gè)基于協(xié)程的服務(wù)器框架,支持多線程、多協(xié)程協(xié)同調(diào)度;支持以異步處理的方式提高服務(wù)器性能;封裝了網(wǎng)絡(luò)相關(guān)的模塊,包括

    2023年04月09日
    瀏覽(24)
  • C++ Webserver從零開始:基礎(chǔ)知識(shí)(三)——Linux服務(wù)器程序框架

    C++ Webserver從零開始:基礎(chǔ)知識(shí)(三)——Linux服務(wù)器程序框架

    目錄 前言 一.服務(wù)器編程基礎(chǔ)框架 C/S模型 主要框架 二.I/O模型 阻塞I/O 非阻塞I/O 異步I/O 三.兩種高效的事件處理模式 Reactor Proactor 四.模擬Proactor模式 五.半同步/半異步的并發(fā)模式 六.有限狀態(tài)機(jī) 七.其他提高服務(wù)器性能的方法 池 數(shù)據(jù)復(fù)制 上下文切換和鎖 ????????這一章是

    2024年02月22日
    瀏覽(25)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包