网络编程( 实现局域网的相互通信,收发数据)

网络编程——TCP通信

linux网络编程

一,ip和端口的了解

  1. 什么是socket?

1). socket在英文中翻译为套接字,插座,排插。(因为插座种类非常多,就好像有很多协议一样,这些协议必须一致,才可以进行通信。)
网络编程 -> 套接字编程、socket编程。

2).socket()其实也是一个函数接口。
该函数功能: 就是创建一个套接字。

3).socket也指套接字 -> 特殊的文件描述符。

  1. 什么是IP地址?
    例如: 192.168.14.2 -> 点分制
    在一个局域网中,每一台主机都会有一个IP地址,而且IP地址必须是相同的网段 -> 指的是IP地址前3个数字一样。
    每一个IP地址都是32位。 如果在网络编程的代码中使用到IP地址,那么就必须把这32位的数据转成网络字节。
    (简单来讲:电脑与电脑之间的通信是需要一个地址的,有了地址,我才可以准确的跟小明的电脑通信,就相当于日常生活中的门牌号一样,有了门牌号,我就可以找到小明的家,假如没有IP地址/门牌号,我又怎么跟小明的电脑通信/怎么找到小明的家?)

  2. 什么是端口号?
    在一个局域网中,两台主机需要通信,除了IP地址需要处于同一个网段中,还需要使用相同的端口号。
    (端口号其实就比如电脑上的QQ,端口号其实就是电脑上的应用的地址,因为咱们通过IP地址识别并找到相应的电脑主机还不够,还要找出对应的应用,QQ之间的通信其实就是相同的端口号的,那为什么QQ不能和微信之间通信呢,很明显,端口号不一样)

                  主机A              主机B 
    

IP地址相同网段: 192.168.14.2 192.168.14.4
相同的端口号: 5000 5000

端口号是16位。 如果在网络编程的代码中使用到端口号,那么就必须把这16位的数据转成网络字节。
系统用了端口: 0~1023 (用户不能使用)
用户有效端口: 1024~65535 (用户随便用) -> 端口号使用结束后,至少等待1分钟之后,才能再次使用。

  1. 如何查看和配置ubuntu的IP地址
    自己查看其他资料,这里不必多说。

二,通信协议------TCP

  1. TCP协议特点?
    TCP协议全称: Tranmission Control Protocol
    传输 控制 协议

    TCP协议通信类似于现实打电话,必须要连接上才能通信。 -> TCP协议是一种可靠的传输方式。
    TCP协议作用场景: 账号登录,文件传输。

  2. 分析TCP协议原理。
    TCP协议能够正常连接,原因是双方遵循三次握手协议。
    1)第一次握手:
    在建立TCP连接时,客户端发送syn包(seq=j)给服务器,客户端等待服务器确认。

    2)第二次握手:
    服务器收到了syn包之后,必须确认客户端的SYN(ack=j+1),同时服务器也会发送一个SYN包(seq=k)给客户端,即SYN+ACK,此时服务器进入等待状态。

    3)第三次握手:
    客户端收到服务器发过来的SYN+ACK包,想服务器发送确认包(ACK=k+1),此包发送完毕,客户端与服务器就连接上了。

  3. 1).创建TCP套接字
    接口声明:int socket(int domain, int type, int protocol);

    参数:
    domain:域。

    AF_INET/PF_INET:网际协议
    AF_UNIX/PF_UNIX:本地协议,可写成AF_LOCAL/PF_LOCAL
    

    type:类型。

    SOCK_STREAM:流式套接字
    SOCK_DGRAM:数据包套接字
    

    protocol:协议。
    一般为0

    返回值:
    成功:待连接套接字
    失败:-1

    备注:在网际协议中,选择流式套接字就代表TCP协议,选择数据包套接字就代表UDP协议,第三个参数protocol一般都不用。

    2)绑定IP地址和端口号
    接口声明:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    参数:
    sockfd:待连接套接字
    addr:包含本地地址(IP+PORT)的通用地址结构体的指针
    addrlen:地址结构体大小

    返回值:
    成功:0
    失败:-1

    备注:
    通用地址结构体的定义:

