本来是跟着课程学习,但是后来发现课程用搭建好的服务器进行网络访问,而且采用的是内存存储,一旦关闭,数据全部丢失,无法做到持久化存储,因此特地编写服务器。
网络传输采用异步Tcp连接,传输格式为Json,本人用的是LitJson,当然用newtonsoft也是可以的。
先说一下第一次遇到的问题,Invalid character ’,由于UTF8格式编码带有bom头,我就猜测是由于bom头影响了数据传输的准确性,使得服务器端得到的数据变长了3bytes,因此我调用了一下代码
//用于去掉bom头
public static string GetUTF8String(byte[] buffer,int n)
{
byte[] bomBuffer = new byte[] { 0xef, 0xbb, 0xbf };
if (buffer == null || buffer.Length <= 3 || (buffer[0] == bomBuffer[0]
&& buffer[1] == bomBuffer[1]
&& buffer[2] == bomBuffer[2]))
{
print("在这里边");
}
else {
print("不在");
}
if (buffer == null)
{
return null;
Debug.Log("传输得到的字符串为空");
}
if (buffer.Length <= 3)
{
print("传输得到的字符串<3");
return Encoding.UTF8.GetString(buffer);
}
if (buffer[0] == bomBuffer[0]
&& buffer[1] == bomBuffer[1]
&& buffer[2] == bomBuffer[2])
{
print("传输得到的字符串正在去bom头");
return new UTF8Encoding(false).GetString(buffer, 3, buffer.Length - 3);
}
print("我多接收到的长度为"+buffer.Length);
return Encoding.UTF8.GetString(buffer,0,n);
}
随后发现问题仍无法解决,后来经过好长时间的测试后,发现导致这一问题出现,并不是只有bom头,还有可能是越界问题。越界问题的意思就是开的数组够大,当你进行Encoiing.UTF8.getString()时,它会对整个长度的数组进行转化,导致这个问题出现,因此需要用三个参数的getstring,规定长度0到length
第二个遇到的问题就是数据存储,本人采用的是mysql,目前刚刚完成用户登录以及注册,
mysql的访问
public static MySqlConnection dbConnection;
private static string host = "localhost";
private static string id = "root";
private static string pwd = "";
private static string database = "";
private static string Connection_String = "";
// Use this for initialization
void Start () {
Connection_String = string.Format("Server ={0};Database = {1};User ID ={2}; Password ={3};",host,database,id,pwd);
openSqlConnection(Connection_String);
string p = QueryTable("ly");
Debug.Log(p);
}
// Update is called once per frame
void Update () {
}
public static void openSqlConnection(string connectionstring) {
dbConnection = new MySqlConnection(Connection_String);
dbConnection.Open();
}
public static void closeSqlConnection() {
dbConnection.Close();
dbConnection = null;
}
public static void InsertUser(string user_name,string pass_word) {
print("至少进来了");
MySqlCommand mySqlCommand = new MySqlCommand("insert into user values( '" + user_name + "','" + pass_word + "');", dbConnection);
try
{
int reasult = mySqlCommand.ExecuteNonQuery();//用于测试是否真正的插入
if (reasult > 0)
{
Debug.Log("插入成功");
}
else {
Debug.Log("插入失败");
}
}
catch
{
}
finally
{
}
}
public static string QueryTable(string userName) {
MySqlCommand mySqlCommand = new MySqlCommand("Select * from user where user_name = '" + userName + "';", dbConnection);
MySqlDataReader reader = mySqlCommand.ExecuteReader();
try
{
while (reader.Read())
{
if (reader.HasRows)
{
if (reader.GetString(0).Equals(userName))
return reader.GetString(1);
}
}
return null;
}
catch (Exception e)
{
Debug.Log(e.StackTrace);
Debug.Log("查询失败");
return null;
}
finally
{
reader.Close();
}
}
后来发现一个问题,当传入的string不正确时,也就是sql语句不合法时,不会报错,同时以后的代码也不会执行。
第三个问题就是异步连接的问题,目前我所搭建的连接还有些许问题,采用的是异步方式,用ReceiveCallBack处理服务器返回的信息,下面是客户端代码,已经全部加了注释,应该很容易看懂。
public class SocketConnect {
private static SocketConnect connect;
private static Socket socket_Tcp;
private static string ip = "127.0.0.1";
private static int port = 10100;
private static byte[] buff=new byte[1024];
private static List<SocketModel> messageList = new List<SocketModel>();
//用于获得SoketConnect
public static SocketConnect GetInit() {
if (connect == null)
{
connect = new SocketConnect();
Init_Socket();
}
return connect;
//如果该SocketConnect为空则new一个(并进行新的连接尝试),否则直接返回
}
public static void Init_Socket() {
try {
socket_Tcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//该Socket用于网络连接,采用流方式,使用TCP连接
socket_Tcp.Connect(ip,port);
Debug.Log("连接成功");
socket_Tcp.BeginReceive(buff, 0, 1024, SocketFlags.None,ReceiveCallBack, socket_Tcp);
//用于监听网络消息,BeiginReceive用于接收消息,ReceiveCallBack为回调方法当buff接受完全时进行回调
//参数一用于存储接收到的数据
//参数23 ,从零开始接受1024bytes,4不用管,值按位组合
//参数5,操作完成时回调该方法
//参数6用于将该数组传给BeiginRecive调用后返回的IAsyncResult方法
}
catch {
Debug.Log("连接失败");
}
}
//BeiginReceived的回调方法
//其中ar用于存储异步操作的状态信息以及所有定义的用户数据。
private static void ReceiveCallBack(IAsyncResult ar) {
Socket socket = ar.AsyncState as Socket;
try
{//获取消息体长度
int length = socket.EndReceive(ar);
string s = Encoding.UTF8.GetString(buff,0,length);
Debug.Log("收到消息");
Debug.Log(s);
readMessage(s);
//将读取到的数据从buff的0开始读取readCount长度到temp的从0开始的位置、此处即为复制
}
catch(Exception e) {
Debug.Log(e.Message);
Debug.Log(e.StackTrace);
SocketModel model = new SocketModel();
model.type = Protocol.abort;
string abort = codng<SocketModel>.encode(model);
byte[] buffer = Encoding.UTF8.GetBytes(abort);
socket_Tcp.Send(buffer);
socket_Tcp.Close();
Debug.Log("网络错误,连接失败");
}
socket_Tcp.BeginReceive(buff, 0, 1024, SocketFlags.None,ReceiveCallBack, socket);
//实现反复读取,反复解析
}
//用于返回SocketModel的List列表
public List<SocketModel> getlist() {
return messageList;
}
//
private static void readMessage(string s )
{
try
{
Debug.Log(s);
SocketModel model = codng<SocketModel>.decode(s);
messageList.Add(model);
}
catch (Exception e)
{
Debug.Log(e.Message);
Debug.Log(e.StackTrace);
}
}
public void sendMessage(int type, int area, int command, string message)
{
SocketModel model = new SocketModel();
model.type = type;
model.area = area;
model.command = command;
model.message = message;
String trans = codng<SocketModel>.encode(model);
byte[] buffer = Encoding.UTF8.GetBytes(trans);//测试用,下次删掉
string o =Encoding.UTF8.GetString(buffer);
Debug.Log("is " + o );
// Debug.Log(buffer.Length+ "~~~~~~~~~~~ ");//测试用,下次删掉
Debug.Log(trans);
SocketModel model1 = codng<SocketModel>.decode(trans);
Debug.Log("type:" + model1.type + " area" + model1.area + " command: " + model1.command + " message:" + model1.message);
byte[] bytes = new byte[trans.Length * sizeof(char)];
Buffer.BlockCopy(trans.ToCharArray(), 0, bytes, 0, bytes.Length);
string s = "";
for (int i = 0; i < bytes.Length; i++)
{
s += bytes[i];
}
Debug.Log(bytes.Length);
Debug.Log(s+" ~~~~~~~~~~~~");
string y = "{\"type\":0,\"area\":0,\"command\":2,\"message\":\"{\\\"userName\\\":\\\"sdsd\\\",\\\"passWord\\\":\\\"dsd\\\"}\"}";
Debug.Log(y);
SocketModel model2 = codng<SocketModel>.decode(y);
Debug.Log("type:" + model2.type + " area" + model2.area + " command: " + model2.command + " message:" + model2.message);
try
{
socket_Tcp.Send(buffer);
// socket_Tcp.Send(bytes);//测试用,下次删掉
Debug.Log("Send");
}
catch
{
Debug.Log("发送失败");
}
}
}
最后一个问题就是,服务器是多开的,就是可以有多个客户端连接,那么怎么实现每个线程发送的信息对应特定的client呢,因此就需要包装一个结构,当开启这个线程时,获得此网络传输模块,并为他封装上一个ip地址,并用一个dictionary存储所有的ip与socket,从而可以读取特定的socket。
希望对大家有帮助,靴靴