服务器主逻辑代码的重构

简介:

    不知道前主程是处于什么目的,总之我接手这套程序的时候,出现了超级多的问题,也发现了超级多的问题。

     比如说吧,接受网络消息逻辑是线程独立的,而发送消息给客户端缺阻塞在了逻辑线程里面;原本可以放在一个进程里面处理的逻辑,却分散在了四个进程里面去处理,导致我完成一个功能,大部分时间要话费了进程之间的玩家信息的同步上面,在我无法忍受的情况下,我终于是用NIO将网络底层从新写了,而且将四个进程合并,但是在很多逻辑上还是尽量保持了和原逻辑处理的兼容!

      先说说这个重构的底层吧!

     我们看下最重要的ClientHandle类,主要处理每个连接的收发数据的!


public class ClientHandle implements ISession
{
    public final static int RW_BUFFER_SIZE = 1024;
    private SocketChannel socket = null;
     
    private java.nio.ByteBuffer reader = java.nio.ByteBuffer.allocate(RW_BUFFER_SIZE);
    private java.nio.ByteBuffer writer = java.nio.ByteBuffer.allocate(4*RW_BUFFER_SIZE);
     
    BlockingQueue<ByteBuffer> writeQueue = new LinkedBlockingQueue<ByteBuffer>();
     
    private IPlayer player = null;
     
    private boolean active = false;

  包含SocketChannel对象不用说了,reader和writer是用来做消息收发的缓冲的,因为服务器广播的压力会大一些,所以将writer的大小设置为reader的4倍,当然这个可以调整。

     writeQueue是用来存储需要发送给客户端的ByteBuffer,每次在这个链接可以写数据的时候,就将writeQueue里面存储的数据转移到writer中,并且一次发送,减少了writer的系统调用次数。ByteBuffer的结构简单说下,不同于java.nio.ByteBuffer,而是自己封装的一个消息解析器,给出源代码


package NetBase;
 
/**
 * 类说明:字节缓存类,字节操作高位在前,低位在后
 * 
 * @version 1.0
 * @author fxxxysh <hanshuang@linekong.com>
 */
 
public class ByteBuffer
{
 
    /* static fields */
    /** 默认的初始容量大小 */
    public static final int CAPACITY = 32;
 
    /** 默认的动态数据或文字的最大长度,400k */
    public static final int MAX_DATA_LENGTH = 400 * 1024;
 
    /* fields */
    /** 字节数组 */
    byte[] bytes;
 
    /** 字节缓存的长度 */
    int top;
 
    /** 字节缓存的偏移量 */
    int offset;
 
    /* constructors */
    /** 按默认的大小构造一个字节缓存对象 */
    public ByteBuffer()
    {
        this(CAPACITY);
    }
 
    /** 按指定的大小构造一个字节缓存对象 */
    public ByteBuffer(int capacity)
    {
        if (capacity < 1)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, invalid capatity:" + capacity);
        bytes = new byte[capacity];
        top = 0;
        offset = 0;
    }
 
    /** 按指定的字节数组构造一个字节缓存对象 */
    public ByteBuffer(byte[] data)
    {
        if (data == null)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, null data");
        bytes = data;
        top = data.length;
        offset = 0;
    }
 
    /** 按指定的字节数组构造一个字节缓存对象 */
    public ByteBuffer(byte[] data, int index, int length)
    {
        if (data == null)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, null data");
        if (index < 0 || index > data.length)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, invalid index:" + index);
        if (length < 0 || data.length < index + length)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, invalid length:" + length);
        bytes = data;
        top = index + length;
        offset = index;
    }
 
    /* properties */
    /** 得到字节缓存的容积 */
    public int capacity()
    {
        return bytes.length;
    }
 
    /** 设置字节缓存的容积,只能扩大容积 */
    public void setCapacity(int len)
    {
        int c = bytes.length;
        if (len <= c)
            return;
        for (; c < len; c = (c << 1) + 1)
            ;
        byte[] temp = new byte[c];
        System.arraycopy(bytes, 0, temp, 0, top);
        bytes = temp;
    }
 