struct sockaddr
{
sa_family_t  sa_family;  -> 协议 -> 2
char        sa_data[14];  -> IP、端口号..
};

特殊地址结构体 —— IPv4地址结构体:

struct sockaddr_in
{           
       u_short sin_family;	// 地址族  -> 2
       u_short sin_port;	// 端口号  -> 2
       struct in_addr sin_addr;	// IPV4地址  -> 4
       char sin_zero[8];
};

   struct in_addr
{
in_addr_t s_addr;	// 无符号32位网络地址 -> IP 
};

3) 设置监听套接字
接口声明:int listen(int sockfd, int backlog);

参数:
sockfd:待连接套接字
backlog:最大同时接收连接请求个数

返回值:
成功:0,并将sockfd设置为监听套接字
失败:-1

4) 等待对端连接请求
接口声明:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:
sockfd:监听套接字
addr:通用地址结构体,用以存储对端地址(IP+PORT)
addrlen:参数addr的存储区域大小

返回值:
成功:已连接套接字(非负整数)
失败:-1

5) 从TCP套接字接收数据
接口声明: ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数:
sockfd:已连接套接字
buf:存储数据缓冲区
len:缓冲区大小
flags:接收标志,置为0即可以。

返回值:
成功:已接收字节数
失败:-1

6)将文本地址转化为二进制地址
接口声明:int inet_pton(int af, const char *src, void *dst);

参数:
af:地址族。AF_INET:IPv4地址
src:指向“点分式”IPv4或IPv6地址的指针,例如“192.168.1.100”
dst:类型为struct in_addr *的指针

返回值:
成功:1
失败:0代表地址与地址族不匹配,-1代表地址不合法

7)将二进制地址转化为文本地址
接口声明:const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

参数:
af:地址族。

AF_INET:IPv4地址
AF_INET6:IPv6地址

src:类型为struct in_addr *或者struct in6_addr *的指针
dst:地址缓冲区指针,缓冲区至少
size:地址缓冲区大小,至少要INET_ADDRSTRLEN或者INET6_ADDRSTRLEN个字节

返回值:
成功:dst
失败:NULL

8)连接对端监听套接字
接口声明:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:
sockfd:待连接套接字
addr:包含对端地址(IP+PORT)的通用地址结构体的指针
addrlen:地址结构体大小

返回值:
成功:0
失败:-1

- 简单的我们通过分析两段代码就可以理解解以上内容

服务器端(rose):
编译: ./rose 50000
rose是文件名,50000是端口号

#include "head.h"

int main(int argc,char *argv[])  // ./rose 50000
{
	//1. 创建TCP套接字
	int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);//AF_INET:网际协议  SOCK_STREAM:流式套接字
	printf("sockfd = %d\n",sockfd);
	
	//2. 绑定IP地址,端口号
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,len);
	
	srvaddr.sin_family = AF_INET;  //地址族,AF_INET:网际协议(ipv4地址)
	srvaddr.sin_port = htons(atoi(argv[1]));  //端口号  htons:将一个无符号短整型数值转换为网络字节序   atoi:将字符串转化为整型数
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP地址  /usr/include/linux/in.h   //htonl:将主机的无符号长整形数转换成网络字节顺序
	                                //INADDR_ANY:指的是服务器当前的IP地址
	
	bind(sockfd,(struct sockaddr *)&srvaddr,len);  //绑定IP+端口
	
	//3. 设置监听套接字
	//调用listen之前: sockfd -> 待连接的套接字
	listen(sockfd,5);
	//调用listen之后: sockfd -> 监听套接字
	
	//4. 等待客户端的连接
	struct sockaddr_in cliaddr;
	int connfd;
	
	connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);//阻塞等待客户端建立连接,成功的话,返回一个与客户端成功连接的socket文件描述符
	if(connfd >= 0)
	{
		printf("connfd = %d\n",connfd);
		printf("new connection:%s\n",inet_ntoa(cliaddr.sin_addr));
	}
	
	//5. 读取套接字上的数据
	char buf[100];
	while(1)
	{
		bzero(buf,sizeof(buf));
		//阻塞
		recv(connfd,buf,sizeof(buf),0);//用来接收远程主机通过套接字sockfd发送来的数据,并把这些数据保存到数组buf中。
		printf("from jack:%s",buf);
		
		if(strncmp(buf,"fenshou",7) == 0)
		{
			break;
		}
	}
	
	//6. 回收TCP套接字的资源
	close(sockfd);
	close(connfd);
	
	return 0;	
}

