天天看點

gb28181簡單實作sip信令伺服器(java版基于springboot):一、netty建立udp伺服器

以下僅代表個人了解,僅供參考,歡迎大佬糾正!!!

maven依賴

<!-- springboot配置依賴 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
	<optional>true</optional>
</dependency>
<!-- netty -->
<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
	<version>4.1.38.Final</version>
</dependency>
           

SipUdpServer 服務類

import com.fengyulei.fylsipserver.config.ConfigInfo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
//整合springboot 直接單例注入
@Component
public class SipUdpServer implements Runnable{

    private static final Logger logger = LoggerFactory.getLogger(SipUdpServer.class);
	//配置類注入
    @Autowired
    private ConfigInfo configInfo;
	//udp伺服器可以使用單例Handler,友善其他類注入
    @Autowired
    private SipServerHandler sipServerHandler;

    @PostConstruct
    private void init(){
        //異步啟動netty 不使用異步會阻塞程式
        Thread thread=new Thread(this);
        thread.setDaemon(true);
        thread.setName("sip server netty thread");
        thread.start();
    }

    @Override
    public void run(){
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        try{
            //通過NioDatagramChannel建立Channel,并設定Socket參數支援廣播
            //UDP相對于TCP不需要在用戶端和服務端建立實際的連接配接,是以不需要為連接配接(ChannelPipeline)設定handler
            Bootstrap b=new Bootstrap();
            b.group(bossGroup)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(sipServerHandler);
            ChannelFuture channelFuture=b.bind(configInfo.getServerPort()).sync();
            sipServerHandler.setChannelFuture(channelFuture);
            logger.info("SipUdpServer 啟動成功 ip:[{}],port:[{}]",configInfo.getServerIp(),configInfo.getServerPort());
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
            logger.error(e.getMessage(),e);
        }finally{
            bossGroup.shutdownGracefully();
        }
    }



}
           

ConfigInfo 配置類

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotEmpty;

@Validated
@Component
@ConfigurationProperties(prefix = "fyl.sip")
public class ConfigInfo {

    @NotEmpty(message = "serverId 不能為空")
    private String serverId;

    private Integer serverPort=5060;

    private String serverIp="0.0.0.0";

    @NotEmpty(message = "mediaServerIp 不能為空")
    private String mediaServerIp;

    @NotEmpty(message = "mediaServerPort 不能為空")
    private String mediaServerPort;


    @NotEmpty(message = "sipDeviceKey 不能為空")
    private String sipDeviceKey;

    private Integer mediaUdpTcpServerPort;

    public Integer getMediaUdpTcpServerPort() {
        return mediaUdpTcpServerPort;
    }

    public void setMediaUdpTcpServerPort(Integer mediaUdpTcpServerPort) {
        this.mediaUdpTcpServerPort = mediaUdpTcpServerPort;
    }

    public String getSipDeviceKey() {
        return sipDeviceKey;
    }

    public void setSipDeviceKey(String sipDeviceKey) {
        this.sipDeviceKey = sipDeviceKey;
    }

    public String getServerId() {
        return serverId;
    }

    public void setServerId(String serverId) {
        this.serverId = serverId;
    }

    public Integer getServerPort() {
        return serverPort;
    }

    public void setServerPort(Integer serverPort) {
        this.serverPort = serverPort;
    }

    public String getServerIp() {
        return serverIp;
    }

    public void setServerIp(String serverIp) {
        this.serverIp = serverIp;
    }

    public String getMediaServerIp() {
        return mediaServerIp;
    }

    public void setMediaServerIp(String mediaServerIp) {
        this.mediaServerIp = mediaServerIp;
    }

    public String getMediaServerPort() {
        return mediaServerPort;
    }

    public void setMediaServerPort(String mediaServerPort) {
        this.mediaServerPort = mediaServerPort;
    }
}
           

application.properties 配置資訊