    /** 得到字节缓存的长度 */
    public int top()
    {
        return top;
    }
 
    /** 设置字节缓存的长度 */
    public void setTop(int top)
    {
        if (top < offset)
            throw new IllegalArgumentException(this + " setTop, invalid top:"
                    + top);
        if (top > bytes.length)
            setCapacity(top);
        this.top = top;
    }
 
    /** 得到字节缓存的偏移量 */
    public int offset()
    {
        return offset;
    }
 
    /** 设置字节缓存的偏移量 */
    public void setOffset(int offset)
    {
        if (offset < 0 || offset > top)
            throw new IllegalArgumentException(this
                    + " setOffset, invalid offset:" + offset);
        this.offset = offset;
    }
 
    /** 得到字节缓存的使用长度 */
    public int length()
    {
        return top - offset;
    }
 
    /** 得到字节缓存的字节数组,一般使用toArray()方法 */
    public byte[] getByteArray()
    {
        return bytes;
    }
 
    /* methods */
    /* byte methods */
    /** 得到指定偏移位置的字节 */
    public byte read(int pos)
    {
        return bytes[pos];
    }
 
    /** 设置指定偏移位置的字节 */
    public void write(int b, int pos)
    {
        bytes[pos] = (byte) b;
    }
 
    /* read methods */
    /**
     * 按当前偏移位置读入指定的字节数组
     * 
     * @param data
     *            指定的字节数组
     * @param pos
     *            指定的字节数组的起始位置
     * @param len
     *            读入的长度
     */
    public void read(byte[] data, int pos, int len)
    {
        System.arraycopy(bytes, offset, data, pos, len);
        offset += len;
    }
 
    /** 读出一个布尔值 */
    public boolean readBoolean()
    {
        return (bytes[offset++] != 0);
    }
 
    /** 读出一个字节 */
    public byte readByte()
    {
        return bytes[offset++];
    }
 
    /** 读出一个无符号字节 */
    public int readUnsignedByte()
    {
        return bytes[offset++] & 0xff;
    }
 
    /** 读出一个字符 */
    public char readChar()
    {
        return (char) readUnsignedShort();
    }
 
    /** 读出一个短整型数值 */
    public short readShort()
    {
        return (short) readUnsignedShort();
    }
 
    /** 读出一个无符号的短整型数值 */
    public int readUnsignedShort()
    {
        int pos = offset;
        offset += 2;
        return (bytes[pos + 1] & 0xff) + ((bytes[pos] & 0xff) << 8);
    }
 
    /** 读出一个整型数值 */
    public int readInt()
    {
        int pos = offset;
        offset += 4;
        return (bytes[pos + 3] & 0xff) + ((bytes[pos + 2] & 0xff) << 8)
                + ((bytes[pos + 1] & 0xff) << 16) + ((bytes[pos] & 0xff) << 24);
    }
 
    /** 读出一个浮点数值 */
    public float readFloat()
    {
        return Float.intBitsToFloat(readInt());
    }
 
    /** 读出一个长整型数值 */
    public long readLong()
    {
        int pos = offset;
        offset += 8;
        return (bytes[pos + 7] & 0xffL) + ((bytes[pos + 6] & 0xffL) << 8)
                + ((bytes[pos + 5] & 0xffL) << 16)
                + ((bytes[pos + 4] & 0xffL) << 24)
                + ((bytes[pos + 3] & 0xffL) << 32)
                + ((bytes[pos + 2] & 0xffL) << 40)
                + ((bytes[pos + 1] & 0xffL) << 48)
                + ((bytes[pos] & 0xffL) << 56);
    }
 
    /** 读出一个双浮点数值 */
    public double readDouble()
    {
        return Double.longBitsToDouble(readLong());
    }
 