客户端(jack):
编译:./jack 192.168.14.11 50000
jack:该程序文件名
192.168.14.11:服务器的IP地址
50000:端口号(必须要跟服务器的端口号一样)

#include "head.h"

int main(int argc,char *argv[])  // ./jack 192.168.14.11 50000
{
	//1. 创建TCP套接字
	int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	printf("sockfd = %d\n",sockfd);
	
	//2. 准备rose的IP地址。
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,len);
	
	srvaddr.sin_family = AF_INET;  //地址族
	srvaddr.sin_port = htons(atoi(argv[2]));  //端口号
	inet_pton(AF_INET,argv[1],&srvaddr.sin_addr); //设置好Rose的IP地址
	
	
	//3. 发起连接
	//connect调用之前: sockfd  -> 待连接套接字
	int ret = connect(sockfd,(struct sockaddr *)&srvaddr,len);
	if(ret == 0)
	{
		printf("connect success!\n");
	}
	//connect调用之后: sockfd  -> 已连接套接字
	
	//4. 发送数据给rose
	char buf[100];
	
	while(1)
	{
		bzero(buf,sizeof(buf));
		fgets(buf,sizeof(buf),stdin); //从键盘获取数据,并放到缓冲区buf当中
		
		send(sockfd,buf,strlen(buf),0); //用来发送buf里面的数据到另外一个主机当中
		//参数: 1.socket:指定发送端套接字描述符。 2.buf: 表示存放发送数据的缓冲区。 3.strlen 实际要发送的字节数, 4.第四个参数一般置零
		
		//假如键盘中输入fenshou,就退出
		if(strncmp(buf,"fenshou",7) == 0)
		{
			break;
		}
	}
	
	//5. 回收TCP套接字的资源
	close(sockfd);
	
	return 0;
}



/*在main(int argc,char *argv[]);
  argc表示的是执行的时候的参数的个数
  argv[]表示的是执行参数当中的某一个
  假如一个程序是my.exe,我在执行的时候这样执行./my ss yy
  则argc为3,argv[0]则为my,argv[1]为ss,argv[2]为yy
  
  同样在此代码当中16行17行当中的argv[1]和argv[2]参考第三行的注释
  该文件执行的是./jack 192.168.14.11 50000,所以,从此就比较容易得出该代程序的原理
 */

  • 当然以上代码只能实现客户端单向服务器发送数据
  • 要实现双向能够收发数据,稍微涉及到一点点系统编程 > 线程知识,则有下面代码

服务器端:

#include "head.h"

void *func(void *arg)
{
	int connfd = *(int *)arg;
	
	//不断接收数据
	char buf[100];
	while(1)
	{
		bzero(buf,sizeof(buf));
		recv(connfd,buf,sizeof(buf),0);
		printf("from client:%s",buf);
		if(strncmp(buf,"quit",4) == 0)
		{
			exit(0);
		}
	}
}