#sip伺服器id
fyl.sip.server-id=34020000002000000001
#sip伺服器ip位址,預設為0.0.0.0即可
fyl.sip.server-ip=0.0.0.0
#sip伺服器監聽的端口号
fyl.sip.server-port=5060
#接收流媒體的ip
fyl.sip.media-server-ip=192.168.1.201
#接收流媒體的端口,改端口同時監聽udp和tcp 直接發到第三方伺服器端口
fyl.sip.media-server-port=10000
#儲存攝像頭裝置資訊的redis key
fyl.sip.sip-device-key=fyl_sip_device_key
#接收流媒體的端口,改端口同時監聽udp和tcp 該端口用于自己接收rtp ps流
fyl.sip.media-udp-tcp-server-port=10002
           

SipServerHandler 簡單實作

package com.fengyulei.fylsipserver.netty;

import com.fengyulei.fylsipserver.config.ConfigInfo;
import com.fengyulei.fylsipserver.data.Data;
import com.fengyulei.fylsipserver.data.SendDataTask;
import com.fengyulei.fylsipserver.data.SendTipsTask;
import com.fengyulei.fylsipserver.entity.DeviceInfo;
import com.fengyulei.fylsipserver.service.CommonService;
import com.fengyulei.fylsipserver.util.Utils;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//udp直接單例注入
@Component
public class SipServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    private static final Logger logger= LoggerFactory.getLogger(SipServerHandler.class);
    //udp通道 備用
    private Channel channel;
    private ChannelFuture channelFuture;
    public ChannelFuture getChannelFuture() {
        return channelFuture;
    }
    public void setChannelFuture(ChannelFuture channelFuture) {
        this.channelFuture = channelFuture;
        setChannel(channelFuture.channel());
    }
    //一下屬性參考配置檔案
    private static String serverIp=null;
    private static String serverPort=null;
    private static  String serverId=null;
    private static  String mediaServerIp=null;
    private static String mediaServerPort=null;
    private void setChannel(Channel channel) {
        if(channel==null){
            logger.error("channel null");
            System.exit(1);
        }
        this.channel = channel;
    }
	//配置類
    @Autowired
    private ConfigInfo configInfo;
	//redis
    @Resource(name = "FylSipRedisTemplate")
    private RedisTemplate redisTemplate;

	//業務處理類 可忽略
    @Autowired
    private CommonService commonService;

    private void setDeviceInfo(String deviceId, DeviceInfo deviceInfo){
        redisTemplate.opsForHash().put(configInfo.getSipDeviceKey(),deviceId,deviceInfo);
    }
    private DeviceInfo getDeviceInfo(String deviceId){
        Object obj=redisTemplate.opsForHash().get(configInfo.getSipDeviceKey(),deviceId);
        if(obj==null){
            return null;
        }
        return (DeviceInfo)obj;
    }
    //初始化配置
    @PostConstruct
    private void init(){
        serverIp=configInfo.getServerIp();
        serverPort=configInfo.getServerPort().toString();
        serverId=configInfo.getServerId();

        mediaServerIp=configInfo.getMediaServerIp();
        //mediaServerPort=configInfo.getMediaServerPort();
        mediaServerPort=configInfo.getMediaUdpTcpServerPort().toString();
    }
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, DatagramPacket packet) throws Exception {
        //建議捕獲異常,防止程式異常退出
        try {
            //gbk解碼
            String str=packet.content().toString(Charset.forName("gbk"));
            //去除空包,公網狀态下很多空包 應該是伺服器端口被探測
            if(str.trim().length()==0){
                return;
            }

            //列印接收資訊
            //logger.info("-------------");
            //logger.info(n+str);
            //logger.info(packet.sender().getAddress().getHostAddress());
            //logger.info(String.valueOf(packet.sender().getPort()));
            //logger.info("-------------");
        }catch (Exception e){
            logger.error(e.getMessage(),e);
            //throw e;
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause) throws Exception{
        //關閉之後會導緻netty不可用,udp是單通道的 盡量防止異常抛到這裡
        ctx.close();
        logger.error(cause.getMessage(),cause);
    }
    //發送udp資料包 注意gbk編碼
    private void send(String sendStr,InetSocketAddress inetSocketAddress){
        channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(sendStr,Charset.forName("gbk")), inetSocketAddress));
    }
 }
           

udp伺服器搭建到這結束,不懂的直接留言提問 或滴滴QQ:738126916

下一節講解 攝像頭注冊