windows socket实例

下面是使用TCP协议的服务端

 

#include <stdio.h>

#include <stdlib.h>
#include <map>
#include <string>
#include <winsock2.h>
#include <direct.h>


#pragma comment(lib,"ws2_32.lib")


DWORD WINAPI chat(LPVOID p)
{

SOCKET con_sock = *(SOCKET*)p;
char data[200];
memset(data,0,200);
int a;
while(1)
{
a=recv(con_sock,data,sizeof(data),0);
if(a==0 || a==SOCKET_ERROR)
{
printf("thread exit\n");
closesocket(con_sock);
exit(0);
}
else
{
printf("%d\n",strlen(data));
printf("accept information:%s\n",data);
memset(data,0,200);
}
}
}


DWORD WINAPI send_sock(LPVOID p)
{
SOCKET con_sock = *(SOCKET*)p;
char s[200];
memset(s,0,200);

while(1)
{
fgets(s,sizeof(s),stdin);
if(s[strlen(s)-1]=='\n')
{
s[strlen(s)-1]='\0';
}
send(con_sock,s,strlen(s),0);
}
}




int main(int argc,char* argv[])
{
//初始化window 套接字即(加载套接字库,下面是使用winsock2.2的版本库)
WORD sockVersion = MAKEWORD(2,2);
WSADATA wsadata;


if(0 != WSAStartup(sockVersion,&wsadata))
{
return 0 ;
}
//创建套接字  协议族,地址家族,决定着地址类型
SOCKET listen_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(listen_sock == INVALID_SOCKET)
{
printf("listen_sock fail");
}


//bind套接字
sockaddr_in addr_in;
addr_in.sin_family=AF_INET;
addr_in.sin_port = 8888;
addr_in.sin_addr.S_un.S_addr = inet_addr("192.168.14.152");
if(SOCKET_ERROR == bind(listen_sock,(sockaddr*)&addr_in,sizeof(addr_in)))
{
printf("bind fail\n");
}
//监听
listen(listen_sock,4);


//等待客户端的请求
SOCKET com_sock;
while(1)
{
DWORD newthreadid;
sockaddr_in client_addr;
int len = sizeof(client_addr);
com_sock = accept(listen_sock,(sockaddr*)&client_addr,&len);
if(com_sock == INVALID_SOCKET)
{
printf("com_sock fail");
}
else
{
printf("connect success\n");
}
printf("ip:%s\n  port:%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
HANDLE handle = CreateThread(NULL,0,chat,(LPVOID)&com_sock,0,&newthreadid);
if(handle == NULL)
{
printf("create thread fail");
break;
}
HANDLE handle1 = CreateThread(NULL,0,send_sock,(LPVOID)&com_sock,0,NULL);
if(handle1 == NULL)
{
printf("create thread fail");
break;
}


CloseHandle(handle);
CloseHandle(handle1);
}

closesocket(com_sock);
closesocket(listen_sock);

WSACleanup();


system("pause");
return 0;

}

客户端

#include <WinSock2.h>
#include <stdio.h>
#include <sstream>
#include <string>
#include <strstream>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#pragma pack(4)
DWORD WINAPI chat(LPVOID p)
{
SOCKET con_sock=*(SOCKET*)p;
char s[200];
memset(s,0,sizeof(s));
int a;
FILE* fp;
while(1)
{
//recvfrom也可以
a=recv(con_sock,s,sizeof(s),0);
printf("%d\n",a);
if(0==a || SOCKET_ERROR==a)
{
printf("connect interrupt\n");
closesocket(con_sock);
exit(0);
}
else
{
printf("other: %s\n",s);
fp=fopen("./record.txt","a+");
if(fp==NULL)
{
printf("save message fail\n");
}
else
{
fprintf(fp,"                            ");
fwrite(s,strlen(s),1,fp);
fwrite("\n",1,1,fp);
}
fclose(fp);
memset(s,0,sizeof(s));
}
}
}


DWORD WINAPI chat1(LPVOID p)
{
SOCKET con_sock=*(SOCKET*)p;
printf("%d\n",con_sock);
int len;
char s[200];
memset(s,0,sizeof(s));
FILE* fp;
int pos=0;
char* s1=NULL;
while(1)
{
printf("input:\n");
fgets(s,200,stdin);
len = strlen(s);


if(strcmp(s,"seek\n")!=0)
{
if(s[len-1]=='\n')
{
s[len-1]='\0';
}
printf("len2:%d\n",strlen(s));
//sendto 也可以
if(SOCKET_ERROR == send(con_sock,s,strlen(s),0))
{
printf("send fail\n");
}
else
{
fp=fopen("./record.txt","a+");
if(fp==NULL)
{
printf("save message fail\n");
}
else
{
fwrite(s,strlen(s),1,fp);
fwrite("\n",1,1,fp);
}
fclose(fp);
}
}
else
{
fp=fopen("./record.txt","r");
if(fp==NULL)
{
printf("save message fail\n");
}
fseek(fp,0,SEEK_END);

pos = ftell(fp);
s1 = (char*)malloc(pos);
rewind(fp);
fread(s1,1,pos,fp);
s1[pos]='\0';
printf(s1);
fclose(fp);
}
}


}


int main()
{
WORD word = MAKEWORD(2,2);
WSADATA wsadata;
if(WSAStartup(word,&wsadata)!= 0 )
{
return 0 ;
}


SOCKET client_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(client_sock == INVALID_SOCKET)
{
printf("client_sock fail\n");
}
printf("%d\n",client_sock);
sockaddr_in client_addr;
client_addr.sin_addr.S_un.S_addr = inet_addr("192.168.14.152");
client_addr.sin_port = 8888;
client_addr.sin_family = AF_INET;
/*
if(SOCKET_ERROR == bind(client_sock,(sockaddr*)&client_addr,sizeof(client_addr)))
{
printf("bind fail\n");
}
*/
if(SOCKET_ERROR == connect(client_sock,(sockaddr*)&client_addr,sizeof(client_addr)))
{
printf("connect fail \n");
}


/* sockaddr_in addr_in;
addr_in.sin_family=AF_INET;
addr_in.sin_port = 8888;
addr_in.sin_addr.S_un.S_addr = inet_addr("192.168.14.152");
*/
HANDLE handle = CreateThread(NULL,0,chat,(LPVOID)&client_sock,0,NULL);
if(handle == NULL)
{
printf("createthread fail\n");
}
HANDLE handle1 = CreateThread(NULL,0,chat1,(LPVOID)&client_sock,0,NULL);
if(handle1 == NULL)
{
printf("createthread1 fail\n");
}

CloseHandle(handle);
CloseHandle(handle1);

while(1);

WSACleanup();
system("pause");
return 0;
}

 

函数说明

 

int  socket(int protofamily, int type, int protocol);//返回sockfd

功能:用于创建一个socket描述符(socket descriptor),和文件描述符是一样的。

参数一:protofamily:即协议域,又称为协议族(family)。常用的协议族有,AF_INET(IPV4)AF_INET6(IPV6)AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。不同的协议族对应的地址类型不同。

参数二:type:指定socket类型。常用的socket类型有,SOCK_STREAMSOCK_DGRAMSOCK_RAWSOCK_PACKETSOCK_SEQPACKET等等

参数三:protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCPIPPTOTO_UDPIPPROTO_SCTPIPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议

该函数创建一个socket套接字,参数二决定套接字类型,套接字类型对应着传输协议,参数三设置0,那么使用套接字类型默认的传输协议,有的类型可以有多种协议,有的只有一种协议

 

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:将地址绑定给sockfd。
参数一:套接字描述符,
参数二:绑定的地址,改地址存在结构体里
参数三:结构体大小
看看这个结构体:
 

struct sockaddr

{

__SOCKADDR_COMMON (sa_);    /* Common data: address family and length. 占2个字节协议族*/

char sa_data[14];       /* Address data.  地址+端口号*/

};

struct sockaddr_in

{

__SOCKADDR_COMMON (sin_);           /* 协议族 */

in_port_t sin_port;         /* Port number. 端口号 */

struct in_addr sin_addr;        /* Internet address. IP地址 */

/* Pad to size of `struct sockaddr'.  用于填充的0字节 */

unsigned char sin_zero[sizeof (struct sockaddr) -

__SOCKADDR_COMMON_SIZE -

sizeof (in_port_t) -

sizeof (struct in_addr)];

};

struct in_addr

{

in_addr_t s_addr;

};

二者的占用的内存大小是一致的,因此可以互相转化,从这个意义上说,他们并无区别。

int listen(int sockfd, int backlog);

listen函数的第一个参数即为要监听的socket描述字,第二个参数表示该服务端允许同时连接的个数,是服务端处理连接队列的能力。如:backlog为5,那么表示最多同时允许5个客户端连接,当有第6个来连接时,就会出错。并不是表示能够连接客户端的数量。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与服务端的连接。

 

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //返回连接connect_fd
功能:等待客户端的连接请求,并返回连接套接字。accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接收的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,可以关闭其连接的socket套接字,监听套接字不关闭。

参数一:监听套接字,通过监听套接字来构建一个与客户端连接的套接字。

参数二:为客户端的地址数据
参数三:为存放客户端地址数据的结构体大小
 
下面是发送和接收消息的函数
 
       ssize_t send(int sockfd, const void *buf, size_t len, int flags);
       ssize_t recv(int sockfd, void *buf, size_t len, int flags);
 
flags说明recvsend
 MSG_DONTROUTE绕过路由表查找    •
 MSG_DONTWAIT仅本操作非阻塞   •      •
 MSG_OOB    发送或接收带外数据  •  •
 MSG_PEEK  窥看外来消息  • 
 MSG_WAITALL  等待所有数据   • 

 

       ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

       ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

       ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
       ssize_t read(int fd, void *buf, size_t count);
       ssize_t write(int fd, const void *buf, size_t count);

 

同步Socket的send函数的执行流程,当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲的长度(因为待发送数据是要copy到套接字s的发送缓冲区的,注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里):

1.如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;

2.如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么 send就比较s的发送缓冲区的剩余空间和len:

      (i)如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完;

      (ii)如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里。

3.如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。

注意:send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回 SOCKET_ERROR)。只有发送0字节时才会返回0。

 

