Spring Boot+Netty+UDP+C语言客户端通信

使用Netty来完成Spring Boot和C语言客户端的通信。下面将会分别介绍UDP和TCP。TCP只介绍Java的部分,C语言的部分不说。

一、C语言客户端和服务端的实现
  • 客户端
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(){
        int sockfd;
        int len;
        struct sockaddr_in address;
        int result;
        char str[30];
        char* ch = str;
        while(1){
                sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                address.sin_family = AF_INET;
                address.sin_addr.s_addr = inet_addr("127.0.0.1");
                address.sin_port = htons(6678);
                len = sizeof(address);
                printf("请输入你要传输的数据\n");
                gets(ch);
                len = strlen(ch);
                sendto(sockfd, ch, len, 0,(struct sockaddr *)&address, sizeof(address));        
        }
        close(sockfd);
        exit(0);
        return 0;
}
  • 服务端
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(){
        int sockfd;
        int len;
        struct sockaddr_in address;
        int result;
        char str[300];
        char* ch = str;
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = inet_addr("127.0.0.1");
        address.sin_port = htons(5678);
        bind(sockfd, (struct sockaddr *)&address, sizeof(address));
	int nRecvBuf=32768;
        setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
        while(1){
                //接受输入
                printf("等待接收:\n");
                socklen_t lent = sizeof(address);
                result = recvfrom(sockfd, ch, 8192, 0, (struct sockaddr *)&address, &lent);
                ch[result] = '\0';
                printf("收到回复:%s\n", ch);
        }
        close(sockfd);
        exit(0);
        return 0;
}
二、Netty的依赖导入
<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
	<version>5.0.0.Alpha2</version>
</dependency>
三、UDP的服务端实现
  • 消息处理类
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import lombok.extern.log4j.Log4j2;

/**
 * 对于新消息进行操作的类
 */
@Log4j2
public class NettyUDPServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
    /**
     * 重写接收到的数据的具体操作
     * @param channelHandlerContext
     * @param datagramPacket
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket) throws Exception {
        System.out.println("收到数据:"  + datagramPacket.content().toString(CharsetUtil.UTF_8) );
    }

    /**
     * 出错回调
     * @param channelHandlerContext
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception {
        cause.printStackTrace();
        channelHandlerContext.close();
    }
}
  • 客户端类
import com.example.demo.Servers.CServerListen;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOptipackage com.example.demo.Netty.UDP;

on;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * UDP的服务端类
 */
@Service
@Log4j2
public class UDPServer {
    private Bootstrap bootstrap;
    private NioEventLoopGroup group; //工作组
    private Channel channel; //信道
    private int port;
    private String address;

    /**
     * 线程的具体执行内容
     */
    class Listen implements Runnable {
        @Override
        public void run(){
            Thread.currentThread().setName("等待c客户端的消息进程");
            System.out.println("正在监听" + port + "端口,等待客户端消息。");
            try {
                group = new NioEventLoopGroup();
                bootstrap = new Bootstrap();
                bootstrap.group(group)
                        .channel(NioDatagramChannel.class)
                        .option(ChannelOption.SO_BROADCAST, true)
                        .option(ChannelOption.SO_RCVBUF, 1024 * 1024 * 100)
                        .handler(new NettyUDPServerHandler());
                channel = bootstrap.bind(address, port).sync().channel();
                channel.closeFuture().await();
            }catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                group.shutdownGracefully();
            }
        }
    }

    /**
     * 启动服务端
     */
    public void start() {
        Listen ser = new Listen();
        Thread m = new Thread(ser);
        m.start();
    }

    /**
     * 设定端口和ip
     * @param address
     * @param port
     * @return
     */
    public UDPServer bind(String address, int port) {
        this.address = address;
        this.port = port;
        return this;
    }
}
四、UDP的客户端实现
  • 客户端类
import com.example.demo.Netty.NettyClientInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;

import java.net.InetSocketAddress;

@Service
@Log4j2
public class UDPClient {
    private int port;//绑定的端口
    private String address;//绑定的ip
    private int aim_port;//目标的端口
    private String aim_address;//目标的ip

    /**
     * 绑定目标和本地的ip以及端口
     * @param port
     * @param address
     * @param aim_port
     * @param aim_address
     * @return
     */
    public UDPClient bind(int port, String address, int aim_port, String aim_address){
        this.address = address;
        this.port = port;
        this.aim_address = aim_address;
        this.aim_port = aim_port;
        return this;
    }

