157 Linux C++ 通讯架构实战12,监听端口代码添加。

调用位置

    //(4)一些初始化函数,准备放这里        
    if(ngx_init_signals() != 0) //信号初始化
    {
        exitcode = 1;
        goto lblexit;
    }        
    if(g_socekt.Initialize() == false)//初始化socket
    {
        exitcode = 1;
        goto lblexit;
    }

ngx_c_socket.cxx

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>    //uintptr_t
#include <stdarg.h>    //va_start....
#include <unistd.h>    //STDERR_FILENO等
#include <sys/time.h>  //gettimeofday
#include <time.h>      //localtime_r
#include <fcntl.h>     //open
#include <errno.h>     //errno
#include <sys/socket.h>
#include <sys/ioctl.h> //ioctl
#include <arpa/inet.h>

#include "ngx_c_conf.h"
#include "ngx_macro.h"
#include "ngx_global.h"
#include "ngx_func.h"
#include "ngx_c_socket.h"

//构造函数
CSocekt::CSocekt()
{
    m_ListenPortCount = 1;   //监听一个端口
    return;	
}

//释放函数
CSocekt::~CSocekt()
{
    //释放必须的内存
    std::vector<lpngx_listening_t>::iterator pos;	
	for(pos = m_ListenSocketList.begin(); pos != m_ListenSocketList.end(); ++pos) //vector
	{		
		delete (*pos); //一定要把指针指向的内存干掉,不然内存泄漏
	}//end for
	m_ListenSocketList.clear(); 
    return;
}

//初始化函数【fork()子进程之前干这个事】
//成功返回true,失败返回false
bool CSocekt::Initialize()
{
    bool reco = ngx_open_listening_sockets();
    return reco;
}

