Socket网络通信(二)
之前写过一篇socket的文章,大概也了解了一下相关的概念,今天通过一个测试做个总结吧!Scoket呢,一般就是用来通信用的,客户端和服务器收发数据,不同模块之间用来收发数据,一般来说呢有一个服务器,可以有多个客户端。
下图展示的就是一套基本的Socket通信流程。
Socekt通信的基本流程具体步骤如下:
1)开启一个连接之前,需要完成Socket和Blind两个步骤。Socket是新建一个套接字,Blind指定套接字的IP和端口(客户端在调用Connect时会由系统分配端口,因此可以省去Blind).
2)服务器通过Listen开启监听等待客户端接入。
3)客户端通过Connect连接服务器,服务端通过Accept接受客户端连接。在connect-accept过程中,操作系统将会进行三次握手。
4)客户端和服务端通过Send和Recieve发送和接受数据,操作系统将会完成TCP数据的确认、重发等步骤。
5)通过close关闭连接,操作系统会进行四次挥手。
下面是我写的一个简单的客户端和服务端连接的demo,发送了一条数据,并做了测试。
服务器端代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ServerTest
{
class Program
{
static Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static byte[] Buffer = new byte[4];
static void Main(string[] args)
{
//绑定端口号和IP地址
IPAddress address = IPAddress.Parse("127.0.0.1");
IPEndPoint point = new IPEndPoint(address, 8080);
socket.Bind(point);
//设置最大同时连接数
socket.Listen(10);
//等待客户端连入
Socket client = socket.Accept();
Console.WriteLine("客户端连入");
//接收数据
int length = client.Receive(Buffer);
int newLevel = BitConverter.ToInt32(Buffer, 0);
Console.WriteLine(newLevel);
Console.ReadKey();
}
}
}
客户端代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
namespace ClientTest
{
class Program
{
static Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static void Main(string[] args)
{
client.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
//发送消息
int level = 257;
byte[] levelBytes = BitConverter.GetBytes(level);
client.Send(levelBytes);
//client.Receive
Console.ReadKey();
}
}
}
上述代码表示我开启了服务端,然后打开客户端,客户端连接到服务端,我在客户端给服务端发送了一个等级的消息257,灾服务端应该接收到一个等级为257的消息。
测试结果如下图 :
知识点普及:
1. 客户端socket向服务端发送数据,那么是谁负责接受数据呢?是服务端socket吗?当然不是,如果你这么想就错了,而是服务端socket在等待客户端socket连接之后,会返回与之对应的一个socket,代码如下
//等待客户端连入
Socket client = socket.Accept();
然后让这个socket去接受收到的消息。当然考虑到接受的消息先存放在哪,所以我们事先得定义一个Byte数组。
byte[] Buffer = new byte[4];
//接收数据
int length = client.Receive(Buffer);
2. 客户端socket与服务端socket收发消息,通常是采用二进制流来传输的,那么在发消息的时候呢,我们发的是二进制流格式,但是我们通常用的数据都是int,float,string ,对象等类型的,所以我们可以先事先把这些类型经过序列化来转成可传输流的格式类型。我们以前浏览网页可能都遇见过乱码的现象,这种现象呢,就是它的编码格式不正确,所以一般呢,遇见这种情况,我们会把网页编码改成UTF-8格式,因为呢,UTF-8是字符串格式,就像早期的电报,你有一套自己的编码,你给友方发送了情报,结果被敌方拦截住了,但是它们并不知道你的这一连串数字表示什么意思,如果友方接收到了,相反,大家共用的是一套编码,所以可以通过反编译,得知你的消息。与序列化相对的呢?是反序列化。
通过代码我列举了不同的几种数据类型的序列化与反序列化过程。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
namespace Serialize
{
class Program
{
static void Main(string[] args)
{
//1.通过c#的bitconvertor类进行序列化和反序列化
//int ,float,double,bool等与int型序列化过程类似,我就只写int型的序列化与反序列过程。
//序列化
int level = 257;
byte[] levelBytes = BitConverter.GetBytes(level);
Console.WriteLine("257序列化的结果:");
for (int i = 0; i < levelBytes.Length; i++)
{
Console.Write(levelBytes[i]);
}
Console.WriteLine();
//反序列化
byte[] data = new byte[] { 1, 1, 0, 0 };
int newLevel = BitConverter.ToInt32(data, 0);
Console.WriteLine("data数组反序列化的结果:");
Console.WriteLine(newLevel);
Console.WriteLine();
//2.字符串string类型的序列化与反序列化过程
//序列化
string account = "janus";
byte[] accBytes = Encoding.UTF8.GetBytes(account);
Console.WriteLine("janus序列化的结果:");
for (int i = 0; i < accBytes.Length; i++)
{
Console.Write(accBytes[i]);
}
Console.WriteLine();
//反序列化
string newAcc = Encoding.UTF8.GetString(accBytes);
Console.WriteLine("accBytes数组反序列化的结果:");
Console.WriteLine(newAcc);
Console.WriteLine();
//3.通过内存流memoryStream 、二进制格式 BinaryFormatter来进行对象类型的序列化与反系列过程
//序列化
PlayerInfo info = new PlayerInfo();
info.ID = 1001;
info.name= "janus";
byte[] infoBytes;
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter fb = new BinaryFormatter();
fb.Serialize(stream, info);
infoBytes = stream.ToArray();
}
Console.WriteLine("info对象序列化的结果:");
for (int i = 0; i < infoBytes.Length; i++)
{
Console.Write(infoBytes[i]);
}
Console.WriteLine();
//反序列化
Console.WriteLine("infoBytes数组反序列化的结果:");
using (MemoryStream stream1 = new MemoryStream(infoBytes))
{
BinaryFormatter fb1 = new BinaryFormatter();
PlayerInfo newInfo = (PlayerInfo)fb1.Deserialize(stream1);
Console.WriteLine("newInfo.ID:"+newInfo.ID + "-----" + "newInfo.ID"+newInfo.name);
}
Console.ReadKey();
}
}
[Serializable]
class PlayerInfo
{
public int ID;
public string name;
}
}
上述代码的输出结果为:
总结:一般呢,在网络游戏框架设计上,只有服务器有操作数据库的权限,客户端呢,有数据更改或获取都得去把相应的消息发送给服务器,然后服务器去处理相关信息。比如服务器获取并同步客户端玩家位置,服务器框架等问题,等待后续...