int main(int argc,char *argv[]) // ./server 50001
{
	//1. 创建TCP套接字
	int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	
	//2. 绑定IP地址到套接字上
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,len);
	
	srvaddr.sin_family = AF_INET; 
	srvaddr.sin_port = htons(atoi(argv[1]));
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	bind(sockfd,(struct sockaddr *)&srvaddr,len);
	
	//3. 设置监听套接字
	listen(sockfd,5);
	
	//4. 不断等待对方的连接
	struct sockaddr_in cliaddr;
	int connfd;
	connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
	if(connfd > 0)
	{
		printf("new connection:%s\n",inet_ntoa(cliaddr.sin_addr));
	}
	
	//5. 创建子线程
	pthread_t tid;
	pthread_create(&tid,NULL,func,(void *)&connfd);
	
	//6. 不断发送数据给客户端
	char buf[100];
	while(1)
	{
		bzero(buf,sizeof(buf));
		fgets(buf,sizeof(buf),stdin);
		send(connfd,buf,strlen(buf),0);
		if(strncmp(buf,"quit",4) == 0)
		{
			break;
		}
	}
	
	close(sockfd);
	close(connfd);
	return 0;
}

客户端:

#include "head.h"

void *func(void *arg)
{
	int sockfd = *(int *)arg;
	char buf[100];
	while(1)
	{
		bzero(buf,sizeof(buf));
		recv(sockfd,buf,sizeof(buf),0);
		printf("from server:%s",buf);
		if(strncmp(buf,"quit",4) == 0)
		{
			exit(0);
		}
	}
}

int main(int argc,char *argv[])  // ./jack 192.168.14.4 50000
{
	//1. 创建TCP套接字
	int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	printf("sockfd = %d\n",sockfd);
	
	//2. 准备rose的IP地址。
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,len);
	
	srvaddr.sin_family = AF_INET;  //地址族
	srvaddr.sin_port = htons(atoi(argv[2]));  //端口号
	inet_pton(AF_INET,argv[1],&srvaddr.sin_addr); //设置好Rose的IP地址
	
	
	//3. 发起连接
	//connect调用之前: sockfd  -> 待连接套接字
	int ret = connect(sockfd,(struct sockaddr *)&srvaddr,len);
	if(ret == 0)
	{
		printf("connect success!\n");
	}
	//connect调用之后: sockfd  -> 已连接套接字
	
	//创建线程
	pthread_t tid;
	pthread_create(&tid,NULL,func,(void *)&sockfd);
	
	//4. 发送数据给rose
	char buf[100];
	
	while(1)
	{
		bzero(buf,sizeof(buf));
		fgets(buf,sizeof(buf),stdin);
		
		send(sockfd,buf,strlen(buf),0);
		if(strncmp(buf,"quit",4) == 0)
		{
			break;
		}
	}
	
	//5. 回收TCP套接字的资源
	close(sockfd);
	
	return 0;
}

网络编程——UDP通信


一、 通信协议。 – UDP协议
TCP -> Tranmission Control Protocol
UDP -> User Data Protocol
IP -> Internet Protocol

  1. TCP协议与UDP协议差异?
    TCP协议面向有连接情况,是一种可靠传输方式。 -> 必须要连接上,才能通信。
    UDP协议面向无连接情况,是一种不可靠传输方式。-> 不需要连接也能发送数据。

  2. 如何实现?
    UDP协议 -> 类似于写信。

