Помогите с TCP сервером(постоянный коннект)
Код кнопки для включения сервера:
{
Thread t = new Thread(new ThreadStart(ThStart));
t.Start();
}
Код самого сервера:
{
while (true)
{
TcpListener TList = new TcpListener(80);
TList.Start();
TcpClient TClient = TList.AcceptTcpClient();
NetworkStream ns = TClient.GetStream();
StreamReader srrr = new StreamReader(ns);
StreamWriter swww = new StreamWriter(ns);
swww.WriteLine("ПРЕВЕД");
swww.Flush();
swww.Close();
srrr.Close();
TClient.Close();
TList.Stop();
}
}
public void button1_Click(object sender, EventArgs e)
{
listener = new TcpListener(80);
listener.Start();
listener.BeginAcceptTcpClient(new AsyncCallback(ClientReceivedCallback), null);
}
private void ClientReceivedCallback(IAsyncResult asyncResult)
{
try
{
TcpClient client = listener.EndAcceptTcpClient(asyncResult);
listener.BeginAcceptTcpClient(new AsyncCallback(ClientReceivedCallback), null);
NetworkStream stream = client.GetStream();
//------твой чих-пых код, делающий непонятно что :)
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Ответ уже был дан:
Смысл асинхронного вызова, то что он выполняется в отдельном потоке.
Чем не устраивает модель ремоутинга? Тогда вообще не нужно работать с TcpClient'ами и подключение клиентов идёт полностью прозрачно.
Ты создаёшь объект, наслебуя его от MarshalByRefObject, определяя для него интерфейс I.
Далее "публикуешь" его используя RemotingConfiguration.RegisterWellKnownServiceType, предварительно создав Tcp канал TcpServerChannel. Клиенты будут соединяться автоматически через TcpClientChannel и будут работать с сервером через интерфейс I, который они получат через активатор Activator. Таким образом ты общаешься с сервером в терминах "вызов/возврат" а маршаллингом/демаршаллингом параметров и результатов занимается сам .NET.
Ключевые слова поиска в МСДН: TcpServerChannel, TcpClientChannel (там есть пример реализации, он занимает 10-20 строк)
Также нужно, чтобы при определенном запросе клиента(посылки определенной строки), сервер ставил его у себя в очередь, а по прошествии определенного времени, отвечал(т.о. нужно клиенты идентифицировать)
Вот простейший вариант с использованием пула тредов.
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
public const int port = 8081;
public static void ClientServerDialog(object _client)
{
Console.WriteLine("client accepted");
TcpClient client = (TcpClient) _client;
try {
Stream stream = client.GetStream( );
// do in/out operations
} finally {
client.Close( );
Console.WriteLine("client leaved");
}
}
static void Main(string[] args)
{
TcpListener listener = new TcpListener(port);
listener.Start( );
Console.WriteLine("starting listening to port {0}", port);
try {
while (true) {
TcpClient client = listener.AcceptTcpClient( );
// fork new thread from process thread pool for new client
ThreadPool.QueueUserWorkItem(ClientServerDialog, client);
}
} finally {
listener.Stop( );
Console.WriteLine("listening terminated");
}
}
}
}
в пуле кажется 24 рабочие нитки, т.о. именно этим числом ограничено максимальное количество обрабатываемых в единый момент времени клиентов.
учти, что System.Runtime.Serialization тебе не удасться использовать для диалога с клиентом на Java, потому тебе придётся передвать unicode-строки в собственном формате (например передавать в сеть сначала длину строки, потом поток байтов).
А как потом идентифицировать клиентов, если допустим они отправляют запросы с разных ip или в запросах разные строки(ну т.е. логин например отсылают) ?
Просто мне нужно определенному клиенту отвечать определенным запросом.
Просто мне нужно определенному клиенту отвечать определенным запросом.
Можно считать, что на каждого клиента создаётся отдельный поток в лице метода ClientServerDialog. В контексте потока тебе доступен объект client типа TcpClient.
Вот пример дальнейшего развития представленного ранее кода.
using System.ComponentModel;
<...>
public static void ClientServerDialog(object _client)
{
using(MyClient newClient = new MyClient((TcpClient) _client) {
newClient.Execute();
}
}
public class MyClient: IDisposable
{
public static List<MyClient> Clients = new List<MyClient>( );
public static int BufferSize = 4096;
private TcpClient client;
public TcpClient Client { get { return client; } }
public MyClient(TcpClient client)
{
this.client = client;
lock (Clients) {
Clients.Add(this);
}
Console.WriteLine("client accepted");
}
~MyClient( )
{
lock (Clients) {
Clients.Remove(this);
}
Dispose(false);
Console.WriteLine("client leaved");
}
public void Execute( )
{
Stream stream = new BufferedStream(Client.GetStream( ), BufferSize);
// in/out operations;
}
// Track whether Dispose has been called.
private bool disposed = false;
// Implement IDisposable.
void IDisposable.Dispose( )
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed) {
client.Close( );
}
disposed = true;
}
}
Здесь в каждом потоке создаётся новый объект MyClient, инкапсулирующий TcpClient-клиента.
MyClient.Clients - это список всех текущих экземпляров MyClient.
в методе Execute() предполагается выполненние всех операций ввода-вывода с клиентом.
замечание:
конструкция
using(ObjectType obj = new ObjectType()) {
}
предполагает реализацию у ObjectType интерфейса IDisposable, потому как после } автоматически вызывается obj.Dispose(), который должен возвращать ресурсы, занятые объектом obj - в нашем случае это ресурсы, отноясщиеся к TcpClient'у, который необходимо закрыть методом Close().
уж и в поток писать нельзя?
в духе работы с текстовым файлом:
{
Stream stream = new BufferedStream(Client.GetStream( ), BufferSize);
StreamWriter writer = new StreamWriter(stream);
StreamReader reader = new StreamReader(stream);
// write
writer.WriteLine("hi client, this is greeting from server!!!");
writer.Flush();
// read
string reply = reader.ReadLine();
}
В Java есть аналогичные классы StreamReader и StreamWriter - их будет удобно использовать на клиенте.
Есть функция:
{
TcpClient Tc = new TcpClient(ip, 80);
StreamWriter sw = new StreamWriter(Tc.GetStream());
StreamReader sr = new StreamReader(Tc.GetStream());
sw.WriteLine(w);
sw.Flush();
r = sr.ReadLine();
}
И в нужном месте она вызывается(т.е. отправляет серверу ответ и получает). А как сделать, чтобы клиент находился в ожидании(при этом и клиент и сервер - WinApplication) получения сообщений от сервера. И как только он получает определенный ответ, так выполняет что-то.
PS: метод Read класса Stream будет ждать данных от клиента.
{
try
{
int go = 0;
Stream streamm = new BufferedStream(Client.GetStream(), BufferSize);
srrr = new StreamReader(streamm);
swww = new StreamWriter(streamm);
string ssrx = srrr.ReadLine(); // на этой строке почему-то зависает
string XXX, YYY;
double found = 0;
int reg = 0;
На той строке, что я обозначил, почему-то не происходит считывание данных.
Так зависает или НЕ считывает?
А ты пробовал записывать С другой стороны потока?
Если певый случай, то ReadLine() производит блокирующий вызов и ждёт данных из потока.
Можешь в принципе убрать слой BufferedStream и читателя/писателя создавать сразу из NetworkStream'а
PS: конечно, это не константное утверждение, но как предостережение...
PPS: И, в потоке лучше работать с блокирующим сокетом, при этом, что бы не замораживать поток ожиданием очередной порции данных - надо установить приемлимое время на чтение (см. доступные методы используемого тобой транспортного класса). К стати, то, что не происходит чтение из srrrr.ReadLine()... - подтверждение выше сказанного. Просто на той стороне клиент не отправил данные, или ещё что - не важно, метод ждёт данных... Ставь время... можно например, 1000 - может вполне хватить.
Мне хочется организовать группу программистов. Нужна любая поддержка и идеи.
Язык программирования, на котором ты пишешь значения не имеет!