C# 使用 Socket

以一些示例,記錄 C# 中如何使用Socket:

目錄

1.第一個示例,使用.net 自帶的 Socket 純手工寫

2.第一個示例,直接使用WebSocket,推薦使用


1.第一個示例,使用.net 自帶的 Socket 純手工寫

這個方式靈活性高,但不建議使用:

這個示例的作用是:服務端可以監測到是否和客戶端連接,並且如果連上了,可以互相收發消息。如上圖,我用的本機測試的,如果是不同機器且連着網,請換成對應的局域網ip或者公網映射ip.

步驟:

1.新增個空解決方案,並添加兩個winform項目,一個是server端一個是client端:

2.在服務端和客戶端分別加入如下代碼:

服務端:

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;

namespace Server
{
    public partial class Server : Form
    {
        //心跳連接socket
        private Socket udpServerSocketHeartbeat;
        //接收聊天信息的socket
        private Socket udpServerSocketRevceiveChatInfo;
        //定時刷新接收狀態
        private System.Timers.Timer heartbeatTimerReceive = new System.Timers.Timer();
        private System.Timers.Timer receiveChatInfoTimer = new System.Timers.Timer();
        //服務端用來接收心跳包的 IP+端口
        private EndPoint receiveHeartbeat = new IPEndPoint(IPAddress.Parse("192.168.1.101"), 10000);
        //發送消息至客戶端的ip+port
        private EndPoint sendChatInfoIpPort = new IPEndPoint(IPAddress.Parse("192.168.1.101"), 10002);
        //接收聊天消息的ip+port
        private EndPoint receiveChatInfoIpPort = new IPEndPoint(IPAddress.Parse("192.168.1.101"), 10001);
        //心跳包數據
        private byte[] buffer = new byte[1024];
        //聊天信息數據
        private byte[] chatBuffer = new byte[1024];
        //心跳包字符串數據
        private string _heartbeatString = string.Empty;
        //聊天信息數據(字符串)
        private string _chatString = string.Empty;

        //最近一次接收心跳包時間點
        private DateTime lastUpdate;
        //客戶端ip+端口
        private EndPoint _clientIpPort;
        //隨便定個委託
        public delegate void myDelegate();

        public Server()
        {
            InitializeComponent();

            //心跳socket,使用Udp協議
            udpServerSocketHeartbeat = new Socket(AddressFamily.InterNetwork,SocketType.Dgram, ProtocolType.Udp);
            udpServerSocketHeartbeat.Bind(receiveHeartbeat);//本機用Bind,遠程用Connect
            udpServerSocketHeartbeat.BeginReceiveFrom(buffer, 0, 1024, SocketFlags.None, ref receiveHeartbeat, new AsyncCallback(ReceiveHeartbeatData), udpServerSocketHeartbeat);
            //開始定時顯示接收狀態
            heartbeatTimerReceive.Interval = 1000;
            heartbeatTimerReceive.AutoReset = true;
            heartbeatTimerReceive.Elapsed += new System.Timers.ElapsedEventHandler(checkTimer_Elapsed);
            heartbeatTimerReceive.Start();
            //創建接收客戶端消息的socket
            udpServerSocketRevceiveChatInfo = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            udpServerSocketRevceiveChatInfo.Bind(receiveChatInfoIpPort);//本機用Bind,遠程用Connect
            udpServerSocketRevceiveChatInfo.BeginReceiveFrom(chatBuffer, 0, 1024, SocketFlags.None, ref receiveChatInfoIpPort, new AsyncCallback(ReceiveChatInfoData), udpServerSocketRevceiveChatInfo);
            //開始定時顯示接收聊天數據
            receiveChatInfoTimer.Interval = 1000;
            receiveChatInfoTimer.AutoReset = true;
            receiveChatInfoTimer.Elapsed += new System.Timers.ElapsedEventHandler(showChatInfoTimer_Elapsed);
            receiveChatInfoTimer.Start();
        }

        private void showChatInfoTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            myDelegate md = new myDelegate(ShowChatInfo);
            this.richTextBox_chatInfo.Invoke(md);
        }

        private void ShowChatInfo()
        {
            if (!string.IsNullOrEmpty(_chatString))
            {
                this.richTextBox_chatInfo.Text = string.Format("接收到客戶端({0})的信息:\n{1}", _clientIpPort.ToString(),_chatString);
            }
        }