二、实现过程。

  • 服务器:
    //1. 创建UDP套接字
    int sockfd;
    sockfd = socket(AF_INET,SOCK_DGRAM,0);

    //2. 绑定。
    bind(sockfd,(struct sockaddr *)&srvaddr,len);

    //3. 等待对方的数据。
    recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&cliaddr,&len);

    //4. 关闭
    close(sockfd);

  • 客户端:
    //1. 创建UDP套接字
    int sockfd;
    sockfd = socket(AF_INET,SOCK_DGRAM,0);

    //2. 直接发送数据即可。
    sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&srvaddr,len);

    //3. 关闭
    close(sockfd);

  • 两个新东西我们要知道一下

  1. 从UDP套接字接收数据
    接口声明:ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
    struct sockaddr *src_addr, socklen_t *addrlen);

    参数:
    sockfd:UDP套接字
    buf:储存数据缓冲区
    len:缓冲区大小
    flags:接收标志,与函数send的flags完全一致
    src_addr:对端网络地址
    addrlen:地址长度

    返回值:
    成功:已接收字节数
    失败:-1

  2. 向UDP套接字发送数据
    接口声明:ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
    const struct sockaddr *dest_addr, socklen_t addrlen);

    参数:
    sockfd:UDP套接字
    buf:即将发送的数据
    len:数据的长度
    flags:发送标志,与函数send的flags完全一致
    dest_addr:对端网络地址
    addr_len:地址长度

    返回值:
    成功:已发送字节数
    失败:-1

  • 以下是UDP通信的代码
    服务器端:
#include "head.h"

int main(int argc,char *argv[])  //./rose 50002
{
	//1. 创建UDP套接字
	int sockfd;
	sockfd = socket(AF_INET,SOCK_DGRAM,0);
	
	//2. 绑定IP地址,端口号,地址族到UDP套接字中
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,sizeof(srvaddr));
	
	srvaddr.sin_family = AF_INET;//地址族
	srvaddr.sin_port = htons(atoi(argv[1]));//端口号
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址  /usr/include/linux/in.h
	
	//绑定ip+端口
	bind(sockfd,(struct sockaddr *)&srvaddr,len);
	
	//3. 不断等待客户端给自己发送数据
	char buf[100];
	struct sockaddr_in cliaddr;
	while(1)
	{
		//清空缓冲区
		bzero(buf,sizeof(buf));
		//等待对方的数据
		recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&cliaddr,&len);printf("from jack:%s",buf);
		if(strncmp(buf,"quit",4) == 0)
		{
			break;
		}
	}
	
	//4. 回收
	close(sockfd);
	
	return 0;	
}

客户端:

#include "head.h"

int main(int argc,char *argv[])  //./rose 192.168.14.4 50002
{
	//1. 创建UDP套接字
	int sockfd;
	sockfd = socket(AF_INET,SOCK_DGRAM,0);
	
	//2 准备rose的地址。
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,sizeof(srvaddr));
	
	srvaddr.sin_family = AF_INET;//地址族
	srvaddr.sin_port = htons(atoi(argv[2]));//端口号
	inet_pton(AF_INET,argv[1],&srvaddr.sin_addr);//设置好rose的地址
	
	//3. 不断写信给rose。
	char buf[100];
	while(1)
	{
		bzero(buf,sizeof(buf));
		fgets(buf,sizeof(buf),stdin);//从键盘输入数据
		
		//直接发送数据
		sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&srvaddr,len);
		if(strncmp(buf,"quit",4) == 0)
		{
			break;
		}
	}
	
	//4. 回收资源
	close(sockfd);
	
}

网络编程——非阻塞IO

  1. 什么是IO模型?
    网络编程对于数据输出输入方法: 阻塞IO,非阻塞IO,多路复用,信号驱动。

  2. 这几种IO模型原理是如何?
    1)阻塞IO: 一直阻塞等待某一个套接字的数据到达,如果有数据,则读取并返回,如果无数据,就会一直等待。
    read(fd);
    recv(fd);
    accept(sockfd);
    以上的例子,在过去,我们都会觉得是函数阻塞,例如read函数,accept函数阻塞,但是这样想是错误,因为真正阻塞的是套接字/文件描述符,并不是函数本身具有阻塞的属性。

    因为fd是文件描述符 -> fd默认就是阻塞属性 -> read(fd) -> 阻塞