    /**
     * 读出动态长度, 数据大小采用动态长度,整数类型下,最大为512M 1xxx,xxxx表示(0~0x80) 0~128B
     * 01xx,xxxx,xxxx,xxxx表示(0~0x4000) 0~16K
     * 001x,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx表示(0~0x20000000) 0~512M
     */
    public int readLength()
    {
        int n = bytes[offset] & 0xff;
        if (n >= 0x80)
        {
            offset++;
            return n - 0x80;
        }
        else if (n >= 0x40)
            return readUnsignedShort() - 0x4000;
        else if (n >= 0x20)
            return readInt() - 0x20000000;
        else
            throw new IllegalArgumentException(this
                    + " readLength, invalid number:" + n);
    }
 
    /** 读出一个指定长度的字节数组,可以为null */
    public byte[] readData()
    {
        int len = readLength() - 1;
        if (len < 0)
            return null;
        if (len > MAX_DATA_LENGTH)
            throw new IllegalArgumentException(this
                    + " readData, data overflow:" + len);
        byte[] data = new byte[len];
        read(data, 0, len);
        return data;
    }
 
    /** 读出一个短字节数组,长度不超过254 */
    public byte[] readShortData()
    {
        int len = readUnsignedByte();
        if (len == 255)
            return null;
        byte[] data = new byte[len];
        if (len != 0)
            read(data, 0, len);
        return data;
    }
 
    /** 读出一个指定长度的字符串 */
    public String readString(int len)
    {
        byte[] data = new byte[len];
        if (len == 0)
            return "";
        read(data, 0, len);
        return new String(data);
    }
 
    /** 读出一个短字符串,长度不超过254 */
    public String readShortString()
    {
        int len = readUnsignedByte();
        if (len == 255)
            return null;
        return readString(len);
    }
 
    /** 读出一个字符串,长度不超过65534 */
    public String readString()
    {
        int len = readUnsignedShort();
        if (len == 65535)
            return null;
        return readString(len);
    }
 
    /** 读出一个指定长度和编码类型的字符串 */
    public String readUTF(String charsetName)
    {
        int len = readLength() - 1;
        if (len < 0)
            return null;
        if (len > MAX_DATA_LENGTH)
            throw new IllegalArgumentException(this
                    + " readUTF, data overflow:" + len);
        byte[] data = new byte[len];
        read(data, 0, len);
        if (charsetName == null)
            return new String(data);
        try
        {
            return new String(data, charsetName);
        }
        catch (Exception e)
        {
            throw new IllegalArgumentException(this
                    + " readUTF, invalid charsetName:" + charsetName);
        }
    }
 
    /** 读出一个指定长度的utf字符串 */
    public String readUTF()
    {
        int len = readLength() - 1;
        if (len < 0)
            return null;
        if (len == 0)
            return "";
        if (len > MAX_DATA_LENGTH)
            throw new IllegalArgumentException(this
                    + " readUTF, data overflow:" + len);
        StringBuffer sb = new StringBuffer(len);
        int pos = ByteKit.readUTF(bytes, offset, len, sb);
        if (pos > 0)
            throw new IllegalArgumentException(this
                    + " readUTF, format err, len=" + len + ", pos:" + pos);
        offset += len;
        return sb.toString();
    }
 
    /* write methods */
    /**
     * 写入指定字节数组
     * 
     * @param data
     *            指定的字节数组
     * @param pos
     *            指定的字节数组的起始位置
     * @param len
     *            写入的长度
     */
    public void write(byte[] data, int pos, int len)
    {
        if (bytes.length < top + len)
            setCapacity(top + len);
        System.arraycopy(data, pos, bytes, top, len);
        top += len;
    }
 
    /** 写入一个布尔值 */
    public void writeBoolean(boolean b)
    {
        if (bytes.length < top + 1)
            setCapacity(top + CAPACITY);
        bytes[top++] = (byte) (b ? 1 : 0);
    }
 
