c++ docker 服务器 部署
2017-07-28 12:54:00 weixin_33924220 阅读数 206

最近在编译一个C++的服务器代码并计划把它部署实施到一个Linux的系统上,它在Windows上已经可以编译运行了。

首先,为了提高开发的效率,我先选择了一款C++的IDE,因为平常已经习惯了使用jetbrains的工具,所以就先选择了CLion作为自己的开发工具。

安装CLion可以去官网。
其实应该可以选择其它的工具如:Eclipse等等,在我最近几天的使用过程中还是发现了一些问题,比如,不论是用CLion自己的调试还是GDB,单步调试时总是会跳来跳去,比如已经单步过了100行,但是下一个单步又会回到100行的位置。

然后,就是Linux系统上的环境搭建,在这里我使用的Linux系统是Ubuntu,有图形化界面。而我的工程里使用到了MySql,它的安装倒是花了我不少时间,因为我需要使用MySql C++ Connector。这里我有一个不能理解的问题就是,MySql官方提供的和Windows上使用居然是不一样的。

安装MySqlServer,再安装MySqlClient,Lib等等。但是好在,有其它的工程提供了和Windows上一致的接口:MySqlDev,MySqlPP。这里有一个命令可以查看MySql已经安装的库。(这里我的思路还是有一些乱的,我用了网上的很多方式,现在我都不能确定是哪个是正确的,有时间的话再整理一下。)

$ mysql_config --libs

这样可以查看到库的目录,可以给Linker使用。

2018-05-03 23:36:42 len_yue_mo_fu 阅读数 9264

版权声明:本文为博主原创文章,未经博主允许不得转载。

#Docker介绍
Docker是一个开源的容器引擎,它有助于更快地交付产品。Docker可将应用程序和基础设施层隔离,并且将基础设施当作程序一样进行管理。使用Docker,可以更快地打包,测试以及部署应用程序,并可以缩短从编程到部署运行代码的周期

docker部署c/c++程序

关于docker的使用网上有很多的教程但是很少有介绍如何使用docker来部署一个c/c++程序,笔者作为一个c/c++程序员,在学习docker的时候没有找到相关的使用,经过博主这几天的研究使用终于在docker中成功的运行了c/c++程序,下面博主就来介绍一下使用方法:

想要把c/c++程序运行在docker的容器中,就必须先创建一个docker镜像,通过镜像创建容器,来使我们的程序在容器中运行起来。
#######简单介绍一下docker中镜像和容器的关系:
• Images (镜像)
Docker镜像是一个只读模板,包含创建Docker容器的说明。Docker镜像可以运行Docker镜像中的程序。
• Container (容器)
容器是镜像的可运行实例。镜像与容器类似与面向对象中类与对象的关系。可通过Docker API或者CLI命令起停,移动,删除等。

明白了docker中镜像和容器的关系之后,我们想要把程序执行起来,其实就是将程序放在镜像中,通过镜像启动一个容器,在容器中执行我们的程序。

那么我们运行一个c/c++程序到底该选择怎么样的镜像呢?其实也很简单,我们只要知道我们的程序如果不使用docker他是在什么系统或者说是环境中使用,那么我们就可以通过docker官方的仓库去下载这样的镜像来供我们创建包含我们程序的镜像了。

我们现在有这样的一段代码,功能就是给一个叫t.txt的文件中写hello world!!!。下面我们就通过这个简单的代码来示范如果把一个c/c++程序放到docker镜像中制作一个新的镜像

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


int main()
{
	FILE* file = fopen("t.txt","w+");
	if (file == NULL)
	{
		return 0;
	}

	char buf[20]="hello world!!!\n";
	int len = strlen(buf);

	while(1)
	{
		fputs(buf,file);
		fflush(file);
	//	printf("%s",buf);
		sleep(1);
	}

	fclose(file);

	return 0;
}

1. 镜像的选择:

c/c++程序我们都是直接运行在linux系统上,所以我们可以直接选择centos或者ubuntu镜像,在这里我们是要把.c文件编译成一个可执行程序,docker还有一个gcc或者g++的镜像,使用gcc或者g++镜像的话,我们就不需要在ubunt或者centos镜像中再安装gcc 、g++了。

推荐三种镜像:

  1. gcc 或者g++
  2. ubuntu
  3. centos

2. 下载镜像到宿主主机:

//产看版本:
docker search gcc

下面是执行结果:
这里写图片描述
这里我们选择第一个下载:

//下载镜像
docker pull gcc
//查看本地已经下载的镜像
docker images

这里写图片描述

