安卓Socket与pc端c#服务器的通信 附完整代码

一、前言

小生不才,学习安卓socke通信打了很多滚,遇到过很多问题,也翻过前辈们关于socket的讲解,很杂各有见解,案例也不是很完整,于是特地将此安卓Socket与pc端c#服务器的通信代码讲解与socket的资料整理一遍加深理解,希望能有所帮助,小生没写过几次博客,欢迎各位大佬提出宝贵意见。安卓客户端项目源代码与pc端C#服务器与客户端的资源已经上传:安卓Socket与pc端c#服务器的通信完整代码(包含C#服务器与安卓项目工程)-CSDN下载  https://download.csdn.net/download/zengpengqing1204/10374110

 

 

二、Socket的基本概念  

 

1、套接字(socket)概念

 

Socket(套接字)用于描述IP地址和端口,是通信链的句柄,应用程序可以通过Socket向网络发出请求或者应答网络请求。

 

Socket是支持TCP/IP协议的网络通信的基本操作单元,是对网络通信过程中端点的抽象表示,包含了进行网络通信所必需的5种信息:连接所使用的协议、本地主机的IP地址、本地进程的协议端口、远地主机的IP地址以及远地进程的协议端口。

 

套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

 

应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应 用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。

 

2 、建立socket连接

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。

套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。

客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发 给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

 

ps:其他一些几次握手的事情我就不弄出来了,没什么太大的营养,自己了解即可,重在实战

三、Socket的使用

 1、socket的构造方法

Java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的Socket客户端和服务器端。


  Socket的构造方法如下:


  (1)Socket(InetAddress address, int port);


  (2)Socket(InetAddress address, int port, boolean stream);


  (3)Socket(String host, int port);


  (4)Socket(String host, int port, boolean stream);


  (5)Socket(SocketImpl impl);


  (6)Socket(String host, int port, InetAddress localAddr, int localPort);


  (7)Socket(InetAddress address, int port, InetAddrss localAddr, int localPort);


  ServerSocket的构造方法如下:


  (1)ServerSocket(int port);


  (2)ServerSocket(int port, int backlog);


  (3)ServerSocket(int port, int backlog, InetAddress bindAddr);

 

  其中,参数address、host和port分别是双向连接中另一方的IP地址、主机名和端口号;参数stream表示Socket是流Socket还是数据报Socket;参数localAddr和localPort表示本地主机的IP地址和端口号;SocketImpl是Socket的父类,既可以用来创建ServerSocket,也可以用来创建Socket。

 

 

2、输入流和输出流


  Socket提供了方法getInputStream()和getOutPutStream()来获得对应的输入流和输出流,以便对Socket进行读写操作,这两个方法的返回值分别是InputStream和OutPutStream对象。


  为了便于读写数据,我们可以在返回的输入输出流对象上建立过滤流,如PrintStream、InputStreamReader和OutputStreamWriter等。
 

3、关闭Socket

 

  可以通过调用Socket的close()方法来关闭Socket。在关闭Socket之前,应该先关闭与Socket有关的所有输入输出流,然后再关闭Socket。

 

 

四、安卓与PC端的socket通讯