    /** 写入一个字节 */
    public void writeByte(int b)
    {
        if (bytes.length < top + 1)
            setCapacity(top + CAPACITY);
        bytes[top++] = (byte) b;
    }
 
    /** 写入一个字符 */
    public void writeChar(int c)
    {
        writeShort(c);
    }
 
    /** 写入一个短整型数值 */
    public void writeShort(int s)
    {
        int pos = top;
        if (bytes.length < pos + 2)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (s >>> 8);
        bytes[pos + 1] = (byte) s;
        top += 2;
    }
 
    /** 在指定位置写入一个短整型数值,length不变 */
    public void writeShort(int s, int pos)
    {
        if (bytes.length < pos + 2)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (s >>> 8);
        bytes[pos + 1] = (byte) s;
    }
 
    /** 写入一个整型数值 */
    public void writeInt(int i)
    {
        int pos = top;
        if (bytes.length < pos + 4)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (i >>> 24);
        bytes[pos + 1] = (byte) (i >>> 16);
        bytes[pos + 2] = (byte) (i >>> 8);
        bytes[pos + 3] = (byte) i;
        top += 4;
    }
 
    /** 在指定位置写入一个整型数值,length不变 */
    public void writeInt(int i, int pos)
    {
        if (bytes.length < pos + 4)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (i >>> 24);
        bytes[pos + 1] = (byte) (i >>> 16);
        bytes[pos + 2] = (byte) (i >>> 8);
        bytes[pos + 3] = (byte) i;
    }
 
    /** 写入一个浮点数值 */
    public void writeFloat(float f)
    {
        writeInt(Float.floatToIntBits(f));
    }
 
    /** 写入一个长整型数值 */
    public void writeLong(long l)
    {
        int pos = top;
        if (bytes.length < pos + 8)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (l >>> 56);
        bytes[pos + 1] = (byte) (l >>> 48);
        bytes[pos + 2] = (byte) (l >>> 40);
        bytes[pos + 3] = (byte) (l >>> 32);
        bytes[pos + 4] = (byte) (l >>> 24);
        bytes[pos + 5] = (byte) (l >>> 16);
        bytes[pos + 6] = (byte) (l >>> 8);
        bytes[pos + 7] = (byte) l;
        top += 8;
    }
 
    /** 写入一个双浮点数值 */
    public void writeDouble(double d)
    {
        writeLong(Double.doubleToLongBits(d));
    }
 
    /** 写入动态长度 */
    public void writeLength(int len)
    {
        if (len >= 0x20000000 || len < 0)
            throw new IllegalArgumentException(this
                    + " writeLength, invalid len:" + len);
        if (len >= 0x4000)
            writeInt(len + 0x20000000);
        else if (len >= 0x80)
            writeShort(len + 0x4000);
        else
            writeByte(len + 0x80);
    }
 
    /** 写入一个字节数组,可以为null */
    public void writeData(byte[] data)
    {
        writeData(data, 0, (data != null) ? data.length : 0);
    }
 
    /** 写入一个字节数组,可以为null */
    public void writeData(byte[] data, int pos, int len)
    {
        if (data == null)
        {
            writeLength(0);
            return;
        }
        writeLength(len + 1);
        write(data, pos, len);
    }
 
    /** 写入一个字符串,可以为null */
    public void writeString(String s)
    {
        if (s != null)
        {
            byte[] temp = s.getBytes();
            if (temp.length > 65534)
                throw new IllegalArgumentException(getClass().getName()
                        + " writeString, invalid s:" + s);
            writeShort(temp.length);
            if (temp.length != 0)
                write(temp, 0, temp.length);
        }
        else
            writeShort(65535);
    }
 