3. 使用gcc镜像制作我们自己的镜像:

制作镜像有两种方式,在这里就不做赘述了,可以产看博主单独接受制作镜像的博客:
在这里我们通过Dockerfile的方式创建自己的镜像,下面是Dockerfile的内容:

FROM gcc:latest

RUN  mkdir /usr/src/myapp

COPY test.c /usr/src/myapp

WORKDIR /usr/src/myapp

RUN  gcc test.c 

CMD ["./a.out"]

执行命令:

docker build -t mygcc-test:v1 .
//再次执行
docker images
//这时我们就可以看到在本地镜像中多了一个叫做mygcc-test的镜像,TAG为v1

这里写图片描述
##4. 启动制作好的镜像:

//执行命令:
docker run -d mygcc-test:v1 //启动镜像
//执行命令:
docker ps //查看容器的运行情况

这里写图片描述

//接下来我们进入到容器当中,查看a.out程序在后台执行的情况:
docker exec -it mystifying_mclean /bin/bash

进入到容器之后我们执行top命令,查看a.out:
这里写图片描述
查看当前目录发现已经生成了t.txt文件说明,a.tou程序执行正常:
这里写图片描述

##进阶教程:

1.直接将一个可执行程序复制到镜像中,制作镜像

还是刚才那个test.c文件,我们直接在本地虚拟机,将他编译成可执行程序a.out,下面是Dockerfile的内容:

FROM gcc:latest

RUN  mkdir /usr/src/myapp

COPY a.out /usr/src/myapp

WORKDIR /usr/src/myapp

#RUN  gcc test.c 

CMD ["./a.out"]

我们通过下面命令制作一个名字是mygcc-test,但是tag是v2的镜像

docker build -t mygcc-test:v2 .

镜像制作好了之后,我们再运行一个该镜像的容器,还是通过上面的命令,在这里就不列出来了,直接上截图了,看看效果:
这里写图片描述
还是top,查看a.out是否在后台运行,在查看t.txt的执行情况:
这里写图片描述

2.将一个启动之前需要设置环境变量的可执行程序复制到镜像中,制作镜像:

现在我们有DataImportClient.tar.gz这样的一个可执行程序压缩包,解压之后的目录如下,lib是存放client程序所依赖的动态库,startup.sh是启动client程序的脚本,在startup.sh启动之前我们首先会设置环境变量LD_LIBRARY_PATH
这里写图片描述
下面我们还是通过Dockerfile 来创建一个镜像,这次我们选用ubuntu作为基础镜像,Dockerfile的内容如下:

FROM ubuntu:latest

ADD ./DataImportClient.tar.gz  /usr/src/myapp

WORKDIR /usr/src/myapp/DataImportClient

CMD ["./startup.sh"]

直接上截图了,通过上面的方法我们发现我们期待的结果并没有出现,docker ps 发现什么也没有:
这里写图片描述
这就很让人困扰了,按理说通过./startup.sh会将环境变量设置,并且执行./client,但是我们事实却并不是这样,我们查看日志,发现退出的原因是找不到动态库:
这里写图片描述

其实在docker中一个容器运行的时间长短,其实就是CMD后面跟的命令的执行时间,startup.sh就是一个脚本,执行结束之后就退出了,所以容器很快也就退出了。这个地方环境变量没有设置成功的可能原因是,docker容器总执行shell命令可能不是在同一个终端,也就是不是同一个进程中,设置了变量在./client中并不能生效。
startup.sh的内容如下:

#!/bin/bash

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib

./client &

但是我们观察COMMAND发现命令是**"./client",所有可以大致推断出,CMD一定是将startup中的命令,分成多个CMD来执行,但是Dockerfile**的语法是,CMD只执行最后一个,这样就能解释为什么找不到动态库的原因了。
这里写图片描述

正确的Dockerfile:

FROM ubuntu:latest

ADD ./DataImportClient.tar.gz  /usr/src/myapp

WORKDIR /usr/src/myapp/DataImportClient

ENV  LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH.

#RUN echo $LD_LIBRARY_PATH

CMD ["./client"]

通过ENV来设置环境变量LD_LIBRARY_PATH
创建镜像之后,我们再次启动一个容器,看看效果:
这里写图片描述

如果要做一个很小的镜像,如果有需求的伙伴可以查看博主下面文章:https://blog.csdn.net/len_yue_mo_fu/article/details/88899743

以上就是如何使用docker来部署一个c/c++的程序了,如果小伙伴们有什么问题,欢迎大家留言提问。

欢迎关注问我团队公众号:

在这里插入图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