1.主程序代码附首页链接

 

 
  1. package com.example.android_socket_pc_zpq3;

  2.  
  3. import java.io.BufferedReader;

  4. import java.io.IOException;

  5. import java.io.InputStreamReader;

  6. import java.io.OutputStream;

  7. import java.io.UnsupportedEncodingException;

  8. import java.net.Socket;

  9. import java.net.UnknownHostException;

  10.  
  11. import android.app.Activity;

  12. import android.os.Bundle;

  13. import android.os.Handler;

  14. import android.os.Message;

  15. import android.view.View;

  16. import android.view.View.OnClickListener;

  17. import android.widget.EditText;

  18. import android.widget.TextView;

  19. import android.widget.Toast;

  20.  
  21. public class MainActivity extends Activity {

  22. private EditText ipEditText,portEditText,sendEditText;//声明输入IP编辑框,端口号编辑框,发送信息编辑框

  23. private TextView chatTextView;//声明聊天记录显示文本

  24. private Thread connectThread;//声明连接服务器线程

  25. private Handler messageHandler;//声明信息处理机制函数

  26. Socket socket;//声明socket对象

  27. BufferedReader bufferedReader;//声明输入流对象

  28. OutputStream outputStream;//声明输出流对象

  29. Boolean isconnectBoolean = false;//初始化socket连接值为未连接

  30.  
  31. @Override

  32. protected void onCreate(Bundle savedInstanceState) {

  33. super.onCreate(savedInstanceState);

  34. setContentView(R.layout.activity_main);

  35. init();//初始化控件

  36. }

  37.  
  38. //连接服务器线程:连接服务器并接收数据 本人觉得可以放一起写

  39. Runnable connectRunnable = new Runnable() {

  40.  
  41. @Override

  42. public void run() {

  43. // TODO Auto-generated method stub

  44.  
  45. try {

  46. //新建socket对象,填入编辑框IP,端口号

  47. socket=new Socket(ipEditText.getText().toString(),

  48. Integer.valueOf(portEditText.getText().toString()));

  49.  
  50. //取得输入流、输出流

  51. bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));

  52. outputStream=socket.getOutputStream();

  53.  
  54. //接收数据可用子线程也可直接在此线程操作

  55. char[] buffer=new char[256];//定义数组接收输入流数据

  56. String bufferString="";//定义一个字符接收数组数据

  57. int conut =0;//初始化buffer数组长度为0

  58. int tag=0;//初识写入数组的位置

  59.  
  60. //死循环重复接收输入流数据并进行处理

  61. while (true) {

  62. //当输入流写入buffer数组的长度大于0时 即 接收到数据时

  63. while ((conut=bufferedReader.read(buffer))>0) {

  64. //将buffer数组的数据全部写入bufferString字符类型

  65. while ( tag<buffer.length) {

  66. bufferString=bufferString+buffer[tag];

  67. tag++;

  68. }

  69. //将数据给messageHandler刷新UI界面

  70. Message msgMessage =new Message();

  71. msgMessage.obj=bufferString;

  72. msgMessage.what=1;

  73. messageHandler.sendMessage(msgMessage);

  74. //初始化数据,以便处理下一条输入流信息

  75. tag=0;

  76. bufferString="";

  77. }

  78.  
  79. }

  80.  
  81.  
  82.  
  83. } catch (NumberFormatException e) {

  84. // TODO Auto-generated catch block

  85. e.printStackTrace();

  86. } catch (UnknownHostException e) {

  87. // TODO Auto-generated catch block

  88. e.printStackTrace();

  89. } catch (IOException e) {

  90. // TODO Auto-generated catch block

  91. e.printStackTrace();

  92. }

  93. }

  94.  
  95. };

  96.  
  97. //初始化所有控件

  98. private void init() {

  99.  
  100. ipEditText=(EditText) findViewById(R.id.IpeditText);

  101. portEditText=(EditText)findViewById(R.id.PorteditText);

  102. sendEditText=(EditText)findViewById(R.id.sendeditText);

  103.  
  104. chatTextView=(TextView)findViewById(R.id.chattextView);

  105.  
  106.  
  107. //连接服务器按钮事件

  108. findViewById(R.id.connectbutton).setOnClickListener(new OnClickListener() {

  109.  
  110. @Override

  111. public void onClick(View arg0) {

  112. //逻辑判断socket是否连接上,再做操作

  113. if (isconnectBoolean==false) {

  114. connectThread=new Thread(connectRunnable);

  115. connectThread.start();

  116. Toast.makeText(MainActivity.this, "连接服务器成功", 2).show();

  117. isconnectBoolean=true;

  118. }

  119.  
  120.  
  121. }

  122. });

  123.  
  124. //断开服务器按钮事件

  125. findViewById(R.id.breakbutton).setOnClickListener(new OnClickListener() {

  126. @Override

  127. public void onClick(View arg0) {

  128. //逻辑判断socket是否连接上,再做操作

  129. if (isconnectBoolean==true) {

  130. if (socket !=null) {

  131. try {

  132. //先关闭socket连接,再关闭输入流、输出流,

  133. socket.close();

  134. socket = null;

  135. bufferedReader.close();

  136. outputStream.close();

  137.  
  138. } catch (IOException e) {

  139. // TODO Auto-generated catch block

  140. e.printStackTrace();

  141. }

  142. }

  143. //中断连接线程

  144. connectThread.interrupt();

  145. Toast.makeText(MainActivity.this, "断开连接!", 2).show();

  146. isconnectBoolean=false;

  147. }

  148.  
  149. }

  150. });

  151.  
  152. //发送信息给服务器按钮事件

  153. findViewById(R.id.sendbutton).setOnClickListener(new OnClickListener() {

  154. @Override

  155. public void onClick(View arg0) {

  156. //这里可以用线程也可以不用线程

  157. try {

  158. //输出流写入发送编辑框的信息并指定类型UTF-8,注意要加换行

  159. outputStream.write((sendEditText.getText().toString()+"\n").

  160. getBytes("utf-8"));

  161. //输出流发送至服务器

  162. outputStream.flush();

  163.  
  164. //handlerMessage处理发送信息刷新UI界面

  165. Message msgMessage =new Message();

  166. msgMessage.obj=sendEditText.getText().toString();

  167. msgMessage.what=0;

  168. messageHandler.sendMessage(msgMessage);

  169.  
  170. } catch (UnsupportedEncodingException e) {

  171. // TODO Auto-generated catch block

  172. e.printStackTrace();

  173. } catch (IOException e) {

  174. // TODO Auto-generated catch block

  175. e.printStackTrace();

  176. }

  177.  
  178.  
  179.  
  180. }

  181. });

  182.  
  183. //处理接收服务器信息以及发送至服务器信息呈现聊天室效果

  184. messageHandler = new Handler(){

  185. public void handleMessage(android.os.Message msgMessage) {

  186. String sendString="";

  187. String receiveString="";

  188. switch (msgMessage.what) {

  189. case 0:

  190. sendString="曾鹏晴说:"+msgMessage.obj.toString()+"\n";

  191. chatTextView.append(sendString);

  192. break;

  193.  
  194. case 1:

  195. receiveString="小权权说:"+msgMessage.obj.toString()+"\n";

  196. chatTextView.append(receiveString);

  197. break;

  198. default:

  199. break;

  200. }

  201. }

  202. };

  203.  
  204.  
  205.  
  206.  
  207. }

  208.  
  209. }