    /** 写入一个字符串,以指定的字符进行编码 */
    public void writeUTF(String str, String charsetName)
    {
        if (str == null)
        {
            writeLength(0);
            return;
        }
        byte[] data;
        if (charsetName != null)
        {
            try
            {
                data = str.getBytes(charsetName);
            }
            catch (Exception e)
            {
                throw new IllegalArgumentException(this
                        + " writeUTF, invalid charsetName:" + charsetName);
            }
        }
        else
            data = str.getBytes();
        writeLength(data.length + 1);
        write(data, 0, data.length);
    }
 
    /** 写入一个utf字符串,可以为null */
    public void writeUTF(String str)
    {
        writeUTF(str, 0, (str != null) ? str.length() : 0);
    }
 
    /** 写入一个utf字符串中指定的部分,可以为null */
    public void writeUTF(String str, int index, int length)
    {
        if (str == null)
        {
            writeLength(0);
            return;
        }
        int len = ByteKit.getUTFLength(str, index, length);
        writeLength(len + 1);
        int pos = top;
        if (bytes.length < pos + len)
            setCapacity(pos + len);
        ByteKit.writeUTF(str, index, length, bytes, pos);
        top += len;
    }
 
    /** 检查是否为相同类型的实例 */
    public boolean checkClass(Object obj)
    {
        return (obj instanceof ByteBuffer);
    }
 
    /** 在指定位置写入一个字节,length不变 */
    public void writeByte(int b, int pos)
    {
        if (bytes.length < pos + 1)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) b;
    }
 
    /** 得到字节缓存当前长度的字节数组 */
    public byte[] toByteArray()
    {
        byte[] data = new byte[top - offset];
        System.arraycopy(bytes, offset, data, 0, data.length);
        return data;
    }
 
    /** 清除字节缓存对象 */
    public void clear()
    {
        top = 0;
        offset = 0;
    }
 
    /* common methods */
    public int hashCode()
    {
        int hash = 17;
        for (int i = top - 1; i >= 0; i--)
            hash = 65537 * hash + bytes[i];
        return hash;
    }
 
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (!checkClass(obj))
            return false;
        ByteBuffer bb = (ByteBuffer) obj;
        if (bb.top != top)
            return false;
        if (bb.offset != offset)
            return false;
        for (int i = top - 1; i >= 0; i--)
        {
            if (bb.bytes[i] != bytes[i])
                return false;
        }
        return true;
    }
 
    public String toString()
    {
        return super.toString() + "[" + top + "," + offset + "," + bytes.length
                + "] ";
    }
}

   下面看下 ClientHandle的可读逻辑:


public int handleRead() throws IOException 
    {
        int r = this.socket.read(this.reader);
        if(r <= 0)
        {
            return -1;
        }
        this.reader.flip();
         
        ByteBuffer data = this.createBuffer();
        while(data != null)
        {
            this.reader.get(data.getByteArray(), data.top(), data.capacity());
            this.processData(data);
            data = this.createBuffer();
        }
         
        this.reader.clear();
        return 0;
    }

   依次将数据读入到reader中,并且按照LC(L表示长度,C表示内容)结构将reader中的数据解析成一个个ByteBuffer对象处理。下面是createBuffer函数和processData函数:


private ByteBuffer createBuffer()
    {
        if(reader.remaining() < 4)
        {
            return null;
        }
         
        int len = reader.getInt();
        if(len > reader.remaining())
        {
            reader.clear();
            return null;
        }
         
        if (len > 0 && len <= 10 * 1024)
        {
            return new ByteBuffer(len);
        }
         
        return null;
    }
 
public void processData(ByteBuffer data)
{
       player.insertData(data);
}

  这里要注意,1:解析reader中的消息一定要做容错处理;2:将解析的待处理包放到玩家身上,让玩家自己处理!

     发送函数的处理:


