unity深入研究--开发之C#使用Socket与HTTP连接服务器传输数据包
unity深入研究--开发之C#使用Socket与HTTP连接服务器传输数据包
- 2243
1.Socket
Socket不要写在脚本上,如果写在脚本上游戏场景一旦切换,那么这条脚本会被释放掉,Socket会断开连接。场景切换完毕后需要重新在与服务器建立Socket连接,这样会很麻烦。所以我们需要把Socket写在一个单例的类中,不用继承MonoBehaviour。这个例子我模拟一下,主角在游戏中移动,时时向服务端发送当前坐标,当服务器返回同步坐标时角色开始同步服务端新角色坐标。
Socket在发送消息的时候采用的是字节数组,也就是说无论你的数据是 int float short object 都会将这些数据类型先转换成byte[] , 目前在处理发送的地方我使用的是数据包,也就是把(角色坐标)结构体object转换成byte[]发送, 这就牵扯一个问题, 如何把结构体转成字节数组, 如何把字节数组回转成结构体。请大家接续阅读,答案就在后面,哇咔咔。
直接上代码
JFSocket.cs 该单例类不要绑定在任何对象上
001
|
using UnityEngine;
|
002
|
using System.Collections;
|
003
|
using System;
|
004
|
using System.Threading;
|
005
|
using System.Text;
|
006
|
using System.Net;
|
007
|
using System.Net.Sockets;
|
008
|
using System.Collections.Generic;
|
009
|
using System.IO;
|
010
|
using System.Runtime.InteropServices;
|
011
|
using System.Runtime.Serialization;
|
012
|
using System.Runtime.Serialization.Formatters.Binary;
|
013
|
014
|
public class JFSocket
|
015
|
{
|
016
|
017
|
//Socket客户端对象
|
018
|
private Socket clientSocket;
|
019
|
//JFPackage.WorldPackage是我封装的结构体,
|
020
|
//在与服务器交互的时候会传递这个结构体
|
021
|
//当客户端接到到服务器返回的数据包时,我把结构体add存在链表中。
|
022
|
public List<JFPackage.WorldPackage> worldpackage;
|
023
|
//单例模式
|
024
|
private static JFSocket instance;
|
025
|
public static JFSocket GetInstance()
|
026
|
{
|
027
|
if (instance == null )
|
028
|
{
|
029
|
instance = new JFSocket();
|
030
|
}
|
031
|
return instance;
|
032
|
}
|
033
|
034
|
//单例的构造函数
|
035
|
JFSocket()
|
036
|
{
|
037
|
//创建Socket对象, 这里我的连接类型是TCP
|
038
|
clientSocket = new Socket (AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
|
039
|
//服务器IP地址
|
040
|
IPAddress ipAddress = IPAddress.Parse ( "192.168.1.100" );
|
041
|
//服务器端口
|
042
|
IPEndPoint ipEndpoint = new IPEndPoint (ipAddress, 10060);
|
043
|
//这是一个异步的建立连接,当连接建立成功时调用connectCallback方法
|
044
|
IAsyncResult result = clientSocket.BeginConnect (ipEndpoint, new AsyncCallback (connectCallback),clientSocket);
|
045
|
//这里做一个超时的监测,当连接超过5秒还没成功表示超时
|
046
|
bool success = result.AsyncWaitHandle.WaitOne( 5000, true );
|
047
|
if ( !success )
|
048
|
{
|
049
|
//超时
|
050
|
Closed();
|
051
|
Debug.Log( "connect Time Out" );
|
052
|
} else
|
053
|
{
|
054
|
//与socket建立连接成功,开启线程接受服务端数据。
|
055
|
worldpackage = new List<JFPackage.WorldPackage>();
|
056
|
Thread thread = new Thread( new ThreadStart(ReceiveSorket));
|
057
|
thread.IsBackground = true ;
|
058
|
thread.Start();
|
059
|
}
|
060
|
}
|
061
|
062
|
private void connectCallback(IAsyncResult asyncConnect)
|
063
|
{
|
064
|
Debug.Log( "connectSuccess" );
|
065
|
}
|
066
|
067
|
private void ReceiveSorket()
|
068
|
{
|
069
|
//在这个线程中接受服务器返回的数据
|
070
|
while ( true )
|
071
|
{
|
072
|
073
|
if (!clientSocket.Connected)
|
074
|
{
|
075
|
//与服务器断开连接跳出循环
|
076
|
Debug.Log( "Failed to clientSocket server." );
|
077
|
clientSocket.Close();
|
078
|
break ;
|
079
|
}
|
080
|
try
|
081
|
{
|
082
|
//接受数据保存至bytes当中
|
083
|
byte [] bytes = new byte [4096];
|
084
|
//Receive方法中会一直等待服务端回发消息
|
085
|
//如果没有回发会一直在这里等着。
|
086
|
int i = clientSocket.Receive(bytes);
|
087
|
if (i <= 0)
|
088
|
{
|
089
|
clientSocket.Close();
|
090
|
break ;
|
091
|
}
|
092
|
093
|
//这里条件可根据你的情况来判断。
|
094
|
//因为我目前的项目先要监测包头长度,
|
095
|
//我的包头长度是2,所以我这里有一个判断
|
096
|
if (bytes.Length > 2)
|
097
|
{
|
098
|
SplitPackage(bytes,0);
|
099
|
} else
|
100
|
{
|
101
|
Debug.Log( "length is not > 2" );
|
102
|
}
|
103
|
104
|
}
|
105
|
catch (Exception e)
|
106
|
{
|
107
|
Debug.Log( "Failed to clientSocket error." + e);
|
108
|
clientSocket.Close();
|
109
|
break ;
|
110
|
}
|
111
|
}
|
112
|
}
|
113
|
114
|
private void SplitPackage( byte [] bytes , int index)
|
115
|
{
|
116
|
//在这里进行拆包,因为一次返回的数据包的数量是不定的
|
117
|
//所以需要给数据包进行查分。
|
118
|
while ( true )
|
119
|
{
|
120
|
//包头是2个字节
|
121
|
byte [] head = new byte [2];
|
122
|
int headLengthIndex = index + 2;
|
123
|
//把数据包的前两个字节拷贝出来
|
124
|
Array.Copy(bytes,index,head,0,2);
|
125
|
//计算包头的长度
|
126
|
short length = BitConverter.ToInt16(head,0);
|
127
|
//当包头的长度大于0 那么需要依次把相同长度的byte数组拷贝出来
|
128
|
if (length > 0)
|
129
|
{
|
130
|
byte [] data = new byte [length];
|
131
|
//拷贝出这个包的全部字节数
|
132
|
Array.Copy(bytes,headLengthIndex,data,0,length);
|
133
|
//把数据包中的字节数组强制转换成数据包的结构体
|
134
|
//BytesToStruct()方法就是用来转换的
|
135
|
//这里需要和你们的服务端程序商量,
|
136
|
JFPackage.WorldPackage wp = new JFPackage.WorldPackage();
|
137
|
wp = (JFPackage.WorldPackage)BytesToStruct(data,wp.GetType());
|
138
|
//把每个包的结构体对象添加至链表中。
|
139
|
worldpackage.Add(wp);
|
140
|
//将索引指向下一个包的包头
|
141
|
index = headLengthIndex + length;
|
142
|
143
|
} else
|
144
|
{
|
145
|
//如果包头为0表示没有包了,那么跳出循环
|
146
|
break ;
|
147
|
}
|
148
|
}
|
149
|
}
|
150
|
151
|
//向服务端发送一条字符串
|
152
|
//一般不会发送字符串 应该是发送数据包
|
153
|
public void SendMessage( string str)
|
154
|
{
|
155
|
byte [] msg = Encoding.UTF8.GetBytes(str);
|
156
|
157
|
if (!clientSocket.Connected)
|
158
|
{
|
159
|
clientSocket.Close();
|
160
|
return ;
|
161
|
}
|
162
|
try
|
163
|
{
|
164
|
//int i = clientSocket.Send(msg);
|
165
|
IAsyncResult asyncSend = clientSocket.BeginSend (msg,0,msg.Length,SocketFlags.None, new AsyncCallback (sendCallback),clientSocket);
|
166
|
bool success = asyncSend.AsyncWaitHandle.WaitOne( 5000, true );
|
167
|
if ( !success )
|
168
|
{
|
169
|
clientSocket.Close();
|
170
|
Debug.Log( "Failed to SendMessage server." );
|
171
|
}
|
172
|
}
|
173
|
catch
|
174
|
{
|
175
|
Debug.Log( "send message error" );
|
176
|
}
|
177
|
}
|
178
|
179
|
//向服务端发送数据包,也就是一个结构体对象
|
180
|
public void SendMessage( object obj)
|
181
|
{
|
182
|
183
|
if (!clientSocket.Connected)
|
184
|
{
|
185
|
clientSocket.Close();
|
186
|
return ;
|
187
|
}
|
188
|
try
|
189
|
{
|
190
|
//先得到数据包的长度
|
191
|
short size = ( short )Marshal.SizeOf(obj);
|
192
|
//把数据包的长度写入byte数组中
|
193
|
byte [] head = BitConverter.GetBytes(size);
|
194
|
//把结构体对象转换成数据包,也就是字节数组
|
195
|
byte [] data = StructToBytes(obj);
|
196
|
197
|
//此时就有了两个字节数组,一个是标记数据包的长度字节数组, 一个是数据包字节数组,
|
198
|
//同时把这两个字节数组合并成一个字节数组
|
199
|
200
|
byte [] newByte = new byte [head.Length + data.Length];
|
201
|
Array.Copy(head,0,newByte,0,head.Length);
|
202
|
Array.Copy(data,0,newByte,head.Length, data.Length);
|
203
|
204
|
//计算出新的字节数组的长度
|
205
|
int length = Marshal.SizeOf(size) + Marshal.SizeOf(obj);
|
206
|
207
|
//向服务端异步发送这个字节数组
|
208
|
IAsyncResult asyncSend = clientSocket.BeginSend (newByte,0,length,SocketFlags.None, new AsyncCallback (sendCallback),clientSocket);
|
209
|
//监测超时
|
210
|
bool success = asyncSend.AsyncWaitHandle.WaitOne( 5000, true );
|
211
|
if ( !success )
|
212
|
{
|
213
|
clientSocket.Close();
|
214
|
Debug.Log( "Time Out !" );
|
215
|
}
|
216
|
217
|
}
|
218
|
catch (Exception e)
|
219
|
{
|
220
|
Debug.Log( "send message error: " + e );
|
221
|
}
|
222
|
}
|
223
|
224
|
//结构体转字节数组
|
225
|
public byte [] StructToBytes( object structObj)
|
226
|
{
|
227
|
228
|
int size = Marshal.SizeOf(structObj);
|
229
|
IntPtr buffer = Marshal.AllocHGlobal(size);
|
230
|
try
|
231
|
{
|
232
|
Marshal.StructureToPtr(structObj,buffer, false );
|
233
|
byte [] bytes = new byte [size];
|
234
|
Marshal.Copy(buffer, bytes,0,size);
|
235
|
return bytes;
|
236
|
}
|
237
|
finally
|
238
|
{
|
239
|
Marshal.FreeHGlobal(buffer);
|
240
|
}
|
241
|
}
|
242
|
//字节数组转结构体
|
243
|
public object BytesToStruct( byte [] bytes, Type strcutType)
|
244
|
{
|
245
|
int size = Marshal.SizeOf(strcutType);
|
246
|
IntPtr buffer = Marshal.AllocHGlobal(size);
|
247
|
try
|
248
|
{
|
249
|
Marshal.Copy(bytes,0,buffer,size);
|
250
|
return Marshal.PtrToStructure(buffer, strcutType);
|
251
|
}
|
252
|
finally
|
253
|
{
|
254
|
Marshal.FreeHGlobal(buffer);
|
255
|
}
|
256
|
257
|
}
|
258
|
259
|
private void sendCallback (IAsyncResult asyncSend)
|
260
|
{
|
261
|
262
|
}
|
263
|
264
|
//关闭Socket
|
265
|
public void Closed()
|
266
|
{
|
267
|
268
|
if (clientSocket != null && clientSocket.Connected)
|
269
|
{
|
270
|
clientSocket.Shutdown(SocketShutdown.Both);
|
271
|
clientSocket.Close();
|
272
|
}
|
273
|
clientSocket = null ;
|
274
|
}
|
275
|
276
|
}
|
为了与服务端达成默契,判断数据包是否完成。我们需要在数据包中定义包头 ,包头一般是这个数据包的长度,也就是结构体对象的长度。正如代码中我们把两个数据类型 short 和 object 合并成一个新的字节数组。
然后是数据包结构体的定义,需要注意如果你在做IOS和Android的话数据包中不要包含数组,不然在结构体转换byte数组的时候会出错。
Marshal.StructureToPtr () error : Attempting to JIT compile method
JFPackage.cs
01
|
using UnityEngine;
|
02
|
using System.Collections;
|
03
|
using System.Runtime.InteropServices;
|
04
|
05
|
public class JFPackage
|
06
|
{
|
07
|
//结构体序列化
|
08
|
[System.Serializable]
|
09
|
//4字节对齐 iphone 和 android上可以1字节对齐
|
10
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
11
|
public struct WorldPackage
|
12
|
{
|
13
|
public byte mEquipID;
|
14
|
public byte mAnimationID;
|
15
|
public byte mHP;
|
16
|
public short mPosx;
|
17
|
public short mPosy;
|
18
|
public short mPosz;
|
19
|
public short mRosx;
|
20
|
public short mRosy;
|
21
|
public short mRosz;
|
22
|
23
|
public WorldPackage( short posx, short posy, short posz, short rosx, short rosy, short rosz, byte equipID, byte animationID, byte hp)
|
24
|
{
|
25
|
mPosx = posx;
|
26
|
mPosy = posy;
|
27
|
mPosz = posz;
|
28
|
mRosx = rosx;
|
29
|
mRosy = rosy;
|
30
|
mRosz = rosz;
|
31
|
mEquipID = equipID;
|
32
|
mAnimationID = animationID;
|
33
|
mHP = hp;
|
34
|
}
|
35
|
36
|
};
|
37
|
38
|
}
|
在脚本中执行发送数据包的动作,在Start方法中得到Socket对象。
1
|
public JFSocket mJFsorket;
|
2
|
void Start ()
|
3
|
{
|
4
|
mJFsorket = JFSocket.GetInstance();
|
5
|
}
|
让角色发生移动的时候,调用该方法向服务端发送数据。
01
|
void SendPlayerWorldMessage()
|
02
|
{
|
03
|
//组成新的结构体对象,包括主角坐标旋转等。
|
04
|
Vector3 PlayerTransform = transform.localPosition;
|
05
|
Vector3 PlayerRotation = transform.localRotation.eulerAngles;
|
06
|
//用short的话是2字节,为了节省包的长度。这里乘以100 避免使用float 4字节。当服务器接受到的时候小数点向前移动两位就是真实的float数据
|
07
|
short px = ( short )(PlayerTransform.x*100);
|
08
|
short py = ( short )(PlayerTransform.y*100);
|
09
|
short pz = ( short )(PlayerTransform.z*100);
|
10
|
short rx = ( short )(PlayerRotation.x*100);
|
11
|
short ry = ( short )(PlayerRotation.y*100);
|
12
|
short rz = ( short )(PlayerRotation.z*100);
|
13
|
byte equipID = 1;
|
14
|
byte animationID =9;
|
15
|
byte hp = 2;
|
16
|
JFPackage.WorldPackage wordPackage = new JFPackage.WorldPackage(px,py,pz,rx,ry,rz,equipID,animationID,hp);
|
17
|
//通过Socket发送结构体对象
|
18
|
mJFsorket.SendMessage(wordPackage);
|
19
|
}
|
接着就是客户端同步服务器的数据,目前是测试阶段所以写的比较简陋,不过原理都是一样的。哇咔咔!!
01
|
//上次同步时间
|
02
|
private float mSynchronous;
|
03
|
void Update ()
|
04
|
{
|
05
|
mSynchronous +=Time.deltaTime;
|
06
|
//在Update中每0.5s的时候同步一次
|
07
|
if (mSynchronous > 0.5f)
|
08
|
{
|
09
|
int count = mJFsorket.worldpackage.Count;
|
10
|
//当接受到的数据包长度大于0 开始同步
|
11
|
if (count > 0)
|
12
|
{
|
13
|
//遍历数据包中 每个点的坐标
|
14
|
foreach (JFPackage.WorldPackage wp in mJFsorket.worldpackage)
|
15
|
{
|
16
|
float x = ( float )(wp.mPosx / 100.0f);
|
17
|
float y = ( float )(wp.mPosy /100.0f);
|
18
|
float z = ( float )(wp.mPosz /100.0f);
|
19
|
Debug.Log( "x = " + x + " y = " + y+ " z = " + z);
|
20
|
//同步主角的新坐标
|
21
|
mPlayer.transform.position = new Vector3 (x,y,z);
|
22
|
}
|
23
|
//清空数据包链表
|
24
|
mJFsorket.worldpackage.Clear();
|
25
|
}
|
26
|
mSynchronous = 0;
|
27
|
}
|
28
|
}
|
主角移动的同时,通过Socket时时同步坐标喔。。有没有感觉这个牛头人非常帅气 哈哈哈。
对于Socket的使用,我相信没有比MSDN更加详细的了。 有关Socket 同步请求异步请求的地方可以参照MSDN 链接地址给出来了,好好学习吧,嘿嘿。http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.aspx
上述代码中我使用的是Thread() 没有使用协同任务StartCoroutine() ,原因是协同任务必需要继承MonoBehaviour,并且该脚本要绑定在游戏对象身上。问题绑定在游戏对象身上切换场景的时候这个脚本必然会释放,那么Socket肯定会断开连接,所以我需要用Thread,并且协同任务它并不是严格意义上的多线程。
2.HTTP
HTTP请求在Unity我相信用的会更少一些,因为HTTP会比SOCKET慢很多,因为它每次请求完都会断开。废话不说了, 我用HTTP请求制作用户的登录。用HTTP请求直接使用Unity自带的www类就可以,因为HTTP请求只有登录才会有, 所以我就在脚本中来完成, 使用 www 类 和 协同任务StartCoroutine()。
01
|
using UnityEngine;
|
02
|
using System.Collections;
|
03
|
using System.Collections.Generic;
|
04
|
public class LoginGlobe : MonoBehaviour {
|
05
|
06
|
void Start ()
|
07
|
{
|
08
|
//GET请求
|
09
|
StartCoroutine(GET( "http://xuanyusong.com/" ));
|
10
|
11
|
}
|
12
|
13
|
void Update ()
|
14
|
{
|
15
|
16
|
}
|
17
|
18
|
void OnGUI()
|
19
|
{
|
20
|
21
|
}
|
22
|
23
|
//登录
|
24
|
public void LoginPressed()
|
25
|
{
|
26
|
//登录请求 POST 把参数写在字典用 通过www类来请求
|
27
|
Dictionary< string , string > dic = new Dictionary< string , string > ();
|
28
|
//参数
|
29
|
dic.Add( "action" , "0" );
|
30
|
dic.Add( "usrname" , "xys" );
|
31
|
dic.Add( "psw" , "123456" );
|
32
|
33
|
StartCoroutine(POST( "http://192.168.1.12/login.php" ,dic));
|
34
|
35
|
}
|
36
|
//注册
|
37
|
public void SingInPressed()
|
38
|
{
|
39
|
//注册请求 POST
|
40
|
Dictionary< string , string > dic = new Dictionary< string , string > ();
|
41
|
dic.Add( "action" , "1" );
|
42
|
dic.Add( "usrname" , "xys" );
|
43
|
dic.Add( "psw" , "123456" );
|
44
|
45
|
StartCoroutine(POST( "http://192.168.1.12/login.php" ,dic));
|
46
|
}
|
47
|
48
|
//POST请求
|
49
|
IEnumerator POST( string url, Dictionary< string , string > post)
|
50
|
{
|
51
|
WWWForm form = new WWWForm();
|
52
|
foreach (KeyValuePair< string , string > post_arg in post)
|
53
|
{
|
54
|
form.AddField(post_arg.Key, post_arg.Value);
|
55
|
}
|
56
|
57
|
WWW www = new WWW(url, form);
|
58
|
yield return www;
|
59
|
60
|
if (www.error != null )
|
61
|
{
|
62
|
//POST请求失败
|
63
|
Debug.Log( "error is :" + www.error);
|
64
|
65
|
} else
|
66
|
{
|
67
|
//POST请求成功
|
68
|
Debug.Log( "request ok : " + www.text);
|
69
|
}
|
70
|
}
|
71
|
72
|
//GET请求
|
73
|
IEnumerator GET( string url)
|
74
|
{
|
75
|
76
|
WWW www = new WWW (url);
|
77
|
yield return www;
|
78
|
79
|
if (www.error != null )
|
80
|
{
|
81
|
//GET请求失败
|
82
|
Debug.Log( "error is :" + www.error);
|
83
|
84
|
} else
|
85
|
{
|
86
|
//GET请求成功
|
87
|
Debug.Log( "request ok : " + www.text);
|
88
|
}
|
89
|
}
|
90
|
91
|
}
|
如果想通过HTTP传递二进制流的话 可以使用 下面的方法。
1
|
WWWForm wwwForm = new WWWForm();
|
2
|
byte [] byteStream = System.Text.Encoding.Default.GetBytes(stream);
|
3
|
wwwForm.AddBinaryData( "post" , byteStream);
|
4
|
www = new WWW(Address, wwwForm);
|
目前Socket数据包还是没有进行加密算法,后期我会补上。欢迎讨论,互相学习互相进度 加油,蛤蛤。
转载http://www.xuanyusong.com/archives/1948
unity深入研究--开发之C#使用Socket与HTTP连接服务器传输数据包相关推荐
- unity使用Socket与HTTP连接服务器传输数据包
最近比较忙,有段时间没写博客拉.最近项目中需要使用HTTP与Socket,雨松MOMO把自己这段时间学习的资料整理一下.有关Socket与HTTP的基础知识MOMO就不赘述拉,不懂得朋友自己谷歌吧.我 ...
- Unity3D研究院之C#使用Socket与HTTP连接服务器传输数据包
最近项目中需要使用HTTP与Socket,把自己这段时间学习的资料整理一下.有关Socket与HTTP的基础知识MOMO就不赘述拉,不懂得朋友自己谷歌吧.我们项目的需求是在登录的时候使用HTTP请求, ...
- C#使用Socket与HTTP连接服务器传输数据包
最近项目中需要使用HTTP与Socket,把自己这段时间学习的资料整理一下.有关Socket与HTTP的基础知识MOMO就不赘述拉,不懂得朋友自己谷歌吧.我们项目的需求是在登录的时候使用HTTP请求, ...
- (0014)iOS 开发之Mac自带的Apache本地服务器玩耍(01)
需求:项目结束,没什么事,一直对服务器的访问比较感兴趣,但是又没有正式的服务器地址测试.所以就想研究一下本地服务器的访问来模拟,正好发现Mac自带的Apache本地服务器. http://blog.c ...
- Unity3D 游戏引擎之C#使用Socket与HTTP连接server数据传输包
近期比較忙.有段时间没写博客拉.近期项目中须要使用HTTP与Socket.雨松MOMO把自己这段时间学习的资料整理一下. 有关Socket与HTTP的基础知识MOMO就不赘述拉,不懂得朋友自己谷歌吧. ...
- (0104)iOS开发之在Mac上用Charles给iPhone抓包
长时间不抓包用到时又不会了,又要百度一番.记录最近一次在Mac上用Charles给iPhone抓包的步骤. 在Mac上用Charles给iPhone抓包 Charles 从入门到精通 [抓包工具–Ch ...
- C#中开发之Socket网络编程TCP/IP层次模型、端口及报文等探讨
我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念 1.TCP/IP层次模型 当然这里我们只讨论重要的四层 01,应用层(Application):应用层是个很广泛的概念,有一些基 ...
- python 实现TCP socket通信和 HTTP服务器、服务器和客户端通信python实例
python 实现TCP socket通信和 HTTP服务器.服务器和客户端通信实例 socket是什么? 服务器和客户端通信的流程 python 实现TCP socket通信例子 关于Host和PO ...
- python运维开发之socket网络编程01
python运维开发之socket网络编程01说明:本文来自来自北京老男孩linux运维实战培训中心-运维开发课程免费视频内容,本文内容为系列内容,更多分享信息见:http://oldboy.blog ...
最新文章
- 有源汇上下界最小费用可行流 ---- P4043 [AHOI2014/JSOI2014]支线剧情(模板)
- php 过滤css样式,PHPCMS v9过滤采集内容中的CSS样式
- python编程语言是什么-python是什么编程语言
- 我的世界minecraft-Python3.9编程(4)-近实时刷新玩家位置
- 为什么服务端程序都需要先 listen 一下?
- SDOI2016R2(怎么可能是解题报告)
- Unity3D之UGUI基础3:Image图片
- 腾讯云服务器2003系统,腾讯云服务器windows2003系统续用的说明
- 凛冬已至:大厂裁员浪潮,基础福利大砍,行业饱和,大龄程序员该如何自处
- matlab解方程组解析解
- 【阅读笔记】Towards Efficient and Privacy-preserving Federated Deep Learning
- vue 实现切换主题色(低配版)
- Web 2.0时代RSS的.Net实现
- 总线干扰神器——VH6501
- C语言 输入成绩按成绩输出等级
- 水果店的问题和风险,开水果店会遇到什么问题
- Mac软件安装包和问题解决方案
- 还在埋头干活?给程序员的几个忠告
- 10017---今天下午接到阿里的电话面试
- 回顾“90后”——MISRA的25年岁月
热门文章
- PTA L1题目合集(更新至2019.3)
- MySQL安全配置规范
- B站视频任意调整倍速
- oracle删除的数据恢复,日志分析LOGMNR工具恢复的方法
- 更新源后,执行 sudo apt-get update 出现 0% [Working] 的解决方案
- android华为手机虚拟键盘盖住底部,关于PopWindow在底部展示时被华为虚拟键盘遮盖的问题...
- 互联网除了美国大厂,还有哪些不错的外企?
- Base64工具类及2点问题
- 三维RIP,曲面全彩分色prn,prt,机械臂曲面全彩打印
- python中map和星号的简单运用