        private void ReceiveChatInfoData(IAsyncResult ar)
        {
            // 創建ip+port
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint tempRemoteEP = (EndPoint)sender;

            //獲取socket
            Socket remote = (Socket)ar.AsyncState;

            //獲取客戶端發來的數據(客戶端ip+port在第二個參數中)
            int recv = remote.EndReceiveFrom(ar, ref tempRemoteEP);

            //客戶端ip+端口
            _clientIpPort = tempRemoteEP;

            //將數據流轉成字符串
            _chatString = Encoding.Unicode.GetString(chatBuffer, 0, recv);

            
            if (!this.IsDisposed)
            {
                udpServerSocketRevceiveChatInfo.BeginReceiveFrom(chatBuffer, 0, 1024, SocketFlags.None, ref receiveChatInfoIpPort, new AsyncCallback(ReceiveChatInfoData), udpServerSocketRevceiveChatInfo);
            }
        }

        private void ReceiveHeartbeatData(IAsyncResult ar)
        {            
            // 創建ip+port
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint tempRemoteEP = (EndPoint)sender;

            //獲取socket
            Socket remote = (Socket)ar.AsyncState;

            //獲取客戶端發來的數據(客戶端ip+port在第二個參數中)
            int recv = remote.EndReceiveFrom(ar, ref tempRemoteEP);

            //客戶端ip+端口
            _clientIpPort = tempRemoteEP;

            //將數據流轉成字符串
            _heartbeatString = Encoding.Unicode.GetString(buffer, 0, recv);


            //最後一次接收數據時間點
            lastUpdate = DateTime.Now.ToUniversalTime();
            
            if (!this.IsDisposed)
            {
                udpServerSocketHeartbeat.BeginReceiveFrom(buffer, 0, 1024, SocketFlags.None, ref receiveHeartbeat, new AsyncCallback(ReceiveHeartbeatData), udpServerSocketHeartbeat);
            }
        }
        private void checkTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            myDelegate md = new myDelegate(ShowHeartbeatState);
            this.label_state.Invoke(md);
        }

        private void ShowHeartbeatState()
        {
            //當前時間點-最近一次接收數據包時間點
            TimeSpan timeSinceLastHeartbeat = DateTime.Now.ToUniversalTime() - lastUpdate;

            //超過3秒算超時
            if (timeSinceLastHeartbeat > TimeSpan.FromSeconds(3))
            {
                label_state.Text = "沒有監測到客戶端!";
                label_state.BackColor = Color.Red;
            }
            else
            {
                label_state.Text = "已監測到客戶端,ip+端口是:" + _clientIpPort.ToString();
                label_state.BackColor = Color.Green;
            }
        }

        private void button_send_Click(object sender, EventArgs e)
        {
            SendUdpChatPacket();
        }

        private void SendUdpChatPacket()
        {
            byte[] data = new byte[1024];

            //創建udp socket
            Socket udpClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            //字符串轉byte[] 準備發送
            data = Encoding.Unicode.GetBytes(this.textBox_sendInfo.Text);

            //發
            udpClientSocket.SendTo(data, 0, data.Length, SocketFlags.None, sendChatInfoIpPort);
        }

    }
}

 

客戶端:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;

namespace Client
{
    public partial class Client : Form
    {
        //定時器 定時發送心跳包
        private System.Timers.Timer heartbeatTimer = new System.Timers.Timer();
        //心跳包服務器接收 地址+端口
        private IPEndPoint sendHeartbeatIpPort = new IPEndPoint(IPAddress.Parse("192.168.1.101"), 10000);        
        //客戶端用來接收聊天數據的socket
        private Socket udpServerSocketReceiveChat;
        //客戶端用來接收聊天信息的ip+port
        private EndPoint receiveChatInfoIpPort = new IPEndPoint(IPAddress.Parse("192.168.1.101"), 10002);
        //保存聊天信息
        private byte[] bufferChat = new byte[1024];
        //保存服務器地址
        private EndPoint _serverIpPort;
        //聊天信息(字符串)
        private string _chatString = string.Empty;
        //定時器 刷新聊天信息
        private System.Timers.Timer receiveChatInfoTimer = new System.Timers.Timer();
        //發送至服務器的ip+port
        private EndPoint sendChatInfoIpPort = new IPEndPoint(IPAddress.Parse("192.168.1.101"), 10001);
        //隨便定個委託
        public delegate void myDelegate();