public int handleWrite() throws IOException
    {
        ByteBuffer data = writeQueue.poll();
        while(data != null)
        {
            this.writer.putInt(data.length());
            this.writer.put(data.toByteArray(), 0, data.length());
            data = writeQueue.poll();
        }
         
        this.writer.flip();
        if(!this.writer.hasRemaining())
        {
            this.writer.limit(this.writer.capacity());
            return 0;
        }
         
        this.socket.write(writer);
         
        if(this.writer.hasRemaining())
        {
            this.writer.compact();
            this.writer.position(this.writer.limit());
            this.writer.limit(this.writer.capacity());
        }
        else
        {
            this.writer.compact();
            this.writer.limit(this.writer.capacity());
        }
         
        return 0;
    }

  发送函数的处理相对复杂些,首先要做的就是每个连接的发送函数每100ms(可以调整)触发一次,每次触发时候,要将待发送的数据包bytebuffer填充到writer缓冲区,然后一次发送!

     管理协调这些链接的新建和处理都是使用了java nio的selector结构,具体的代码就不贴出来了,想要的可以联系我,需要注意的有两点,1:对于空闲连接的处理,2:对于发送数据的处理

     大致讲完了网络线程,那么讲一讲主逻辑线程,逻辑线程采用线程绑定地图的设计;在服务器启动之时,启动n(可以调整)个地图线程,每个地图线程绑定N(可以调整)个地图,这N个地图上的所有玩家的逻辑处理,都有地图所在线程来处理,具体处理方式:

     地图线程的主逻辑:


public class SceneThread implements Runnable
{
     
    List<IScene> scenes = new ArrayList<IScene>();
    private int index = 0;
     