    /**
     * 发送消息
     * @param data
     */
    public void send(String data) {
        EventLoopGroup eventExecutors = new NioEventLoopGroup();
        try {
            Bootstrap clientBoostrap = new Bootstrap();
            clientBoostrap.group(eventExecutors)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    //接收缓冲区大小设置
                    .option(ChannelOption.SO_RCVBUF, 1024 * 1024 * 100)
                    //发送缓冲区大小设置
                    .option(ChannelOption.SO_SNDBUF, 1024 * 1024 * 100)
                    .handler(new NettyUDPClientHandler());
            //绑定本地的ip和端口
            Channel channel = clientBoostrap.bind(address, port).sync().channel();
            //发送消息
            channel.writeAndFlush(new DatagramPacket(
                    Unpooled.copiedBuffer(data, CharsetUtil.UTF_8),
                    new InetSocketAddress(aim_address, aim_port))).sync();
            System.out.println("通道id:" + channel.id().toString());
            //等待5秒后,关闭
            channel.closeFuture().await(5000);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("客户端关闭了");
            eventExecutors.shutdownGracefully();
        }
    }
}
  • 消息处理类
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import lombok.extern.log4j.Log4j2;

/**
 * 操作发送消息之后,接收的消息操作类
 */
@Log4j2
public class NettyUDPClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    /**
     * 操作接收到的消息
     * @param channelHandlerContext
     * @param datagramPacket
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket) throws Exception {
        System.out.println(datagramPacket.content().toString());
//        channelHandlerContext.close();
    }

    /**
     * 通道读取完毕回调
     * @param channelHandlerContext
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext channelHandlerContext) throws Exception {
        super.channelReadComplete(channelHandlerContext);
        System.out.println("关闭channel");
        channelHandlerContext.close();
    }

    /**
     * 出错回调
     * @param channelHandlerContext
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception {
        cause.printStackTrace();
        channelHandlerContext.close();
    }
}
五、TCP的服务端实现
  • 服务端类
import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;

@Slf4j
public class NettyServer {
    public void start(InetSocketAddress socketAddress){
        //主线程,先创建一个
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        //工作线程
        EventLoopGroup workGroup = new NioEventLoopGroup(200);
        ServerBootstrap bootstrap = new ServerBootstrap()
                .group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ServerChannelInitializer())
                .localAddress(socketAddress)
                //设置队列大小
                .option(ChannelOption.SO_BACKLOG, 1024)
                //两小时没有通讯,发送探测数据报文
                .childOption(ChannelOption.SO_KEEPALIVE, true);
        try {
            ChannelFuture future = bootstrap.bind(socketAddress).sync();
            System.out.println("服务器开始监听端口:" + socketAddress.getPort());
            future.channel().closeFuture().sync();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            //关闭两个部分工作组
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}
  • 初始化类
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
    /**
     * 添加解码和编码
     * @param socketChannel
     * @throws Exception
     */
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
        socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
        socketChannel.pipeline().addLast(new NettyServerHandler());
    }
}
  • 消息处理类
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 客户端连接会触发
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("通道激活");
    }

    /**
     * 客户端发消息会触发
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("收到消息" + msg.toString());
        ctx.write("你好啊");
        ctx.flush();
    }

    /**
     * 发生异常触发
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
六、TCP的客户端实现
  • 客户端类
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.NioSocketChannel;

public class NettyClient {

    public void start() {
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap()
                .group(group)
                .option(ChannelOption.TCP_NODELAY, true)
                .channel(NioSocketChannel.class)
                .handler(new NettyClientInitializer());
        try {
            ChannelFuture future = bootstrap.connect("127.0.0.1", 5678).sync();
            System.out.println("客户端尝试连接");
            future.channel().closeFuture().sync();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
    }
}
  • 初始化类
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class NettyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        socketChannel.pipeline().addLast("decoder", new StringDecoder());
        socketChannel.pipeline().addLast("encoder", new StringEncoder());
        socketChannel.pipeline().addLast(new NettyClientHandler());
    }
}
  • 消息处理类
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端活动");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("客户端收到消息" + msg.toString());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

七、设置Spring Boot启动时开始启动Netty的服务端或者客户端
import com.example.demo.Netty.UDP.UDPClient;
import com.example.demo.Netty.UDP.UDPServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ServletContextAware;

import javax.servlet.ServletContext;


/**
 * 启动Spring boot 开始等待客户端消息
 */
@Component
public class StartWith implements ServletContextAware {
    UDPServer udpServer;
    UDPClient udpClient;

    @Autowired
    public void setUdpClient(UDPClient udpClient) {
        this.udpClient = udpClient;
    }
    @Autowired
    public void setUdpServer(UDPServer udpServer) {
        this.udpServer = udpServer;
    }

    @Override
    public void setServletContext(ServletContext servletContext){
        //发送
//        udpClient.bind(6679,"127.0.0.1",5678,"127.0.0.1")
//                .send("你好啊,这次没问题了吧?");
        //接收
        udpServer.bind("127.0.0.1", 6678)
                .start();
    }
}
八、注意事项
  • UDP的部分,我全部都自己测试过,没有问题,TCP的部分没有测试。
  • UDP的Java部分采用了构建者模式,线程启动也比较重要,否则Spring Boot的主线程会被阻塞。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值