//监听端口【支持多个端口】,这里遵从nginx的函数命名
//在创建worker进程之前就要执行这个函数;
bool CSocekt::ngx_open_listening_sockets()
{
    CConfig *p_config = CConfig::GetInstance();
    m_ListenPortCount = p_config->GetIntDefault("ListenPortCount",m_ListenPortCount); //取得要监听的端口数量
    
    int                isock;                //socket
    struct sockaddr_in serv_addr;            //服务器的地址结构体
    int                iport;                //端口
    char               strinfo[100];         //临时字符串 
   
    //初始化相关
    memset(&serv_addr,0,sizeof(serv_addr));  //先初始化一下
    serv_addr.sin_family = AF_INET;                //选择协议族为IPV4
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本地所有的IP地址;INADDR_ANY表示的是一个服务器上所有的网卡(服务器可能不止一个网卡)多个本地ip地址都进行绑定端口号,进行侦听。

    for(int i = 0; i < m_ListenPortCount; i++) //要监听这么多个端口
    {        
        //参数1:AF_INET:使用ipv4协议,一般就这么写
        //参数2:SOCK_STREAM:使用TCP,表示可靠连接【相对还有一个UDP套接字,表示不可靠连接】
        //参数3:给0,固定用法,就这么记
        isock = socket(AF_INET,SOCK_STREAM,0); //系统函数,成功返回非负描述符,出错返回-1
        if(isock == -1)
        {
            ngx_log_stderr(errno,"CSocekt::Initialize()中socket()失败,i=%d.",i);
            //其实这里直接退出,那如果以往有成功创建的socket呢?就没得到释放吧,当然走到这里表示程序不正常,应该整个退出,也没必要释放了 
            return false;
        }

        //setsockopt():设置一些套接字参数选项;
        //参数2:是表示级别,和参数3配套使用,也就是说,参数3如果确定了,参数2就确定了;
        //参数3:允许重用本地地址
        //设置 SO_REUSEADDR,目的第五章第三节讲解的非常清楚:主要是解决TIME_WAIT这个状态导致bind()失败的问题
        int reuseaddr = 1;  //1:打开对应的设置项
        if(setsockopt(isock,SOL_SOCKET, SO_REUSEADDR,(const void *) &reuseaddr, sizeof(reuseaddr)) == -1)
        {
            ngx_log_stderr(errno,"CSocekt::Initialize()中setsockopt(SO_REUSEADDR)失败,i=%d.",i);
            close(isock); //无需理会是否正常执行了                                                  
            return false;
        }
        //设置该socket为非阻塞
        if(setnonblocking(isock) == false)
        {                
            ngx_log_stderr(errno,"CSocekt::Initialize()中setnonblocking()失败,i=%d.",i);
            close(isock);
            return false;
        }

        //设置本服务器要监听的地址和端口,这样客户端才能连接到该地址和端口并发送数据        
        strinfo[0] = 0;
        sprintf(strinfo,"ListenPort%d",i);
        iport = p_config->GetIntDefault(strinfo,10000);
        serv_addr.sin_port = htons((in_port_t)iport);   //in_port_t其实就是uint16_t

        //绑定服务器地址结构体
        if(bind(isock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
        {
            ngx_log_stderr(errno,"CSocekt::Initialize()中bind()失败,i=%d.",i);
            close(isock);
            return false;
        }
        
        //开始监听
        if(listen(isock,NGX_LISTEN_BACKLOG) == -1)
        {
            ngx_log_stderr(errno,"CSocekt::Initialize()中listen()失败,i=%d.",i);
            close(isock);
            return false;
        }

        //可以,放到列表里来
        lpngx_listening_t p_listensocketitem = new ngx_listening_t; //千万不要写错,注意前边类型是指针,后边类型是一个结构体
        memset(p_listensocketitem,0,sizeof(ngx_listening_t));      //注意后边用的是 ngx_listening_t而不是lpngx_listening_t
        p_listensocketitem->port = iport;                          //记录下所监听的端口号
        p_listensocketitem->fd   = isock;                          //套接字木柄保存下来   
        ngx_log_error_core(NGX_LOG_INFO,0,"监听%d端口成功!",iport); //显示一些信息到日志中
        m_ListenSocketList.push_back(p_listensocketitem);          //加入到队列中
    } //end for(int i = 0; i < m_ListenPortCount; i++)    
    return true;
}

//设置socket连接为非阻塞模式【这种函数的写法很固定】:非阻塞,概念在五章四节讲解的非常清楚【不断调用,不断调用这种:拷贝数据的时候是阻塞的】
bool CSocekt::setnonblocking(int sockfd) 
{    
    int nb=1; //0:清除,1:设置  
    if(ioctl(sockfd, FIONBIO, &nb) == -1) //FIONBIO:设置/清除非阻塞I/O标记:0:清除,1:设置
    {
        return false;
    }
    return true;

    //如下也是一种写法,跟上边这种写法其实是一样的,但上边的写法更简单
    /* 
    //fcntl:file control【文件控制】相关函数,执行各种描述符控制操作
    //参数1:所要设置的描述符,这里是套接字【也是描述符的一种】
    int opts = fcntl(sockfd, F_GETFL);  //用F_GETFL先获取描述符的一些标志信息
    if(opts < 0) 
    {
        ngx_log_stderr(errno,"CSocekt::setnonblocking()中fcntl(F_GETFL)失败.");
        return false;
    }
    opts |= O_NONBLOCK; //把非阻塞标记加到原来的标记上,标记这是个非阻塞套接字【如何关闭非阻塞呢?opts &= ~O_NONBLOCK,然后再F_SETFL一下即可】
    if(fcntl(sockfd, F_SETFL, opts) < 0) 
    {
        ngx_log_stderr(errno,"CSocekt::setnonblocking()中fcntl(F_SETFL)失败.");
        return false;
    }
    return true;
    */
}

//关闭socket,什么时候用,我们现在先不确定,先把这个函数预备在这里
void CSocekt::ngx_close_listening_sockets()
{
    for(int i = 0; i < m_ListenPortCount; i++) //要关闭这么多个监听端口
    {  
        //ngx_log_stderr(0,"端口是%d,socketid是%d.",m_ListenSocketList[i]->port,m_ListenSocketList[i]->fd);
        close(m_ListenSocketList[i]->fd);
        ngx_log_error_core(NGX_LOG_INFO,0,"关闭监听端口%d!",m_ListenSocketList[i]->port); //显示一些信息到日志中
    }//end for(int i = 0; i < m_ListenPortCount; i++)
    return;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
c 监听端口 demo 是一个演示程序,用于展示如何使用 c 语言监听一个指定端口的网络连接。 首先,我们需要引入相关的库文件。在 c 语言中,网络编程通常使用 Socket 库来实现。头文件 `<sys/socket.h>` 和 `<netinet/in.h>` 包含了所需的函数和数据结构。 接下来,我们创建一个套接字(socket)。套接字是一种用来进行网络通信的文件描述符。通过调用 `socket()` 函数,我们可以创建一个新的套接字,并将其赋值给一个整型变量。 然后,我们需要设置套接字的属性。可以使用 `setsockopt()` 函数来设置套接字的属性,比如地址和端口等信息。 接下来,我们需要绑定套接字到指定的地址和端口上。可以使用 `bind()` 函数来实现。在调用 `bind()` 函数时,我们需要填入套接字的文件描述符,以及一个数据结构 `struct sockaddr_in`,该数据结构包含了要绑定的地址和端口信息。 最后,我们使用 `listen()` 函数开始监听指定端口上的连接请求。这样,当有新的连接请求到达时,我们就可以接受它。可以使用 `accept()` 函数来接受连接请求,并返回一个新的套接字用于后续的通信。 在实际应用中,我们通常将监听端口代码放在一个无限循环中,以保持程序一直处于监听状态,不会退出。 总结起来,c 监听端口 demo 是一个使用 c 语言编写的演示程序,通过 Socket 编程实现监听指定端口的网络连接。它包含了创建套接字、设置套接字属性、绑定套接字到地址和端口、监听连接请求等一系列操作。这个演示程序可以帮助初学者理解并入门 c 语言网络编程的基本概念和操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值