    @Override
    public void run()
    {
        while(true)
        {
            try
            {
                synchronized (scenes)
                {
                    for(IScene scene : this.scenes)
                    {
                        scene.beatHeart();
                    }   
                }
                Thread.sleep(100);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
     
    public void addScene(IScene scene)
    {
        synchronized (scenes)
        {
            this.scenes.add(scene);
        }
    }
     
    public void removeScene(IScene scene)
    {
        synchronized (scenes)
        {
            this.scenes.remove(scene);
        }
    }
     
    public void setIndex(int index)
    {
        this.index = index;
    }
     
    public String toString()
    {
        return "SceneThread : " + index;
    }
     
}

   场景Scene的心跳函数:


public class Scene implements IScene
{
        public void beatHeart()
    {
        long now = System.currentTimeMillis();
        List<IPlayer> players = null;
        synchronized (idPlayerMap)
        {
            players = new ArrayList<IPlayer>(idPlayerMap.values());   
        }
         
        for(IPlayer player : players)
        {
            player.beatHeart(now);
        }
         
    }
         
}

   玩家的心跳函数:


BlockingQueue<ByteBuffer> dataToProcess = new LinkedBlockingDeque<ByteBuffer>();
     
    public void insertData(ByteBuffer data)
    {
        this.dataToProcess.offer(data);
    }
 
public void beatHeart(long now)
{
         
        ByteBuffer data = this.dataToProcess.poll();
        while(data != null)
        {
            this.processData(data);
            data = this.dataToProcess.poll();
        }
                //.....处理心跳定时器,上一篇有讲到
} 

  好了,大概的服务器的主逻辑就这些了,是不是精简小巧。晚上的时候还做了一下广播压力测试,效果还不错!

     欢迎大家提出宝贵意见!


相关文章
|
7月前
|
机器学习/深度学习 人工智能 开发者
DeepSeek服务器繁忙?拒绝稍后再试!基于阿里云PAI实现0代码一键部署DeepSeek-V3和DeepSeek-R1大模型
阿里云PAI平台支持零代码一键部署DeepSeek-V3和DeepSeek-R1大模型,用户可轻松实现从训练到部署再到推理的全流程。通过PAI Model Gallery,开发者只需简单几步即可完成模型部署,享受高效便捷的AI开发体验。具体步骤包括开通PAI服务、进入控制台选择模型、一键部署并获取调用信息。整个过程无需编写代码,极大简化了模型应用的门槛。
380 7
|
11月前
|
IDE 网络安全 开发工具
IDE之pycharm:专业版本连接远程服务器代码,并配置远程python环境解释器(亲测OK)。
本文介绍了如何在PyCharm专业版中连接远程服务器并配置远程Python环境解释器,以便在服务器上运行代码。
2592 0
IDE之pycharm:专业版本连接远程服务器代码,并配置远程python环境解释器(亲测OK)。
|
11月前
|
IDE 网络安全 开发工具
IDE之vscode:连接远程服务器代码(亲测OK),与pycharm链接服务器做对比(亲自使用过了),打开文件夹后切换文件夹。
本文介绍了如何使用VS Code通过Remote-SSH插件连接远程服务器进行代码开发,并与PyCharm进行了对比。作者认为VS Code在连接和配置多个服务器时更为简单,推荐使用VS Code。文章详细说明了VS Code的安装、远程插件安装、SSH配置文件编写、服务器连接以及如何在连接后切换文件夹。此外,还提供了使用密钥进行免密登录的方法和解决权限问题的步骤。
4769 0
IDE之vscode:连接远程服务器代码(亲测OK),与pycharm链接服务器做对比(亲自使用过了),打开文件夹后切换文件夹。
|
11月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
2091 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
11月前
|
前端开发 Java
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
文章介绍了如何使用SpringBoot创建简单的后端服务器来处理HTTP请求,包括建立连接、编写Controller处理请求,并返回响应给前端或网址。
279 0
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
|
设计模式 数据库连接 PHP
PHP中的设计模式:如何提高代码的可维护性与扩展性在软件开发领域,PHP 是一种广泛使用的服务器端脚本语言。随着项目规模的扩大和复杂性的增加,保持代码的可维护性和可扩展性变得越来越重要。本文将探讨 PHP 中的设计模式,并通过实例展示如何应用这些模式来提高代码质量。
设计模式是经过验证的解决软件设计问题的方法。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理地使用设计模式可以显著提高代码的可维护性、复用性和扩展性。本文将介绍几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并通过具体的例子展示如何在PHP项目中应用这些模式。
|
网络安全 开发工具 云计算
服务器看代码阿里云
随着云计算技术的发展,阿里云作为国内领先的云计算服务提供商,其服务器受到广大用户青睐。本文主要介绍如何在阿里云服务器上便捷地查看与管理代码,如使用SSH连接服务器并通过命令行工具打开文件,以及利用Git进行版本控制和协作开发,提高代码管理效率。无论个人开发者还是企业团队,都能借助阿里云服务器高效地部署与管理应用程序,提升工作效率及产品质量。
197 10
|
存储 Linux 网络安全
存放位置阿里云服务器代码的
阿里云服务器提供虚拟化计算、存储与网络服务。创建服务器时,可基于不同需求选择代码存放位置:文件系统支持直接通过SSH访问与编辑;公共目录如 `/var/www/html` 适合Web应用;对象存储OSS适用于大数据处理;代码托管服务如 GitLab 则利于版本控制与团队协作。合理选择有助于提升开发效率。
158 7
|
人工智能 运维 Cloud Native
专访阿里云:AI 时代服务器操作系统洗牌在即,生态合作重构未来
AI智算时代,服务器操作系统面临的挑战与机遇有哪些?
专访阿里云:AI 时代服务器操作系统洗牌在即,生态合作重构未来
|
网络安全 云计算
阿里云服务器代码
阿里云作为中国领先的云计算服务提供商,为用户提供了丰富的云服务器实例。本文详细介绍如何在阿里云上定位服务器代码,包括利用控制台搜索实例并访问详细页面查找相关信息,使用`ssh`和`cat`等命令行工具远程连接及读取文件内容,以及在遇到困难时及时联系阿里云技术支持获取帮助的具体方法。无论您的实例类型与操作系统有何不同,总有一种方式能帮您顺利找到所需的服务器代码。
154 3