2011-03-01 21:40:00 wapysun 阅读数 0

MMORPG不同于其它的局域网的网络游戏,它是一个面向整个Internet的连接人数过万的网络游戏,因此他的服务器端设计则极为重要
  
  服务器的基本设置
  
  在大型网络游戏里,通常设计为C/S结构,客户端不再对数据进行逻辑处理,而只是一个收发装置,从玩家那里接受到操作信息,然后反馈给服务器,再由服务器进行处理后发回客户端,经客户端通过图形化处理,给玩家呈现出一个缤纷的游戏世界。
   
  登陆服务器
  
  在这里也可以称之为连接服务器,网络游戏的客户端一般是连接到这里,然后再由该连接服务器根据不同的需要,把游戏消息转发给其它相应的服务器(逻辑和地图服务器)也因为它是客户端直接连接的对象,它同时也负担了验证客户身份的工作。
  
  地图服务器
  
  在这里也可以称之为连续事件服务器。在这个服务器里要处理的对象(玩家)所做的动作都是一个连续事件。例如玩家从A点移动到B点,这样一个动作,需要一定的时间进行移动,因此说移动是一个连续事件。
  
  逻辑服务器
  
在这里可以称之为瞬时事件服务器,在这个服务器里,处理对象(玩家)所做的动作均可以在非常断时间内完成完成。例如玩家从商店购买一瓶药书,当玩家确认购买后,服务器先扣除玩家的游戏币,然后再把相应的药水瓶加入玩家的背包里。这2个操作对于服务器来说,只是2个数字的加减,计算完这两个数字的加减,这个事件就可以结束了。因此,我们可以说这个事件是一个瞬时事件
  
  服务器组的改进
  
  不过在实际应用的过程中,游戏服务器的结构要比上面所说的3种服务结构要复杂些,不过也都是在这3种最基本的服务器架构下进行扩充,扩充的主要是其它辅助功能。在实际应用里可能增加的2种服务器,数据库服务器,计费服务器,由逻辑服务器独立出来的聊天服务器。
   
  数据库服务器
  
  数据库服务器其实就是专门利用一台服务器进行数据库的读写操作。这点特别是在大型的网络游戏里尤为重要。因为在大型网络游戏里,要处理玩家的数据量非常大,如果不利用专门的服务器进行处理,很有可能会拖累这个服务器组。
  
  计费服务器
  
  通常在商业的网络游戏里出现,用于记录玩家在线的时间,给收费提供依据,同时也是整个服务器组里最重要的部分,一旦出现问题,运营商就不用赚钱了。
  
  聊天服务器
  
  在游戏里的聊天功能是属于一种瞬时动作,理论上是放在逻辑服务器里进行处理。不过在大型网络游戏里,因为这个部分功能与游戏里的其它部分联系并不紧密,因此可以独立出来做一个功能服务器。
  
  服务器的集群设置
  
  在大型游戏的应用过程中,实际需要处理的玩家数量可能过万,一台普通的服务器是无法完成所要完成的工作,因此,在实际应用的时候,通常是由一组多台服务器共同完成一个功能。
  例如地图服务器,可以根据需要,把游戏里所有的地域进行划分,划分为N个区域,然后让这一个区域里发生的事件都用一个特定的服务器进行处理。这样做的目的是减少一个服务器所承担的计算量,把整个系统组成一个分布式的网络。
不过这样做的同时会造成一个麻烦:当一位玩家从区域1,移动到区域2。这个时候,就必须先在服务器1里把玩家删除,然后再在区域2里加入玩家。同时需要由服务器1向服务器2转移玩家的数据信息(因为服务器组在工作的时候,玩家的信息只能保存在当前所在区域的服务器里),也就是说一旦玩家发生服务器间区域移动,服务器端就不可避免的造成数据通讯。因为这种移动并不是有规律的,玩家所在的服务器都有可能到达其它服务器。这样,如果服务器组里有N台地图服务器,那么,每个服务器都可能向其它N-1台服务器产生连接,总共就可能产生N×N个连接。如此数量连接如果只是使用普通的socket设计,就很有可能会给服务器通讯间的各种问题所困扰,为此,在商业网络游戏的服务器之间,通常都使用成熟的第三方的通讯中间件,如ACE,ICE等作为网络连接的传输层。
  

2018-04-09 16:11:11 www5256246 阅读数 140
#include <WinSock2.h>  
#include <stdio.h>  
#include <stdlib.h>  

#pragma comment(lib, "ws2_32.lib")  

