以一些示例,記錄 C# 中如何使用Socket:
目錄
1.第一個示例,使用.net 自帶的 Socket 純手工寫
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}");
}