二、非阻塞IO

  1. 如果要使用非阻塞IO,思路是如何?
    1)先创建一个文件描述符/套接字 -> 默认都是阻塞属性。
    2)设置非阻塞的属性给文件描述符 -> 这时候,文件描述符就是非阻塞的。
    3)再调用read()/accept()/recv()去处理这个文件描述符时,就会非阻塞。

  2. 如何设置非阻塞属性给文件描述符? -> fcntl() -> man 2 fcntl

    头文件:
    #include <unistd.h>
    #include <fcntl.h>

    原型:
    int fcntl(int fd, int cmd, … /* arg */ );

    参数:
    fd: 需要设置属性的文件描述符

    cmd:

 F_GETFL (void)  -> void代表第三个参数不需要填
     Get the file access mode and the file status flags; arg is ignored.
     //获取当前文件描述符的属性
F_SETFL (int)   -> int代表后面那个参数要填
    Set the file status flags to the value specified by arg.

O_NONBLOCK   -> 非阻塞属性

返回值:
成功:
F_GETFL 返回文件的属性
F_SETFL 返回0

失败:
	-1
  • 咱们通过一段代码来了解

服务器端


#include "head.h"

int main(int argc,char *argv[])
{
	//1. 创建一个TCP套接字
	int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	
	//2. 绑定IP地址,端口号
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,len);
	
	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[1]));
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	bind(sockfd,(struct sockaddr *)&srvaddr,len);
	
	//3. 设置监听套接字
	listen(sockfd,5);
	
	//sockfd  -> 默认是阻塞属性
	
	//4. 添加非阻塞属性给sockfd
	int state;
	state = fcntl(sockfd,F_GETFL); //state就是sockfd当前的属性。
	state |= O_NONBLOCK;           //state就是新属性了
	fcntl(sockfd,F_SETFL,state);
	
	//sockfd  -> 具有非阻塞属性
	
	//5. 等待客户端连接
	struct sockaddr_in cliaddr;
	bzero(&cliaddr,len);
	int connfd;
	
	while(1)
	{
		connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
		if(connfd > 0) //有人连接,就会返回成功
		{
			printf("connfd = %d\n",connfd);
			printf("new connection:%s\n",inet_ntoa(cliaddr.sin_addr));
			break;
		}
		else{ //没有人连接,就会返回失败
			printf("connect error!\n");
		}
	}
		
	close(sockfd);
	close(connfd);

	return 0;
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以在局域网中发送消息的vbs程序,小巧,易用 go.exe 命令参数: /p 图片路径 [-L1 -L2 -H1 -H2 -B1 -Q1] /z 公告内容 /t 公告时长 /n 网吧名称 /u 网址 --------------------------------------------------------------------------------------------- /p 背景路径 /z 公告内容 /t 公告时长 /n 网吧属名 /u 给公告加连接 ( /p /z /n /t )四位参数的组合使用或其中三位与三位以下参数组合时候,参数不分前后,加上/U时一定要按指定格式书写! '当公告在执行的其间,任务管理器失效,自动退出后,任务管理器复原! 1.增加/u参数 必须用上 /p /z /t /n 四个参数之时,/u才存在,否则都会出错!而且要以下面的顺序格式才OK! ----------------------------------------------------------------------------------------------- 1 2 3 4 5 < /p 图片路径 /z 公告内容 /t 公告时长 /n 网吧名称 /u 网址> 例: go /n 盈众网吧 /p Background.jpg /z 请注意好您的贵重物品 /t 5 /u http:\\www.sina.com.cn (注意:网址的斜线必须为"\") ----------------------------------------------------------------------------------------------- 2.增加六种颜色: (注意: 必须使用在公告内存后面,下面有例子) ----------------------------------------------------------------------------------------------- <-L1> <-L2> <-H1> <-H2> <-B1> <-Q1> ~~~字母匀为大写,小写无效~~~~ 蓝 绿 红 黄 白 青 例: go /z 请注意好您的贵重物品,如有丢失,本网吧概不负责 -B1 /t 3 ----------------------------------------------------------------------------------------------- 3. 去掉了广告 4. 加动态移出! 5.链接为“公告内容”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

睡眼红瞳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值