Unity C# 网络学习(二)
一.Socket的重要API
public class Lesson05 : MonoBehaviour
{
private void Start ( )
{
var tcpSocket = new Socket ( AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. Tcp) ;
var udpSocket = new Socket ( AddressFamily. InterNetwork, SocketType. Dgram, ProtocolType. Udp) ;
Debug. Log ( tcpSocket. Connected) ;
Debug. Log ( tcpSocket. SocketType) ;
Debug. Log ( tcpSocket. ProtocolType) ;
Debug. Log ( tcpSocket. AddressFamily) ;
Debug. Log ( tcpSocket. Available) ;
Debug. Log ( tcpSocket. LocalEndPoint) ;
Debug. Log ( tcpSocket. RemoteEndPoint) ;
tcpSocket. Bind ( new IPEndPoint ( IPAddress. Parse ( "127.0.0.1" ) , 8080 ) ) ;
tcpSocket. Listen ( 10 ) ;
tcpSocket. Accept ( ) ;
tcpSocket. Connect ( new IPEndPoint ( IPAddress. Parse ( "127.0.0.1" ) , 8080 ) ) ;
tcpSocket. Send ( new byte [ 10 ] ) ;
tcpSocket. Shutdown ( SocketShutdown. Both) ;
tcpSocket. Close ( ) ;
}
}
二.实现一个最简单的服务端
namespace TcpServer
{
class Program
{
static void Main ( string [ ] args)
{
var serverSocket = new Socket ( AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. Tcp) ;
try
{
serverSocket. Bind ( new IPEndPoint ( IPAddress. Parse ( "127.0.0.1" ) , 8080 ) ) ;
}
catch ( Exception e)
{
Console. WriteLine ( "绑定失败!" + e. Message) ;
return ;
}
serverSocket. Listen ( 10 ) ;
Console. WriteLine ( "等待玩家接入!" ) ;
var clientSocket = serverSocket. Accept ( ) ;
Console. WriteLine ( "有客户端接入了!!!" ) ;
clientSocket. Send ( Encoding. UTF8. GetBytes ( "欢迎接入服务器!" ) ) ;
var buffer = new byte [ 1024 ] ;
var num = clientSocket. Receive ( buffer) ;
Console. WriteLine ( $"接收到了 { 0 } 的消息: { 1 } " , clientSocket. RemoteEndPoint, Encoding. UTF8. GetString ( buffer, 0 , num) ) ;
clientSocket. Shutdown ( SocketShutdown. Both) ;
clientSocket. Close ( ) ;
Console. WriteLine ( "按任意键退出" ) ;
Console. ReadKey ( ) ;
}
}
}
三.实现一个最简单的客户端
public class Lesson06 : MonoBehaviour
{
private void Start ( )
{
var socket = new Socket ( AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. Tcp) ;
try
{
socket. Connect ( new IPEndPoint ( IPAddress. Parse ( "127.0.0.1" ) , 8080 ) ) ;
}
catch ( Exception e)
{
Debug. Log ( e) ;
return ;
}
var buffer = new byte [ 1024 ] ;
var num = socket. Receive ( buffer) ;
Debug. Log ( "收到服务器的消息:" + Encoding. UTF8. GetString ( buffer, 0 , num) ) ;
socket. Send ( Encoding. UTF8. GetBytes ( "你好,我是客户端" ) ) ;
socket. Shutdown ( SocketShutdown. Both) ;
socket. Close ( ) ;
}
}
四.优化服务端
由于之前的服务端只能够接收一次客户端发来的消息,并且只能发送一次消息,这样并不能在实际开发中使用,所以要为服务端进行优化 用多线程处理客户端连接,接收消息,处理消息
namespace TcpServerPlus
{
class Program
{
private static Socket _serverSocket;
private static List< Socket> _allClientSocketList = new List< Socket> ( ) ;
static void Main ( string [ ] args)
{
_serverSocket = new Socket ( AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. Tcp) ;
try
{
_serverSocket. Bind ( new IPEndPoint ( IPAddress. Parse ( "127.0.0.1" ) , 8080 ) ) ;
}
catch ( Exception e)
{
Console. WriteLine ( e) ;
return ;
}
_serverSocket. Listen ( 10 ) ;
Thread acceptThread = new Thread ( AcceptClientConnect) ;
acceptThread. Start ( ) ;
Thread reciveThread = new Thread ( ReciveClientMsg) ;
reciveThread. Start ( ) ;
while ( true )
{
var inputStr = Console. ReadLine ( ) ;
if ( inputStr == "Quit" )
{
for ( int i = 0 ; i < _allClientSocketList. Count; i++ )
{
_allClientSocketList[ i] . Shutdown ( SocketShutdown. Both) ;
_allClientSocketList[ i] . Close ( ) ;
}
break ;
}
else if ( inputStr. StartsWith ( "S:" , StringComparison. OrdinalIgnoreCase) )
{
for ( int i = 0 ; i < _allClientSocketList. Count; i++ )
{
_allClientSocketList[ i] . Send ( Encoding. UTF8. GetBytes ( inputStr. Substring ( 2 ) ) ) ;
}
}
}
}
static void AcceptClientConnect ( )
{
while ( true )
{
var clientSocket = _serverSocket. Accept ( ) ;
_allClientSocketList. Add ( clientSocket) ;
clientSocket. Send ( Encoding. UTF8. GetBytes ( "欢迎连接服务器!" ) ) ;
}
}
static void ReciveClientMsg ( )
{
Socket clientSocket;
byte [ ] buffer = new byte [ 1024 * 1024 ] ;
int num = 0 ;
while ( true )
{
for ( int i = 0 ; i < _allClientSocketList. Count; i++ )
{
clientSocket = _allClientSocketList[ i] ;
if ( clientSocket. Available > 0 )
{
num = clientSocket. Receive ( buffer) ;
ThreadPool. QueueUserWorkItem ( HandleMsg, ( clientSocket, Encoding. UTF8. GetString ( buffer, 0 , num) ) ) ;
}
}
}
}
static void HandleMsg ( object state)
{
( Socket s, string str) info = ( ( Socket s, string str) ) state;
Console. WriteLine ( $"收到客户端 { info. s. RemoteEndPoint } 发来的消息: { info. str } " ) ;
}
}
}
五.继续优化服务器
虽然上面的代码以及可以实现功能了,但是不够面向对象,可以进一步优化
namespace TcpServerPlusPlus
{
class ClientSocket
{
public int id;
public static int BEGIN_ID = 1 ;
public Socket socket;
public ClientSocket ( Socket socket)
{
this . socket = socket;
id = BEGIN_ID++ ;
}
public void SendMsg ( string str)
{
try
{
socket. Send ( Encoding. UTF8. GetBytes ( str) ) ;
}
catch ( Exception e)
{
Console. WriteLine ( e) ;
return ;
}
}
public void ReceiveMsg ( )
{
if ( socket == null )
return ;
try
{
if ( socket. Available > 0 )
{
var buffer = new byte [ 1024 * 5 ] ;
var num = socket. Receive ( buffer) ;
ThreadPool. QueueUserWorkItem ( MsgHandle, Encoding. UTF8. GetString ( buffer, 0 , num) ) ;
}
}
catch ( Exception e)
{
Console. WriteLine ( e) ;
throw ;
}
}
public void Close ( )
{
socket. Shutdown ( SocketShutdown. Both) ;
socket. Close ( ) ;
socket = null ;
}
private void MsgHandle ( object state)
{
var info = state as string ;
Console. WriteLine ( $"收到客户端 { socket. RemoteEndPoint } 发来的消息: { info } " ) ;
}
}
}
namespace TcpServerPlusPlus
{
class ServerSocket
{
public Socket socket;
private Dictionary< int , ClientSocket> _allID2ClientDic = new Dictionary< int , ClientSocket> ( ) ;
public void Connent ( string ip, int host, int maxNum)
{
if ( socket != null )
return ;
socket = new Socket ( AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. Tcp) ;
try
{
socket. Bind ( new IPEndPoint ( IPAddress. Parse ( ip) , host) ) ;
socket. Listen ( maxNum) ;
ThreadPool. QueueUserWorkItem ( Accept) ;
ThreadPool. QueueUserWorkItem ( Receive) ;
}
catch ( Exception e)
{
Console. WriteLine ( e) ;
throw ;
}
}
public void Broadcast ( string info)
{
foreach ( var clientSocket in _allID2ClientDic. Values)
clientSocket. SendMsg ( info) ;
}
public void Close ( )
{
foreach ( var clientSocket in _allID2ClientDic. Values)
clientSocket. Close ( ) ;
socket. Shutdown ( SocketShutdown. Both) ;
socket. Close ( ) ;
socket = null ;
}
private void Receive ( object state)
{
while ( true )
{
try
{
if ( _allID2ClientDic. Count > 0 )
{
foreach ( var clientSocket in _allID2ClientDic. Values)
clientSocket. ReceiveMsg ( ) ;
}
}
catch ( Exception e)
{
Console. WriteLine ( e) ;
return ;
}
}
}
private void Accept ( object state)
{
while ( true )
{
try
{
var clientSocket = socket. Accept ( ) ;
var newClientSocket = new ClientSocket ( clientSocket) ;
_allID2ClientDic. Add ( newClientSocket. id, newClientSocket) ;
newClientSocket. SendMsg ( "连接成功!" ) ;
}
catch ( Exception e)
{
Console. WriteLine ( e) ;
throw ;
}
}
}
}
}
namespace TcpServerPlusPlus
{
class Program
{
static void Main ( string [ ] args)
{
var serverSocket = new ServerSocket ( ) ;
serverSocket. Connent ( "127.0.0.1" , 8080 , 10 ) ;
Console. WriteLine ( "服务器开启成功!!!" ) ;
while ( true )
{
var inputStr = Console. ReadLine ( ) ;
if ( inputStr == "Quit" )
{
serverSocket. Close ( ) ;
break ;
}
else if ( inputStr. StartsWith ( "S:" , StringComparison. OrdinalIgnoreCase) )
{
serverSocket. Broadcast ( inputStr. Substring ( 2 ) ) ;
}
}
}
}
}
六.编写客户端
public class NetMgr : MonoBehaviour
{
private static NetMgr _instance;
public static NetMgr Instance => _instance;
private Socket _socket;
private readonly Queue< string > _sendMsgQueue = new Queue< string > ( ) ;
private static readonly object SendMsgQueueLock = new object ( ) ;
private readonly Queue< string > _receiveMsgQueue = new Queue< string > ( ) ;
private static readonly object ReceiveMsgQueueLock = new object ( ) ;
private bool _isClose = false ;
private readonly byte [ ] _receiveMsgBuffer = new byte [ 1024 * 1024 ] ;
private int _receiveMsgLength = 0 ;
private const int UpdateCheckCount = 5 ;
private int _currUpdateCheckCount = 0 ;
private void Awake ( )
{
_instance = this ;
}
private void Update ( )
{
while ( _currUpdateCheckCount< UpdateCheckCount)
{
_currUpdateCheckCount++ ;
lock ( ReceiveMsgQueueLock)
{
if ( _receiveMsgQueue. Count<= 0 )
{
_currUpdateCheckCount = 0 ;
break ;
}
string msg = _receiveMsgQueue. Dequeue ( ) ;
Debug. Log ( msg) ;
}
}
}
public void Connect ( string ip, int port)
{
if ( _socket is { Connected: true } )
return ;
_isClose = false ;
_socket = new Socket ( AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. Tcp) ;
try
{
_socket. Connect ( new IPEndPoint ( IPAddress. Parse ( ip) , port) ) ;
ThreadPool. QueueUserWorkItem ( CheckSendMsgQueue) ;
ThreadPool. QueueUserWorkItem ( CheckReceiveMsgQueue) ;
}
catch ( Exception e)
{
Debug. Log ( e) ;
}
}
public void SendMsg ( string msg)
{
lock ( SendMsgQueueLock)
{
_sendMsgQueue. Enqueue ( msg) ;
}
}
private void CheckSendMsgQueue ( object obj)
{
while ( ! _isClose)
{
try
{
lock ( SendMsgQueueLock)
{
if ( _sendMsgQueue. Count> 0 )
{
_socket. Send ( Encoding. UTF8. GetBytes ( _sendMsgQueue. Dequeue ( ) ) ) ;
}
}
}
catch ( Exception e)
{
Debug. Log ( e) ;
return ;
}
}
}
private void CheckReceiveMsgQueue ( object obj)
{
while ( ! _isClose)
{
try
{
if ( _socket. Available > 0 )
{
_receiveMsgLength = _socket. Receive ( _receiveMsgBuffer) ;
lock ( ReceiveMsgQueueLock)
{
_receiveMsgQueue. Enqueue ( Encoding. UTF8. GetString ( _receiveMsgBuffer, 0 , _receiveMsgLength) ) ;
}
}
}
catch ( Exception e)
{
Debug. Log ( e) ;
return ;
}
}
}
public void Close ( )
{
_isClose = true ;
if ( _socket!= null )
{
_socket. Shutdown ( SocketShutdown. Both) ;
_socket. Close ( ) ;
}
_socket = null ;
}
private void OnDestroy ( )
{
Close ( ) ;
}
}