2、布局文件

 

 

 
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

  2. xmlns:tools="http://schemas.android.com/tools"

  3. android:layout_width="match_parent"

  4. android:layout_height="match_parent"

  5. android:paddingBottom="@dimen/activity_vertical_margin"

  6. android:paddingLeft="@dimen/activity_horizontal_margin"

  7. android:paddingRight="@dimen/activity_horizontal_margin"

  8. android:paddingTop="@dimen/activity_vertical_margin"

  9. tools:context=".MainActivity" >

  10.  
  11. <EditText

  12. android:id="@+id/IpeditText"

  13. android:layout_width="wrap_content"

  14. android:layout_height="wrap_content"

  15. android:layout_alignParentLeft="true"

  16. android:layout_alignParentTop="true"

  17. android:layout_marginLeft="36dp"

  18. android:ems="7"

  19. android:text="192.168.1.101" >

  20.  
  21. <requestFocus />

  22. </EditText>

  23.  
  24. <TextView

  25. android:id="@+id/textView1"

  26. android:layout_width="wrap_content"

  27. android:layout_height="wrap_content"

  28. android:layout_alignBaseline="@+id/IpeditText"

  29. android:layout_alignBottom="@+id/IpeditText"

  30. android:layout_alignParentLeft="true"

  31. android:text="IP:" />

  32.  
  33. <TextView

  34. android:id="@+id/textView2"

  35. android:layout_width="wrap_content"

  36. android:layout_height="wrap_content"

  37. android:layout_alignBaseline="@+id/IpeditText"

  38. android:layout_alignBottom="@+id/IpeditText"

  39. android:layout_toRightOf="@+id/IpeditText"

  40. android:text="Port:" />

  41.  
  42. <EditText

  43. android:id="@+id/PorteditText"

  44. android:layout_width="wrap_content"

  45. android:layout_height="wrap_content"

  46. android:layout_alignBaseline="@+id/textView2"

  47. android:layout_alignBottom="@+id/textView2"

  48. android:layout_alignParentRight="true"

  49. android:ems="4"

  50. android:text="8488" />

  51.  
  52. <Button

  53. android:id="@+id/connectbutton"

  54. android:layout_width="wrap_content"

  55. android:layout_height="wrap_content"

  56. android:layout_alignLeft="@+id/textView1"

  57. android:layout_below="@+id/IpeditText"

  58. android:layout_marginTop="14dp"

  59. android:text="连接服务器" />

  60.  
  61. <Button

  62. android:id="@+id/breakbutton"

  63. android:layout_width="wrap_content"

  64. android:layout_height="wrap_content"

  65. android:layout_alignBaseline="@+id/connectbutton"

  66. android:layout_alignBottom="@+id/connectbutton"

  67. android:layout_marginLeft="41dp"

  68. android:layout_toRightOf="@+id/connectbutton"

  69. android:text="断开服务器" />

  70.  
  71. <EditText

  72. android:id="@+id/sendeditText"

  73. android:layout_width="match_parent"

  74. android:layout_height="wrap_content"

  75. android:layout_alignRight="@+id/PorteditText"

  76. android:layout_below="@+id/connectbutton"

  77. android:layout_marginTop="16dp"

  78. android:ems="10"

  79. android:text="hello 小权权" />

  80.  
  81. <Button

  82. android:id="@+id/sendbutton"

  83. android:layout_width="wrap_content"

  84. android:layout_height="wrap_content"

  85. android:layout_alignLeft="@+id/sendeditText"

  86. android:layout_alignRight="@+id/sendeditText"

  87. android:layout_below="@+id/sendeditText"

  88. android:layout_marginTop="14dp"

  89. android:text="发送信息至服务器" />

  90.  
  91. <TextView

  92. android:id="@+id/textView3"

  93. android:layout_width="wrap_content"

  94. android:layout_height="wrap_content"

  95. android:layout_alignLeft="@+id/sendbutton"

  96. android:layout_below="@+id/sendbutton"

  97. android:layout_marginTop="14dp"

  98. android:text="聊天记录:" />

  99.  
  100. <TextView

  101. android:id="@+id/chattextView"

  102. android:layout_width="wrap_content"

  103. android:layout_height="wrap_content"

  104. android:layout_below="@+id/textView3"

  105. android:layout_toRightOf="@+id/textView1"

  106. android:text="" />

  107.  
  108. </RelativeLayout>

