如題,阻塞了 不能內部break
uj5u.com熱心網友回復:
增加接收資料超時處理邏輯即可。uj5u.com熱心網友回復:
具體代碼如何實作 請問
uj5u.com熱心網友回復:
阻塞接收無法判斷超時啊uj5u.com熱心網友回復:
俺們不知道你想表達什么。對,異步阻塞的。有資料來了就讀,沒資料來就自己阻塞。并沒有任何毛病。同時他到底要怎么個break
難道是
where(ture)
{
異步讀
///你想在這里break,他卡在上面了?
}
我們說,沒啥問題沒資料自然就卡在上面,如果有資料自然到下面。如果你想強行斷開,把tcpclient.close了,他就自然而然的例外,然后你處理例外就是
uj5u.com熱心網友回復:
TcpClient xx = new TcpClient();while (xx.Connected)
{
if (xx.Available > 0)
{
try
{
await xx.GetStream().ReadAsync( cancellationToken:); //這里其實也有一個帶canceltoken的多載,對,除了tcpclient.close 讓他自己例外,也可以用canceltoken控制是否繼續下去
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
uj5u.com熱心網友回復:
對啊 就這個意思啊 所以是反問啊uj5u.com熱心網友回復:
阻塞了不是挺好嗎,不占CPU,一個執行緒用來接收就夠了,不用弄好幾個執行緒都去接收。uj5u.com熱心網友回復:
很多無效的連接過來了,又不發資料, 時間長了 資源會耗盡的
要有一個關斷無效連接的機制
uj5u.com熱心網友回復:
無效連接,簡單處理既然你說是“很多無效的連接過來了”,說明你是服務器端。那很簡單的
不管你是用所謂的IOCP,還是用其他的手段,開監聽。acceptconnect確認連接以后就可以使用system..runtime.cache 下一個快取依賴(相對過期時間控制)
每次接收資料結束,訪問一下cache
cache失效觸發移除事件,在移除事件里把tcpclient.close掉就好了(即使用那個IOCP的,其實也一樣,你close掉,那個堵塞的自動例外,然后自動例外觸發,歸還入池)
uj5u.com熱心網友回復:
弄個服務器比較復雜,我這里簡單用tcpclient搞個demo,自己看把System.Runtime.Caching.MemoryCache cache = new MemoryCache("tcpclientCache");
private async void button1_Click(object sender, EventArgs e)
{
TcpClient tcpClient = new TcpClient();
await tcpClient.ConnectAsync(IPAddress.Parse("192.168.4.100"), 6800);
CacheItemPolicy policy = new CacheItemPolicy();
policy.SlidingExpiration = TimeSpan.FromMinutes(1);
policy.RemovedCallback = p =>
{
MessageBox.Show("快取過期,我準備移除了");
var temp = (TcpClient)p.CacheItem.Value;
temp.Client.Shutdown(SocketShutdown.Both);
temp.Client.Close();
};
cache.Add("t1", tcpClient, policy);
Task.Factory.StartNew(async () =>
{
byte[] buffer =new byte[1024];
while (tcpClient.Connected)
{
if (tcpClient.Available > 0)
{
try
{
var i = await tcpClient.Client.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
var obj = cache.Get("t1");//此處訪問一下,更新一下快取依賴時間,當然有可能null,我簡單演示就不處理了
var b = 0;
}
catch (Exception exception)
{
MessageBox.Show("例外了,此處應該處理,tcpclient斷線處理,不管是對方斷的,還是你自己斷的");
}
}
}
//此處回圈斷了,tcpclient確定沒連了,該處理的處理
});
}
uj5u.com熱心網友回復:
上面那個也解釋了,很多人糾結的心跳指令。其實你看到了,我們其實并不是說一定要收到心跳才認為他活著,心跳的目的,只是保證我可以不斷收到資料,去重繪過期時間。uj5u.com熱心網友回復:
心跳不僅僅是活著, 里面可以帶協議,獲取那些需要定時上傳的引數,同時剔除無效的連接
uj5u.com熱心網友回復:
貼一下你的代碼,我們只在這里猜了。uj5u.com熱心網友回復:
TCp server ,等待設備連接,統計流量和丟包率,[] 里面的數字是包序號,0-99, 如果不連續 , 則判定丟包
代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Data.SqlClient;
namespace tcpServer
{
public partial class Form_TcpServer : Form
{
public Form_TcpServer()
{
InitializeComponent();
txtPort.Text = "9444";
//初始化允許拖拽
InitDragInFile();
//初始化DataGridView
InitDataGridView();
timer1.Interval = 1000;
timer1.Start();
listBox1.Visible = false;
}
/// <summary>
/// 監聽套接字
/// </summary>
private Socket socketWatch = null;
//監聽執行緒句柄
private Thread threadWatch = null;
//通信執行緒套接字串列
List<Socket> listSocket = new List<Socket>();
//通信執行緒執行緒串列
List<Thread> listThread = new List<Thread>();
//IPEndPoint、幀率串列
private Dictionary<string, double> dictSps = new Dictionary<string, double>();
//IPEndpoint、丟包數串列
private Dictionary<string,int> dictLosePkts = new Dictionary<string,int>();
/// <summary>
/// 初始化監聽套接字
/// </summary>
private void InitWatchSocket()
{
try
{
//構建socket物件
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//構建ipaddress 物件
IPAddress ipaddress = IPAddress.Any;
//構建endPoint物件
IPEndPoint endPoint = new IPEndPoint(ipaddress, Convert.ToInt32(txtPort.Text));
//埠系結
socketWatch.Bind(endPoint);
//監聽
socketWatch.Listen(20);
//創建監聽執行緒
threadWatch = new Thread(watchThreadProc);
//將表單執行緒設定為與后臺同步
threadWatch.IsBackground = true;
//開啟執行緒
threadWatch.Start(socketWatch);
btnListen.Text = "監聽中..";
MessageBox.Show("開啟監聽成功");
}
catch(Exception er)
{
MessageBox.Show(er.Message);
}
}
/// <summary>
/// 等待客戶端的連接,并且創建與之通信的socket
/// </summary>
private void watchThreadProc(object obj)
{
Socket sckWatch = obj as Socket;
while (true)
{
//阻塞
Socket sock = sckWatch.Accept();//socketWatch.Accept()
//委托
ParameterizedThreadStart pts = new ParameterizedThreadStart(ThreadRecvProc);
Thread thr = new Thread(pts);
thr.IsBackground = true;
thr.Start(sock);
listSocket.Add(sock);
listThread.Add(thr);
}
}
/// <summary>
/// tcp 接收資料決議
/// </summary>
/// <param name="temp"></param>
/// <param name="state"></param>
/// <param name="No"></param>
/// <returns></returns>
private bool Parse(byte temp, ref byte state, ref int No)
{
bool ret = false;
switch(state)
{
case 0:
if (temp == '[') state = 1;
else state = 0;
break;
case 1://shi
state = 2;
No = temp - '0';
break;
case 2://ge
state = 3;
No = No * 10 + temp - '0';
break;
case 3:
if (temp == ']')
{
ret = true;
}
else
{
No = 0;
}
state = 0;
break;
default:
state = 0;
break;
}
return ret;
}
/// <summary>
/// tcp 接收執行緒
/// </summary>
/// <param name="obj"></param>
private void ThreadRecvProc(object obj)
{
Socket s = obj as Socket;
byte[] buffer = new byte[1500];
long startTime = DateTime.Now.ToUniversalTime().Ticks;
double sps = 0;
UInt32 sumRecv = 0;
int ret = 0;
string remote = s.RemoteEndPoint.ToString();
byte state = 0;
int No = 0, lastNo =-1;
int losePkts = 0;
while (true)
{
try
{
ret = s.Receive(buffer, 1500, SocketFlags.None);
for( int i = 0; i< ret; i++)
{
if( Parse(buffer[i], ref state, ref No) == true )
{
if( lastNo == -1)
{
lastNo = No;
continue;
}
else
{
if(No == 0)
{
if (lastNo != 99) losePkts++;
}
else
{
if (No - lastNo != 1) losePkts++;
}
}
lastNo = No;
dictLosePkts[remote] = losePkts;
}
}
}
catch(Exception er)
{
//MessageBox.Show(er.Message);
//錯誤提示一般是: 遠程主機關閉了一個遠程連接
Console.WriteLine( er.Message );
break;
}
long endTime = DateTime.Now.ToUniversalTime().Ticks;
sumRecv += (UInt32)ret;
if (endTime - startTime >= 10000 * 1000)//1s
{
startTime = DateTime.Now.ToUniversalTime().Ticks;
sps = sumRecv * 8.0 / 1024 / 1024;
sumRecv = 0;
dictSps[remote] = sps;
//Console.WriteLine("sps is {0}", sps);
}
//Console.WriteLine("ret is {0}", ret);
}
}
private delegate void delegate_refresh(double sps);
/// <summary>
/// 重繪listBox控制元件
/// </summary>
/// <param name="sps"></param>
private void refreshListBox(double sps)
{
if (listBox1.InvokeRequired)
{
delegate_refresh d = new delegate_refresh(refreshListBox);
this.Invoke(d, new object[] { sps });
}
else
{
listBox1.Items.Add(sps.ToString());
}
}
private void btnListen_Click(object sender, EventArgs e)
{
InitWatchSocket();
}
/// <summary>
/// 初始化允許拖拽
/// </summary>
private void InitDragInFile()
{
this.AllowDrop = true;
}
/// <summary>
/// 列印輸出拖入檔案的路徑
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] content = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (var s in content)
{
Console.WriteLine("xx:" + s);
}
}
}
/// <summary>
/// 初始化DataGridView控制元件
/// </summary>
private void InitDataGridView()
{
//設定列數為3列
dataGridView1.ColumnCount = 3;
string headers = "地址 速率(Mbps) 丟幀數(個)";
string[] header = headers.Split(' ');
//設定列標題和列寬度
for (int i = 0; i < dataGridView1.ColumnCount; i++)
{
dataGridView1.Columns[i].HeaderText = header[i];
dataGridView1.Columns[i].Width = 150;
}
//設定最后一列為自動填充
dataGridView1.Columns[dataGridView1.ColumnCount - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
//選擇模式為整行選中
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
//添加第一行
//DataGridViewRow row = new DataGridViewRow();
//row.CreateCells(dataGridView1);
//for (int i = 0; i < 3; i++)
//{
// row.Cells[i].Value = i.ToString();
//}
////添加DataGridViewRow
//dataGridView1.Rows.Add(row);
}
//定義委托型別
delegate void delegate_refreshDgv(Dictionary<string, double> dic, Dictionary<string, int> dic2);
private void refreshDataGridView(Dictionary<string, double> dic, Dictionary<string, int> dic2)
{
//在執行緒中呼叫
if(this.InvokeRequired)
{
delegate_refreshDgv d = new delegate_refreshDgv(refreshDataGridView);
this.Invoke(d, new object[] { dic, dic2 });
}
else
{
dataGridView1.Rows.Clear();
try
{
foreach (var d in dic)
uj5u.com熱心網友回復:
你貼得代碼不是一樣得, ThreadRecvProc(object obj)這個obj就是一個Socket么,那還有啥說得,自己把sokect close掉,就會自動進入你后面寫得那個catch里面
s.Shutdown(SocketShutdown.Both);
s.Close();
這樣就行了,想觸發他,可以用上面得cache,如果不想用cache,用
while (s.Connected)
{
CancellationTokenSource cts=new CancellationTokenSource(TimeSpan.FromMinutes(1));
cts.Token.Register(() =>
{
s.Shutdown(SocketShutdown.Both);
s.Close();
});
ret = s.Receive(buffer, 1500, SocketFlags.None);
}
uj5u.com熱心網友回復:
Task<bool> aTask = Task<bool>.Factory.StartNew(() =>
{
for (int i = 0; i < 3; i++)
{
CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(1));
CancellationToken token = cts.Token;
token.Register(() =>
{
if (cts != null)
{
Trace.WriteLine("執行緒a,超時取消");
}
});
Task.Delay(TimeSpan.FromSeconds(3)).Wait();
cts = null;
}
return true;
});
Task<bool> bTask = Task<bool>.Factory.StartNew(() =>
{
for (int i = 0; i < 3; i++)
{
CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
CancellationToken token = cts.Token;
token.Register(() =>
{
if (cts != null)
{
Trace.WriteLine("執行緒b,超時取消");
}
});
Task.Delay(TimeSpan.FromSeconds(1)).Wait();
cts = null;
}
return true;
});
其實超時處理有很多手段,我們只是展示一些常用得手段。那啥信號量,waitone這類我不打算寫了,一次給太多消化不良
ps:你是時候可以看看Task,async/wait 這類東西了,在往后面還不打算碰這些玩意得話,不說看nuget上得代碼,就是他介紹給得簡單演示都沒辦法看了
uj5u.com熱心網友回復:
絕對的大師
那個高級編程里面介紹 async/wait 這些的章節直接被我跳過去了。
uj5u.com熱心網友回復:
同步tcp,如果因為recv阻塞等待,把socket關閉了,就可以不阻塞,退出回圈 了。使用異步通訊
uj5u.com熱心網友回復:
肯定是單拿一個執行緒來做的吧,放進執行緒池,得到future,另起執行緒檢測uj5u.com熱心網友回復:
設定socket超時時間即可,如果超時沒有資料接收就回傳了uj5u.com熱心網友回復:
可以用cancellationTokenSource.Cancel()取消任務,但是如果不監聽cancellationToken.IsCancellationRequested屬性,或者不呼叫ThrowIfCancellationRequested方法任務一直執行下去uj5u.com熱心網友回復:
阻塞 也有退出條件,比如 超時 斷開 等, 再soketerror 有阻塞中斷的愿因uj5u.com熱心網友回復:
學習分享,高手啊都是。uj5u.com熱心網友回復:
牛皮
uj5u.com熱心網友回復:
我知道 TerminateThread 函式uj5u.com熱心網友回復:
牛逼厲害厲害厲害uj5u.com熱心網友回復:
厲害。。。。。。uj5u.com熱心網友回復:
學習分享,學習分享,uj5u.com熱心網友回復:
設定timeout時間應該可以吧uj5u.com熱心網友回復:
兄弟,你代碼肯定有問題。不管你的接受邏輯是在一個單獨執行緒還是在當前執行緒,你都不應該讓接受邏輯阻塞。應該及時的讓出cpu讓其他代碼干活兒,這樣你的代碼肯定效率會更高uj5u.com熱心網友回復:
學習分享,代碼有問題uj5u.com熱心網友回復:
我也不太懂這個uj5u.com熱心網友回復:
不是很明白。uj5u.com熱心網友回復:
增加接受資料鏈uj5u.com熱心網友回復:
好吧,我也不會寫,咋辦呢?uj5u.com熱心網友回復:
為什么阻塞呢?應用層協議沒設計好?是否出現丟包和黏包的現象?uj5u.com熱心網友回復:
/// <summary>
/// 點擊掃描,發送掃描指令
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnScan_Click(object sender, EventArgs e)
{
#region await 異步編程方式實作
//btnScan.Enabled = false;
//var task = await Task_SendScanCmd();
//Console.WriteLine("{0}", task);
//btnScan.Enabled = true;
//InitComboBleAddr();
#endregion
#region CancellationTokenSource 方式取消任務
//創建cancellationTokenSource 物件
m_ctsScanCancel = new CancellationTokenSource();
//創建task物件
Task task = new Task(() =>
{
m_ble.sendBleStartScanCmd();
Thread.Sleep(6000);
try
{
m_ctsScanCancel.Token.ThrowIfCancellationRequested();
Console.WriteLine("掃描執行緒被執行");
}
catch (Exception ex)
{
Console.WriteLine("掃描執行緒被取消" + ex.Message);
}
}, m_ctsScanCancel.Token);
task.Start();
#endregion
}
感覺cancellationTokenSource 只是在task執行完 cancel, 不是中途cancel掉的 , 能在task Sleep的時候停掉么
uj5u.com熱心網友回復:
這里不是簡單的超時就斷開連接,而是一個連接建立后,1分鐘服務器沒有收到資料集而且是合法的資料,就認為這個連接是無效的,超時的,然后服務器就主動斷掉這個連接,避免不必要的資源開銷, 我想實作的是這種效果。
uj5u.com熱心網友回復:
哎,不管你想要什么,不管你想怎么實作。都是一樣的代碼,觸發條件不同。你后面新增的需求。類比最簡單的都能聽懂的東西,asp.net 的session(很像把)
自己做個session不難,微軟給你準備好了,相對時間快取依賴。
你收到了你覺著ok的資料,訪問一下他更新他,他過期移除了給你事件,你
s.Shutdown(SocketShutdown.Both);
s.Close();
就好
快取依賴代碼自己百度。
手段很多,其實實作都一樣。在介紹另外一樣rx.net
假設我們寫個擴展方法是這樣的
s.我認為一條滿意的資料().timeout(timespan.3分鐘)
時間到了,你會得到一個例外,表示3分鐘內,你沒收到一條你認為滿意的資料,那么還是shutdown他,close他
紅字部分是rx編程的一部分,我不想展示,已經在這里展示的夠多了。你們愿意接受就接受,不愿意接受我不想強推
uj5u.com熱心網友回復:
現在的polly庫也是一樣的表達polly
{
做你想做的事情,你想接受資料就接資料,3分鐘沒接到,丟一個timeout例外出來
}
.當有個timeout例外出來,我來掛接處理他
{
s.Shutdown(SocketShutdown.Both);
s.Close();
}
還是那句話,不要糾結那些東西。你只需要做一件事情。無論我給你展示canceltoken,rx,依賴快取,還是polly
你沒發現都是一樣的,全都是 做你該做的事情,如果沒有等到你要東西,那么扔個例外或者呼叫一個事件,讓外面處理
uj5u.com熱心網友回復:
做你想做的事情正常來說,你的代碼不就這么寫么
sockect.接收資料()
決議資料
如果成功決議一條
------------------這里都不用動,下面就是才是需要動的東西
如果要依賴快取,那么這里訪問一下快取,讓快取自己更新一下時間,快取過期有過期通知,你在過期通知里關閉他
如果用rx,那么這里用onnext把決議好的資料發出去,在最初acceptconn的時候我們會有一個訂閱,當超時沒收到這個訂閱我們處理他
canceltoken一樣,每次你while接受資料前給個new一個,決議成功了就出去,沒決議到一條超時就觸發了
polly其實一樣,每次準備決議的動作當作你要完成的一次polly
---------------------------
總之我們不搞什么scan掃描這種東西,我們只自然而然的寫代碼,這東西的自然描述不就是。你連上我了,俺們3分鐘沒來電,那就撤么?就這么自然而然的寫,不必繞那個彎
uj5u.com熱心網友回復:
客戶端采用長連接的方式,server側肯定需要有超時處理的策略來兜底
否則時間長了,server側的資源就耗光了
在client的屬性中增加上一次通信的時戳屬性
采用一定的排序方法,把早未通信的client連接關閉掉
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/44664.html
標籤:C#