void main()
{
    WSADATA wsaData;
    int port = 5099;

    char buf[] = "Server: hello, I am a server.....";

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        printf("Failed to load Winsock");
        return;
    }

    //创建用于监听的套接字  
    SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);

    SOCKADDR_IN addrSrv;
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(port); //1024以上的端口号  
    addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

    int retVal = bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));
    if (retVal == SOCKET_ERROR) {
        printf("Failed bind:%d\n", WSAGetLastError());
        return;
    }

    if (listen(sockSrv, 10) == SOCKET_ERROR) {
        printf("Listen failed:%d", WSAGetLastError());
        return;
    }

    SOCKADDR_IN addrClient;
    int len = sizeof(SOCKADDR);

    while (1)
    {
        //等待客户请求到来    
        SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrClient, &len);
        if (sockConn == SOCKET_ERROR) {
            printf("Accept failed:%d", WSAGetLastError());
            break;
        }
        printf("Accept failed:%d");
    //    printf("Accept client IP:[%s]\n", inet_ntoa(addrClient.sin_addr));

        //发送数据  
        int iSend = send(sockConn, buf, sizeof(buf), 0);
        if (iSend == SOCKET_ERROR) {
            printf("send failed");
            break;
        }

        char recvBuf[100];
        memset(recvBuf, 0, sizeof(recvBuf));
        //      //接收数据  
        recv(sockConn, recvBuf, sizeof(recvBuf), 0);
        printf("%s\n", recvBuf);

        closesocket(sockConn);
    }

    closesocket(sockSrv);
    WSACleanup();
    system("pause");

}

客户端

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

#pragma comment(lib, "ws2_32.lib")  