3、PC端c#服务器代码附首页链接

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Text;

  5. using System.Threading.Tasks;

  6. using System.Windows;

  7. using System.Windows.Controls;

  8. using System.Windows.Data;

  9. using System.Windows.Documents;

  10. using System.Windows.Input;

  11. using System.Windows.Media;

  12. using System.Windows.Media.Imaging;

  13. using System.Windows.Navigation;

  14. using System.Windows.Shapes;

  15.  
  16. using System.Net.Sockets;

  17. using System.Net;

  18. using System.Threading;

  19.  
  20. namespace socket

  21. {

  22. /// <summary>

  23. /// MainWindow.xaml 的交互逻辑

  24. /// </summary>

  25. public partial class MainWindow : Window

  26. {

  27. public MainWindow()

  28. {

  29. InitializeComponent();

  30. }

  31. Socket ServerSocket = null;

  32.  
  33. private void btnStart_Click(object sender, RoutedEventArgs e)

  34. {

  35. IPEndPoint IPE = new IPEndPoint(IPAddress.Parse(tboxIP.Text), Int32.Parse(tboxPort.Text));

  36. ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

  37. ServerSocket.Bind(IPE);

  38. ServerSocket.Listen(10);

  39. showmsg("服务器已启动,监听中...");

  40.  
  41. Thread thread = new Thread(ListenClientConnect);

  42. thread.IsBackground=true;

  43. thread.Start();

  44.  
  45. }

  46. Dictionary<string, Socket> dic = new Dictionary<string, Socket>();

  47. private void ListenClientConnect(object obj)

  48. {

  49. while (true)

  50. {

  51. Socket socketClient = ServerSocket.Accept() ;

  52. string RemoteIP = socketClient.RemoteEndPoint.ToString();

  53. dic.Add(RemoteIP, socketClient);

  54. Dispatcher.Invoke(()=>lstboxIP.Items.Add(RemoteIP));

  55. showmsg(RemoteIP + "已连接");

  56.  
  57. Thread recieveThread = new Thread(recievemsg);

  58. recieveThread.IsBackground = true;

  59. recieveThread.Start(socketClient);

  60. }

  61. }

  62.  
  63. private void recievemsg(object soc)

  64. {

  65. Socket socketClient = (Socket)soc;

  66. while (true)

  67. {

  68. byte[] buffer = new byte[1024];

  69. int n = socketClient.Receive(buffer);

  70. //string msg = Encoding.Default.GetString(buffer, 0, n);

  71. string msg = Encoding.UTF8.GetString(buffer, 0, n);

  72. //可在这里指定接受数据格式

  73.  
  74. showmsg(socketClient.RemoteEndPoint.ToString()+":"+msg);

  75. }

  76. }

  77.  
  78. private void showmsg(string p)

  79. {

  80. Dispatcher.BeginInvoke(new Action(() =>

  81. {

  82. rtbx.AppendText(p + "\r\n");

  83. }));

  84. }

  85.  
  86. private void btnStop_Click(object sender, RoutedEventArgs e)

  87. {

  88. ServerSocket.Close();

  89. }

  90.  
  91. private void btnSend_Click(object sender, RoutedEventArgs e)

  92. {

  93. showmsg(tboxMsg.Text);

  94. string ip = lstboxIP.SelectedValue.ToString();

  95. //byte[] by = Encoding.UTF8.GetBytes(tboxMsg.Text);

  96. byte[] by = Encoding.Default.GetBytes(tboxMsg.Text);

  97. dic[ip].Send(by,0);

  98. tboxMsg.Text = "";

  99. }

  100. }

  101. }

 