        public Client()
        {
            InitializeComponent();

            //開始定時發送心跳包
            heartbeatTimer.Interval = 1000;
            heartbeatTimer.AutoReset = true;
            heartbeatTimer.Elapsed += new System.Timers.ElapsedEventHandler(heartbeatTimer_Elapsed);
            heartbeatTimer.Start();

            //獲取服務端聊天信息
            udpServerSocketReceiveChat = new Socket(AddressFamily.InterNetwork,SocketType.Dgram, ProtocolType.Udp);
            udpServerSocketReceiveChat.Bind(receiveChatInfoIpPort);
            udpServerSocketReceiveChat.BeginReceiveFrom(bufferChat, 0, 1024, SocketFlags.None, ref receiveChatInfoIpPort, new AsyncCallback(ReceiveChatData), udpServerSocketReceiveChat);

            //開始定時刷新接收到的服務端信息
            receiveChatInfoTimer.Interval = 1000;
            receiveChatInfoTimer.AutoReset = true;
            receiveChatInfoTimer.Elapsed += new System.Timers.ElapsedEventHandler(chartInfoTimer_Elapsed);
            receiveChatInfoTimer.Start();
        }

        private void chartInfoTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            myDelegate md = new myDelegate(ShowChatInfo);
            this.richTextBox_chatInfo.Invoke(md);
        }

        private void ShowChatInfo()
        {
            if (!string.IsNullOrEmpty(_chatString))
            {
                this.richTextBox_chatInfo.Text = string.Format("接收到服務端({0})的信息:\n{1}", _serverIpPort.ToString(), _chatString);
            }
        }

        private void ReceiveChatData(IAsyncResult ar)
        {
            // 創建ip+port
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint tempRemoteEP = (EndPoint)sender;

            //獲取socket
            Socket remote = (Socket)ar.AsyncState;

            //獲取客戶端發來的數據(客戶端ip+port在第二個參數中)
            int recv = remote.EndReceiveFrom(ar, ref tempRemoteEP);

            //客戶端ip+端口
            _serverIpPort = tempRemoteEP;

            //將數據流轉成字符串
            _chatString = Encoding.Unicode.GetString(bufferChat, 0, recv);
            
            if (!this.IsDisposed)
            {
                udpServerSocketReceiveChat.BeginReceiveFrom(bufferChat, 0, 1024, SocketFlags.None, ref receiveChatInfoIpPort, new AsyncCallback(ReceiveChatData), udpServerSocketReceiveChat);
            }
        }

        private void heartbeatTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            SendUdpPacket();
        }

        private void SendUdpPacket()
        {
            byte[] data = new byte[1024];

            //創建udp socket
            Socket udpClientSocketHeartbeat = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            //字符串轉成byte[] 準備發送
            data = Encoding.ASCII.GetBytes(this.Text);

            //發送至服務端
            udpClientSocketHeartbeat.SendTo(data, 0, data.Length, SocketFlags.None, sendHeartbeatIpPort);
        }

        private void button_send_Click(object sender, EventArgs e)
        {
            SendUdpChatPacket();
        }

        private void SendUdpChatPacket()
        {
            byte[] data = new byte[1024];

            //創建udp socket
            Socket udpClientSocketHeartbeat = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            //字符串轉成byte[] 準備發送
            data = Encoding.Unicode.GetBytes(this.textBox_sendInfo.Text);

            //發送至服務端
            udpClientSocketHeartbeat.SendTo(data, 0, data.Length, SocketFlags.None, sendChatInfoIpPort);
        }


    }
}

剩下的界面控件,直接去工具箱拖出來改成和代碼對應的name即可,或者直接到這裏下載代碼

 

2.第一個示例,直接使用WebSocket,推薦使用

使用WebSocket方法有很多種,這裏記錄幾種:

爲了簡便示例,服務端就使用 Fleck 

開源地址在 https://github.com/statianzo/Fleck

服務端代碼:

static void Main(string[] args)
{
	FleckLog.Level = LogLevel.Debug;
	var allSockets = new List<IWebSocketConnection>();
	var server = new WebSocketServer("ws://0.0.0.0:8181");
	server.Start(socket =>
	{
		socket.OnOpen = () =>
		{
			Console.WriteLine("Open!");
			allSockets.Add(socket);
		};
		socket.OnClose = () =>
		{
			Console.WriteLine("Close!");
			allSockets.Remove(socket);
		};
		socket.OnMessage = message =>
		{
			Console.WriteLine(message);
			allSockets.ToList().ForEach(s => s.Send("Echo: " + message));
		};
	});

	var input = Console.ReadLine();
	while (input != "exit")
	{
		foreach (var socket in allSockets.ToList())
		{
			socket.Send(input);
		}
		input = Console.ReadLine();
	}
}

 

客戶端就有很多種了:

比如直接用Chrome console終端:

 

或者直接前端javascript

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>websocket client</title>
    <script type="text/javascript">
        var start = function () {
            var inc = document.getElementById('incomming');
            var wsImpl = window.WebSocket || window.MozWebSocket;
            var form = document.getElementById('sendForm');
            var input = document.getElementById('sendText');
            
            inc.innerHTML += "connecting to server ..<br/>";

            // create a new websocket and connect
            window.ws = new wsImpl('ws://localhost:8181/');

            // when data is comming from the server, this metod is called
            ws.onmessage = function (evt) {
                inc.innerHTML += evt.data + '<br/>';
            };

            // when the connection is established, this method is called
            ws.onopen = function () {
                inc.innerHTML += '.. connection open<br/>';
            };

            // when the connection is closed, this method is called
            ws.onclose = function () {
                inc.innerHTML += '.. connection closed<br/>';
            }
            
			form.addEventListener('submit', function(e){
				e.preventDefault();
				var val = input.value;
				ws.send(val);
				input.value = "";
			});
            
        }
        window.onload = start;
    </script>
</head>
<body>
	<form id="sendForm">
		<input id="sendText" placeholder="Text to send" />
	</form>
    <pre id="incomming"></pre>
</body>
</html>

再或者使用微軟自帶的 ClientWebSocket

public partial class MainWindow : Window
{
	private ClientWebSocket _webSocket;
	private CancellationToken _cancellation;
	public MainWindow()
	{
		InitializeComponent();
		Init();
	}

	private async Task<int> Init()
	{
		_webSocket = new ClientWebSocket();
		_cancellation = new CancellationToken();
		await _webSocket.ConnectAsync(new Uri("ws://127.0.0.1:8181/"), _cancellation);
		return 1;
	}

	private async void Button_Click(object sender, RoutedEventArgs e)
	{
		var send = System.Text.Encoding.UTF8.GetBytes("Hello,女神在嗎?");
		await _webSocket.SendAsync(new ArraySegment<byte>(send), WebSocketMessageType.Text, true, _cancellation);
   
		byte[] rec = new byte[1024];
		await _webSocket.ReceiveAsync(new ArraySegment<byte>(rec), _cancellation);
		string str = System.Text.Encoding.UTF8.GetString(rec);
		MessageBox.Show(str);
	}
}

個人覺推薦還是用下面的 WebSocket4Net,簡單好用

 

private static WebSocket _websocketClient;
static void Main(string[] args)
{
	try
	{
		_websocketClient = new WebSocket("ws://192.168.1.173:8181/");
		_websocketClient.Opened += Websocket_Opened;
		_websocketClient.Closed += WebsocketClient_Closed;
		_websocketClient.Error += WebsocketClient_Error;
		_websocketClient.Open();

		Console.Read();
	}
	catch (Exception e)
	{
		Console.WriteLine(e);		
	}
}

private static void Websocket_Opened(object sender, EventArgs e)
{
	Console.WriteLine("Opened");
	_websocketClient.Send("Hello world!");
}

private static void WebsocketClient_Closed(object sender, EventArgs e)
{
	Console.WriteLine("Closed");
}

private static void WebsocketClient_Error(object sender, SuperSocket.ClientEngine.ErrorEventArgs e)
{
	Console.WriteLine($"Error: {e.Exception.Message}");
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章