同步Socket的recv函数的执行流程:当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,

如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR;

如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕;

当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数;

如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。

说明:当接收缓冲区为0时,收不到数据。而当发送缓冲区为0时,send返回时表示数据已经在传输了。这样做会影响性能。

最后需要
int close(int fd);
关闭套接字
 

附录:

 

sendto可以在参数中指定发送的目标地址,send需要socket已建立连接,sendto可用于无连接的socket

对于有连接的socket,两者一样,sendto最后两个参数没用.

对于recvfrom ,可同时应用于面向连接的和无连接的套接字。recv一般只用在面向连接套接字,几乎等同于recvfrom,只要将recvfrom的第五个参数设置NULL。

这里说明一下,服务端listen(sock1);监听套接字sock1,和使用accept返回的连接套接字sock2,这两个套接字相互间没有任何影响(关闭监听套接字后,连接套接字仍然可以与客户端传递信息,关闭监听套接字后,不能在与其他客户端连接了)。如,客户端csock连接服务端后,服务端会通过监听套接字产生一个连接套接字(sock2),此时关闭监听套接字,客户端csock仍能和服务端sock2通信。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是一个简单的 Windows 平台下使用 C 语言编写的 socket 编程实例: #include <stdio.h> #include <string.h> #include <winsock2.h> #define MAX_BUFFER_SIZE 1024 int main() { WSADATA wsaData; SOCKET serverSocket, clientSocket; struct sockaddr_in serverAddr, clientAddr; int clientAddrLen = sizeof(clientAddr); char buffer[MAX_BUFFER_SIZE]; char response[MAX_BUFFER_SIZE] = "Hello from server!"; // 初始化 Winsock if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to initialize winsock.\n"); return 1; } // 创建服务器 socket serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket == INVALID_SOCKET) { printf("Failed to create server socket.\n"); WSACleanup(); return 1; } // 设置服务器地址和端口 serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(12345); // 绑定服务器地址和端口到 socket if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { printf("Failed to bind server address.\n"); closesocket(serverSocket); WSACleanup(); return 1; } // 开始监听客户端连接 if (listen(serverSocket, 5) == SOCKET_ERROR) { printf("Failed to listen for client connections.\n"); closesocket(serverSocket); WSACleanup(); return 1; } printf("Server is listening for incoming connections...\n"); // 接受客户端连接并进行通信 while (1) { // 等待客户端连接 clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen); if (clientSocket == INVALID_SOCKET) { printf("Failed to accept client connection.\n"); closesocket(serverSocket); WSACleanup(); return 1; } printf("Client connected: %s:%d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); // 接收客户端发送的数据 memset(buffer, 0, sizeof(buffer)); if (recv(clientSocket, buffer, sizeof(buffer), 0) == SOCKET_ERROR) { printf("Failed to receive data from client.\n"); closesocket(clientSocket); closesocket(serverSocket); WSACleanup(); return 1; } printf("Received data from client: %s\n", buffer); // 发送响应给客户端 if (send(clientSocket, response, strlen(response), 0) == SOCKET_ERROR) { printf("Failed to send response to client.\n"); closesocket(clientSocket); closesocket(serverSocket); WSACleanup(); return 1; } printf("Response sent to client: %s\n", response); // 关闭客户端 socket closesocket(clientSocket); } // 关闭服务器 socket closesocket(serverSocket); // 清理 Winsock WSACleanup(); return 0; } 在该例子中,服务器创建一个 socket 监听客户端的连接请求。一旦有客户端连接进来,服务器会接收客户端发送的数据,然后返回一个固定的响应给客户端。 ### 回答2: 以下是一个使用Windows的C语言编写的简单socket编程实例: ```c #include <stdio.h> #include <winsock2.h> #define MAX_BUFFER_SIZE 1024 #define DEFAULT_PORT 8080 int main() { WSADATA wsaData; SOCKET listenSocket, clientSocket; struct sockaddr_in serverAddr, clientAddr; int clientAddrSize; char buffer[MAX_BUFFER_SIZE]; // 初始化Winsock库 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Winsock库初始化失败.\n"); return 1; } // 创建监听socket listenSocket = socket(AF_INET, SOCK_STREAM, 0); if (listenSocket == INVALID_SOCKET) { printf("创建socket失败.\n"); WSACleanup(); return 1; } // 设置服务器地址和端口 serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(DEFAULT_PORT); serverAddr.sin_addr.s_addr = INADDR_ANY; // 绑定socket到服务器地址和端口 if (bind(listenSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { printf("绑定socket失败.\n"); closesocket(listenSocket); WSACleanup(); return 1; } // 开始监听连接请求 if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) { printf("监听连接请求失败.\n"); closesocket(listenSocket); WSACleanup(); return 1; } printf("服务器正在监听端口 %d ...\n", DEFAULT_PORT); while (1) { // 接受客户端连接请求 clientSocket = accept(listenSocket, (struct sockaddr *)&clientAddr, &clientAddrSize); if (clientSocket == INVALID_SOCKET) { printf("接收连接失败.\n"); closesocket(listenSocket); WSACleanup(); return 1; } // 从客户端读取数据 memset(buffer, 0, sizeof(buffer)); if (recv(clientSocket, buffer, MAX_BUFFER_SIZE, 0) == SOCKET_ERROR) { printf("接收数据失败.\n"); closesocket(clientSocket); WSACleanup(); return 1; } // 打印接收到的数据 printf("接收到来自客户端的数据: %s\n", buffer); // 发送响应给客户端 const char *response = "服务器已收到你的请求."; if (send(clientSocket, response, strlen(response), 0) == SOCKET_ERROR) { printf("发送响应失败.\n"); closesocket(clientSocket); WSACleanup(); return 1; } // 关闭客户端socket closesocket(clientSocket); } // 关闭监听socket closesocket(listenSocket); // 清理Winsock库 WSACleanup(); return 0; } ``` 这个例子创建了一个服务器程序,它通过在本地8080端口监听连接请求。一旦有客户端连接,服务器将接收该客户端的请求,并向客户端发送一个简单的响应。此例仅用于演示目的,并省略了错误处理和多线程处理。在实际开发中,您还需要根据需求添加适当的错误处理和多线程支持。 ### 回答3: 以下是一个简单的Windows C语言socket编程实例: #include <stdio.h> #include <stdlib.h> #include <winsock2.h> #define MAX_BUFFER_SIZE 1024 #define PORT 8080 int main() { WSADATA wsaData; SOCKET serverSocket, clientSocket; struct sockaddr_in serverAddress, clientAddress; char buffer[MAX_BUFFER_SIZE]; // 初始化Winsock库 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { fprintf(stderr, "Failed to initialize Winsock\n"); return 1; } // 创建服务器socket if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { fprintf(stderr, "Failed to create server socket\n"); WSACleanup(); return 1; } // 绑定服务器地址和端口 serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = INADDR_ANY; serverAddress.sin_port = htons(PORT); if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) { fprintf(stderr, "Failed to bind server socket\n"); closesocket(serverSocket); WSACleanup(); return 1; } // 监听客户端连接请求 if (listen(serverSocket, 1) == SOCKET_ERROR) { fprintf(stderr, "Failed to listen for client connections\n"); closesocket(serverSocket); WSACleanup(); return 1; } printf("Server is listening on port %d\n", PORT); // 接受客户端连接 int clientAddressSize = sizeof(clientAddress); if ((clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressSize)) == INVALID_SOCKET) { fprintf(stderr, "Failed to accept client connection\n"); closesocket(serverSocket); WSACleanup(); return 1; } printf("Client connected\n"); // 接收和发送数据 while (1) { memset(buffer, 0, sizeof(buffer)); if (recv(clientSocket, buffer, sizeof(buffer), 0) == SOCKET_ERROR) { fprintf(stderr, "Failed to receive data from client\n"); closesocket(clientSocket); closesocket(serverSocket); WSACleanup(); return 1; } printf("Received from client: %s", buffer); if (send(clientSocket, buffer, strlen(buffer), 0) == SOCKET_ERROR) { fprintf(stderr, "Failed to send data to client\n"); closesocket(clientSocket); closesocket(serverSocket); WSACleanup(); return 1; } } // 关闭socket和Winsock库 closesocket(clientSocket); closesocket(serverSocket); WSACleanup(); return 0; } 以上是一个简单的基于Windows平台的C语言socket编程示例。它创建了一个服务器端socket,并通过指定的端口监听客户端连接请求。一旦有客户端连接成功,服务器将接收来自客户端的数据,并将其发送回客户端。这个过程将持续进行,直到服务器或客户端关闭连接。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值