4、布局文件

 
  1. <Window x:Class="socket.MainWindow"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. Title="MainWindow" Height="350" Width="525">

  5. <Grid>

  6. <Label x:Name="label" Content="IP地址:" HorizontalAlignment="Left" Margin="24,15,0,0" VerticalAlignment="Top"/>

  7. <TextBox x:Name="tboxIP" HorizontalAlignment="Left" Height="23" Margin="86,17,0,0" TextWrapping="Wrap" Text="192.168.1.101" VerticalAlignment="Top" Width="92"/>

  8. <Label x:Name="label1" Content="端口:" HorizontalAlignment="Left" Margin="183,16,0,0" VerticalAlignment="Top"/>

  9. <TextBox x:Name="tboxPort" HorizontalAlignment="Left" Height="23" Margin="228,17,0,0" TextWrapping="Wrap" Text="8488" VerticalAlignment="Top" Width="43"/>

  10. <Button x:Name="btnStart" Content="服务器启动" HorizontalAlignment="Left" Margin="287,19,0,0" VerticalAlignment="Top" Width="75" Click="btnStart_Click"/>

  11. <Button x:Name="btnStop" Content="服务器停止" HorizontalAlignment="Left" Margin="377,19,0,0" VerticalAlignment="Top" Width="75" Click="btnStop_Click"/>

  12. <ListBox x:Name="lstboxIP" HorizontalAlignment="Left" Height="180" Margin="377,65,0,0" VerticalAlignment="Top" Width="116"/>

  13. <Button x:Name="btnSend" Content="发送信息" HorizontalAlignment="Left" Margin="377,271,0,0" VerticalAlignment="Top" Width="75" Click="btnSend_Click"/>

  14.  
  15. <TextBox x:Name="tboxMsg" HorizontalAlignment="Left" Height="23" Margin="24,270,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="320"/>

  16. <RichTextBox x:Name="rtbx" HorizontalAlignment="Left" Height="180" Margin="24,65,0,0" VerticalAlignment="Top" Width="320">

  17. <FlowDocument>

  18. <Paragraph>

  19. </Paragraph>

  20. </FlowDocument>

  21. </RichTextBox>

  22.  
  23. </Grid>

  24. </Window>

5、聊天室效果图

上一篇eclipse及myeclipse快捷键及各种设置 ——Eclipse的联想功能

C#服务器,Android做客户端,完成Socket通信

洗洗睡了,明天来写~

想对作者说点什么? 我来说一句

  • Zengpengqing1204

    Zengpengqing12042018-08-02 11:31:28#4楼

    [java] view plain copy
    1. try {  
    2.     //新建socket对象,填入编辑框IP,端口号  
    3.     socket=new Socket(ipEditText.getText().toString(),  
    4.       Integer.valueOf(portEditText.getText().toString()));  
    5.   
    6.     //取得输入流、输出流  
    7.     bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));  
    8.     outputStream=socket.getOutputStream();  
    9.   
    10.     //接收数据可用子线程也可直接在此线程操作  
    11.     char[] buffer=new char[1024];//定义数组接收输入流数据  
    12.     //int len = bufferedReader.read(buffer);//将网络输入流的数据写入buffer数组  
    13.     int conut =0;//初始化buffer数组长度为0  
    14.     //死循环重复接收输入流数据并进行处理  
    15.     while (true) {  
    16.      //当输入流写入buffer数组的长度大于0时   即 接收到数据时  
    17.      while ((conut=bufferedReader.read(buffer))>0) {  
    18.       //将buffer数组的数据全部写入bufferString字符类型  
    19.       String bufferString = new String(buffer, 0, conut);  
    20.       //将数据给messageHandler刷新UI界面  
    21.       Message msgMessage =new Message();  
    22.       msgMessage.obj=bufferString;  
    23.       msgMessage.what=1;  
    24.       messageHandler.sendMessage(msgMessage);  
    25.       //初始化数据,以便处理下一条输入流信息  
    26.       conut=0;  
    27.         
    28.      }  
    29.        
    30.     }  
  • Zengpengqing1204

    Zengpengqing12042018-08-02 11:30:59#3楼

    改了最后一个朋友反馈的问题,输入流信息的处理显示

  • Zengpengqing1204

    Zengpengqing12042018-06-03 22:06:59#2楼

    源代码在关闭socket时有个小修改 应该是先关闭socket,再依次关闭输入流输出流

  • qq_37179365

    qq_371793652018-05-27 21:48:42#1楼

    博主有两把刷子哦,用上喽

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值