void main()
{

    //加载套接字  
    WSADATA wsaData;
    char buff[1024];
    memset(buff, 0, sizeof(buff));

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        printf("Failed to load Winsock");
        return;
    }

    SOCKADDR_IN addrSrv;
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(5099);
    addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    //创建套接字  
    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
    if (SOCKET_ERROR == sockClient) {
        printf("Socket() error:%d", WSAGetLastError());
        return;
    }

    //向服务器发出连接请求  
    if (connect(sockClient, (struct  sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET) {
        printf("Connect failed:%d", WSAGetLastError());
        return;
    }
    else
    {
        //接收数据  
        recv(sockClient, buff, sizeof(buff), 0);
        printf("%s\n", buff);
    }

    //发送数据  
    char* buff2 = "hello, this is a Client....";
    send(sockClient, buff, sizeof(buff2), 0);

    //关闭套接字  
    closesocket(sockClient);
    WSACleanup();
    getchar();
}

2011-03-01 21:40:00 nadakiss 阅读数 563

MMORPG不同于其它的局域网的网络游戏,它是一个面向整个Internet的连接人数过万的网络游戏,因此他的服务器端设计则极为重要
  
  服务器的基本设置
  
  在大型网络游戏里,通常设计为C/S结构,客户端不再对数据进行逻辑处理,而只是一个收发装置,从玩家那里接受到操作信息,然后反馈给服务器,再由服务器进行处理后发回客户端,经客户端通过图形化处理,给玩家呈现出一个缤纷的游戏世界。
   
  登陆服务器
  
  在这里也可以称之为连接服务器,网络游戏的客户端一般是连接到这里,然后再由该连接服务器根据不同的需要,把游戏消息转发给其它相应的服务器(逻辑和地图服务器)也因为它是客户端直接连接的对象,它同时也负担了验证客户身份的工作。
  
  地图服务器
  
  在这里也可以称之为连续事件服务器。在这个服务器里要处理的对象(玩家)所做的动作都是一个连续事件。例如玩家从A点移动到B点,这样一个动作,需要一定的时间进行移动,因此说移动是一个连续事件。
  
  逻辑服务器
  
在这里可以称之为瞬时事件服务器,在这个服务器里,处理对象(玩家)所做的动作均可以在非常断时间内完成完成。例如玩家从商店购买一瓶药书,当玩家确认购买后,服务器先扣除玩家的游戏币,然后再把相应的药水瓶加入玩家的背包里。这2个操作对于服务器来说,只是2个数字的加减,计算完这两个数字的加减,这个事件就可以结束了。因此,我们可以说这个事件是一个瞬时事件
  
  服务器组的改进
  
  不过在实际应用的过程中,游戏服务器的结构要比上面所说的3种服务结构要复杂些,不过也都是在这3种最基本的服务器架构下进行扩充,扩充的主要是其它辅助功能。在实际应用里可能增加的2种服务器,数据库服务器,计费服务器,由逻辑服务器独立出来的聊天服务器。
   
  数据库服务器
  
  数据库服务器其实就是专门利用一台服务器进行数据库的读写操作。这点特别是在大型的网络游戏里尤为重要。因为在大型网络游戏里,要处理玩家的数据量非常大,如果不利用专门的服务器进行处理,很有可能会拖累这个服务器组。
  
  计费服务器
  
  通常在商业的网络游戏里出现,用于记录玩家在线的时间,给收费提供依据,同时也是整个服务器组里最重要的部分,一旦出现问题,运营商就不用赚钱了。
  
  聊天服务器
  
  在游戏里的聊天功能是属于一种瞬时动作,理论上是放在逻辑服务器里进行处理。不过在大型网络游戏里,因为这个部分功能与游戏里的其它部分联系并不紧密,因此可以独立出来做一个功能服务器。
  
  服务器的集群设置
  
  在大型游戏的应用过程中,实际需要处理的玩家数量可能过万,一台普通的服务器是无法完成所要完成的工作,因此,在实际应用的时候,通常是由一组多台服务器共同完成一个功能。
  例如地图服务器,可以根据需要,把游戏里所有的地域进行划分,划分为N个区域,然后让这一个区域里发生的事件都用一个特定的服务器进行处理。这样做的目的是减少一个服务器所承担的计算量,把整个系统组成一个分布式的网络。
不过这样做的同时会造成一个麻烦:当一位玩家从区域1,移动到区域2。这个时候,就必须先在服务器1里把玩家删除,然后再在区域2里加入玩家。同时需要由服务器1向服务器2转移玩家的数据信息(因为服务器组在工作的时候,玩家的信息只能保存在当前所在区域的服务器里),也就是说一旦玩家发生服务器间区域移动,服务器端就不可避免的造成数据通讯。因为这种移动并不是有规律的,玩家所在的服务器都有可能到达其它服务器。这样,如果服务器组里有N台地图服务器,那么,每个服务器都可能向其它N-1台服务器产生连接,总共就可能产生N×N个连接。如此数量连接如果只是使用普通的socket设计,就很有可能会给服务器通讯间的各种问题所困扰,为此,在商业网络游戏的服务器之间,通常都使用成熟的第三方的通讯中间件,如ACE,ICE等作为网络连接的传输层。
  

【服务器】简单的C++服务器

阅读数 3567

简单的C++服务器设置头文件和库文件Winsock的初始化套接字的相关函数创建简单的C++服务器一丶设置头文件和库文件     首先我们导入头文件:WinSock2.h#include     头文件导入后,我们再来导入一个Socket的库文件:ws2_32.libPS:如果导入这个库文件,那么就出先外部符号错误的信息。     导入方法:项目的属性

博文 来自: kan464872327

使用docker编译部署开源c/c++程序(三)

阅读数 3

程序可以编译出来了,后面就要着手只做docker镜像,方面以后的使用。编写c++程序的Dockerfile,可以参考别人是如何制作ffmpeg镜像的https://github.com/jrottenberg/ffmpeg/blob/master/docker-images/4.0/alpine/Dockerfile依葫芦画瓢,写了一个poppler的镜像制作文...

博文 来自: weixin_34161064

使用docker编译部署开源c/c++程序(二)

阅读数 5

接着上一篇文章,这次挑战在alpinedocker之中编译pdf渲染软件poppler。选择poppler是因为它具有比较多的依赖库,编译有一定的挑战性。当然,alpinelinux之中可以用apk命令直接安装poppler,本例子主要关注于docker中开源软件的编译apkaddbuild-basecmakefreetype-devfontconf...

博文 来自: weixin_34357267

使用docker编译部署开源c/c++程序(一)

阅读数 5

用docker部署c/c++程序,首先需要一个Linux的基础镜像,可以是ubuntu、centos等。但最近docker都流行用alpine作为基础镜像,因为alpine最大的优点是体积小。先下载一个alpine的镜像玩玩dockerpullalpine查看一下镜像的大小dockerimagesREPOSITORYTAG...

博文 来自: weixin_33936401

C++环境部署

阅读数 195

目前是在Windows平台下学习C++,所以我安装了一个VS2005,安装好以后选择了C++的工作环境,开始了我的C++学习旅程。C++支持面向对象编程,也支持面向过程编程,面向过程编程在做一些小的程序的时候,数据结构+算法的方式可以灵活解决问题;面向对象编程在解决复杂的问题的时候提供的更高级的抽象,代码重用程度、可维护性更高。

博文 来自: RodneyJonson
没有更多推荐了,返回首页