[VC#] Работа с сокетами используя формы

 
0
 
.NET
ava
MuForum | 14.10.2007, 02:17
Доброе время суток всем жителям данного Форума!

Задача: Написать программу, которая при активирование кнопки будет прослушивать указанный порт.
Проблема: Из-за того, что работает цикл программа становится не активна.


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

namespace ConnectServer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
richTextBox1.AppendText("Program was start!" + Environment.NewLine);
}

private void button1_Click(object sender, EventArgs e)
{
int recv;
byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 60000);
//IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 60000);
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
newsock.Bind(ipep);
newsock.Listen((int)SocketOptionName.MaxConnections);
richTextBox1.AppendText("Waiting for a client..." + Environment.NewLine);
Socket client = newsock.Accept();
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
richTextBox1.AppendText(string.Format("Connected with {0} at port {1}", clientep.Address, clientep.Port) + Environment.NewLine);
while (true)
{
data = new byte[1024];
recv = client.Receive(data);
if (recv == 0) break;

richTextBox1.AppendText(Encoding.ASCII.GetString(data, 0, recv) + Environment.NewLine);
client.Send(data, recv, SocketFlags.None);
}
richTextBox1.AppendText(string.Format("Disconnected from {0}", clientep.Address) + Environment.NewLine);
client.Close();
newsock.Close();
}
}
}


Задача: Сделать так, Чтобы при нажатие на кнопку(button1) активировалось прослушивание данного порта.
Comments (13)
ava
tol05 | 14.10.2007, 11:51 #
весь код работы с сокетом вынеси в отдельный поток. Поток запускай по кажатию кнопки. Обновление контролов данными, полученными сокетом, делай через метод Invoke().

Поищи на форуме. Только не в общих вопросах, а в "Распределенные приложения и сеть" или "Разработка Windows Forms".
Примеров достаточно.
ava
MuForum | 19.10.2007, 23:53 #
#2, tol05 - Попользовался поиском, искал в двух разделах: "Распределённые приложения и сеть" и "Разработка Windows Forms".
- К сажалению толком ничего толком полезного не нашел =(

Так же гуглил несколько дней, попутно нашел пару интересных статеек, думаю людям будет интересно почитать(По крайне мере советую всем новечкам в этом деле почитать):



P.S. -> Если у кого-то есть возможность и желание, то было бы не плохо какой-то приметивный пример работы с сокетами на C# опубликовать, так как честно говоря у меня так и не вышло сделать, чтобы форма не зависала(Потом сделал в потоке как говорилось, но затем выключить не мог нормально, тоже форма зависала, Но уже при выключении)...
- Вообщем хотелось бы помощи в этом деле более опытных людей.
ava
tol05 | 20.10.2007, 12:35 #
Цитата (MuForum @ 19.10.2007, 23:53 findReferencedText)
#2, tol05 - Попользовался поиском, искал в двух разделах: "Распределённые приложения и сеть" и "Разработка Windows Forms".

- К сажалению толком ничего толком полезного не нашел =(

ок, допустим не нашел.

Цитата (tol05 @ 14.10.2007, 11:51 findReferencedText)
весь код работы с сокетом вынеси в отдельный поток

Цитата (tol05 @ 14.10.2007, 11:51 findReferencedText)
Поток запускай по кажатию кнопки

Цитата (tol05 @ 14.10.2007, 11:51 findReferencedText)
Обновление контролов данными, полученными сокетом, делай через метод Invoke().

где это все, где код?

Цитата (MuForum @ 19.10.2007, 23:53 findReferencedText)
- Вообщем хотелось бы помощи в этом деле более опытных людей.

ну а пока "более опытные люди" отдыхают, вот тебе то, о чем я говорил:

public partial class Form1 : Form
{
public delegate void Append(String myString);
private Thread thread;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
richTextBox1.AppendText("Program was start!" + Environment.NewLine);
}

private void SocketGoOn()
{
Append append = new Append(richTextBox1.AppendText);

byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 60000);
//IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 60000);
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

richTextBox1.Invoke(append, new object[] { "The socket was created" + Environment.NewLine }); int recv;

newsock.Bind(ipep);
newsock.Listen((int)SocketOptionName.MaxConnections);

richTextBox1.Invoke(append, new object[] {"Waiting for a client..." + Environment.NewLine});

Socket client = newsock.Accept();
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;

richTextBox1.Invoke(append, new object[] { string.Format("Connected with {0} at port {1}", clientep.Address, clientep.Port) + Environment.NewLine });

while(true)
{
data = new byte[1024];
recv = client.Receive(data);
if(recv == 0) break;
richTextBox1.Invoke(append, new object[] { Encoding.ASCII.GetString(data, 0, recv) + Environment.NewLine });
client.Send(data, recv, SocketFlags.None);
}
richTextBox1.Invoke(append, new object[] { string.Format("Disconnected from {0}", clientep.Address) + Environment.NewLine });
client.Close();
newsock.Close();
}

private void btnStart_Click(object sender, EventArgs e)
{
thread = new Thread(SocketGoOn);
thread.IsBackground = true;
thread.Start();
}
}
ava
MuForum | 21.10.2007, 15:10 #
#, tol05 - Ещё вопрос возник, вот как в потоке добавлять текст ты показал, я пытался тоже самое сделать для фокуса и Select, но у меня не вышло =(


public delegate void Append1(String myString);
public delegate void Append2(int start, int length);
...
private void WLog(String T)
{
Append1 append = new Append1(this.form.richTextBox1.AppendText);
Append2 select = new Append2(this.form.richTextBox1.Select);
this.form.richTextBox1.Focus();
this.form.richTextBox1.Invoke(append, new object[] { "[" + DateTime.Now.ToLongTimeString() + "] " + T + Environment.NewLine });
this.form.richTextBox1.Invoke(select, new object[] { this.form.richTextBox1.Text.Length, 0 }); // Вот тут проблема =(
}



P.S. -> Но при попытке добавить новый текст в richTextBox1, компилятор подвисает, и дебагер ругается...
ava
MuForum | 21.10.2007, 16:15 #
# for all - Проблему решил, спасибо корейским парням.


private void WLog(String T)
{
this.form.richTextBox1.Invoke((MethodInvoker)delegate { this.form.richTextBox1.Focus(); });
this.form.richTextBox1.Invoke((MethodInvoker)delegate { this.form.richTextBox1.AppendText(string.Format("[{0}] {1}",DateTime.Now.ToLongTimeString(), T) + Environment.NewLine); });
this.form.richTextBox1.Invoke((MethodInvoker)delegate { this.form.richTextBox1.Select(this.form.richTextBox1.Text.Length, 0); }); // Moving the Caret in end of the text.
}
ava
tol05 | 21.10.2007, 18:23 #
MuForum, передай
Цитата (MuForum @ 21.10.2007, 16:15 findReferencedText)
корейским парням
что можно и так писать:

private void WLog(String T)
{
this.form.richTextBox1.Invoke((MethodInvoker)delegate {
this.form.richTextBox1.Focus();
this.form.richTextBox1.AppendText(string.Format("[{0}] {1}",DateTime.Now.ToLongTimeString(), T) + Environment.NewLine);
this.form.richTextBox1.Select(this.form.richTextBox1.Text.Length, 0); });
}
smile

А вообще я смотрю у тебя много сильных связей получается между потоком и формой. Не очень удобно будет сопровождать такой код, модифицировать.

Ты бы попробовал через события поработать. Вынеси код потока в отдельный класс, объяви в нем события, привяжи к ним обработчики формы. Когда в потоке что-то происходит - возбуждай событие, а форма это событие обработает сама.

Т.о. у тебя будет независимый код логики потока, который время от времени сообщает (всем, кто подпишется) о своем состоянии.
ava
MuForum | 21.10.2007, 18:37 #
Цитата (tol05 @ 21.10.2007, 18:23)
MuForum, передай

Цитата (MuForum @  21.10.2007, 16:15 \\"findReferencedText\\")
корейским парням
что можно и так писать:



private void WLog(String T)

{

this.form.richTextBox1.Invoke((MethodInvoker)delegate {

this.form.richTextBox1.Focus();

this.form.richTextBox1.AppendText(string.Format("[{0}] {1}",DateTime.Now.ToLongTimeString(), T) + Environment.NewLine);

this.form.richTextBox1.Select(this.form.richTextBox1.Text.Length, 0); });

}

smile



А вообще я смотрю у тебя много сильных связей получается между потоком и формой. Не очень удобно будет сопровождать такой код, модифицировать.



Ты бы попробовал через события поработать. Вынеси код потока в отдельный класс, объяви в нем события, привяжи к ним обработчики формы. Когда в потоке что-то происходит - возбуждай событие, а форма это событие обработает сама.



Т.о. у тебя будет независимый код логики потока, который время от времени сообщает (всем, кто подпишется) о своем состоянии.

Цитата


MuForum, передай

Цитата (MuForum @  21.10.2007, 16:15 \\"findReferencedText\\")
корейским парням
что можно и так писать:



- Это нашел на одном из корейских Форумах тему, там было в заголовке текст: "richTextBox1.Invoke", ну я и начал разбирать код, затем увидел этот вариант и уже переделал под себя =)

Цитата


А вообще я смотрю у тебя много сильных связей получается между потоком и формой. Не очень удобно будет сопровождать такой код, модифицировать.



- Да, я делаю "ConnectServer", программу, которая будет связывать игроков с главным Сервером. На C++ я это уже реализовывал детом этого года, но сейчас хочу это реализовать на C# =)

P.S. -> Если у тебя есть возможность и желание, ты не мог бы пожалуйста более развёрнуто объяснить, чтобы я хоть более немение понимал о чём речь и смог поискать в Интернете.

- Заранее благодарен.
ava
tol05 | 21.10.2007, 18:49 #
Цитата (MuForum @ 21.10.2007, 18:37 findReferencedText)
Если у тебя есть возможность и желание, ты не мог бы пожалуйста более развёрнуто объяснить, чтобы я хоть более немение понимал о чём речь и смог поискать в Интернете.

Что именно объяснить? Я вроде объяснил уже :)

У тебя есть достаточно большой кусок кода, работающего в отдельном потоке.
Есть форма для ввода-вывода результатов.

Код потока выполняет много разнообразных действий, получается, что через каждые несколько строк нужно посылать форме ссобщения о чем-то. Вот и сделай в форме класс Worker, создавай его и инициализируй нужными ему данными. Давай ему команду работать. Он с твоими данными начнет работу, и, время от времени, будет возбужлать свои события "Я включился", "Я получил данные", "Я отправил данные" и т.п.
А в форме, после создания экземпляра Worker, подпишись на его события. Вот и все. Отключил одну форму, подключил другую - от этого Worker не изменится. И аналогично с другой стороны - отключил Worker - форма дальше работает, только событий не получает и обработчиков своих не вызывает.

вот и все, в принципе smile
ava
shubert | 20.11.2007, 19:12 #
Посмотрел ветку через события делается легче. если хочешь могу выложить свой клиент только работает с асинхронными вызовами, как только поступают данные вызываются события.
ava
MuForum | 28.11.2007, 11:13 #
#10, shubert - Ну если есть возможность, то опубликую свою работу, что-то я для себя извлеку и может кому-то тоже понадобится.
ava
shubert | 30.11.2007, 10:20 #
сейчас опубликую только у меня есть небольшие проблемы с реконектом. сейчас выложу и объясню.
В файле есть наработки по прокси но он еще тестовый.
У меня возник такой вопрос. как можно убить событие если оно уже вызвалось и висит в памяти.
Вообщем у меня если нету связи возникает событие и вызывается реконект к серверу. у меня есть метод Disconect который должен закрывать сокет если есть соединение или прекращать реконект если нету соединения. и вот получается что не могу остановить реконект. у меня есть глобальная переменная stopreconnect, которая отвечает за остановку реконекта. и получается что когда я останавливаю в один момент к этой переменной обращаются из двух потоков. Вообщем посмотрите.
ava
shubert | 30.11.2007, 10:21 #

#region Делегаты
public delegate void ADelegate(object sender, string st);
public delegate void ReceiveDelegate(object sender, string st, int count);
public delegate void ErrorDelegate(Exception error, string st);
#endregion
/// <summary>
/// класс для работы TCP/IP клиентом. построен на асинхронных методах.
/// </summary>
public class AClient
{
#region Закрытые Поля
private int port=0;
private string ip="127.0.0.1";
internal Socket s=null;
private bool connected=false;
private byte[] buffer=null;
private int buffersize=1024;
private bool blocking=false;
private bool reconnect=false;
private int countreconnect=0;
private int timereconnect=1; //через сколько секунд будет произведен реконнект.
private int i=0; //счетчик реконектов.
private bool stopreconnect=false;
#endregion

#region Свойства
/// <summary>
/// Connected=true - связь установлена,
/// Connected=false - связь не установлена.
/// </summary>
public bool Connected
{
get
{ return connected;}
}
public int BufferSize
{
get
{ return buffersize;}
set
{ buffersize = value;}
}
public byte[] Buffer
{
get
{return buffer;}
}
public int Port
{
get
{return port;}
set
{port = value;}
}
public string IP
{
get
{return ip;}
set
{ip = value;}
}
public bool Blocking
{
get
{return blocking;}
set
{blocking = value;}
}
public bool Reconnect
{
get
{return reconnect;}
set
{reconnect = value;}
}
public int CountReconnect
{
get
{return countreconnect;}
set
{countreconnect = value;}
}
public int TimeReconnect
{
get
{return timereconnect;}
set
{timereconnect = value;}
}
#endregion

#region Констуктор
/// <summary>
/// Конструктор асинхронного клиента
/// </summary>
public AClient()
{
try
{
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
this.OnDisconnect += new ADelegate(AClient_OnDisconnect);
}
catch (SocketException error)
{
if (OnError != null)
OnError(error, "Ошибка создания сокета.");
}
}


/// <summary>
/// Конструктор асинхронного клиента
/// </summary>
/// <param name="ip_">IP адрес удаленного хоста</param>
/// <param name="port_">Порт удаленного хоста</param>
public AClient(string ip_, int port_)
{
ip = ip_;
port = port_;
try
{
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
this.OnDisconnect += new ADelegate(AClient_OnDisconnect);
}
catch (SocketException error)
{
if (OnError != null)
OnError(error, "Ошибка создания сокета.");
}

}
#endregion

#region События
/// <summary>
/// Связь с удаленным хостом установлена.
/// </summary>
public event ADelegate OnConnected;
/// <summary>
/// Связь с удаленным хостом разорвана.
/// </summary>
public event ADelegate OnClose;
/// <summary>
/// Данные удаленному хосту отправлены.
/// </summary>
public event ADelegate OnSend;
/// <summary>
/// Произошел разрыв соединения. Повторить установления связи с удаленным хостом.
/// </summary>
public event ADelegate OnReconnected;
/// <summary>
/// Данные поступили от удаленного хоста.
/// </summary>
public event ReceiveDelegate OnReceive;
/// <summary>
/// Произошла ошибка.
/// </summary>
public event ErrorDelegate OnError;
/// <summary>
/// Происходит закрытие сокета. Прекращение сеанса.
/// </summary>
public event ADelegate OnClosing;

public event ADelegate OnDisconnect;

void AClient_OnDisconnect(object sender, string st)
{
if (!stopreconnect)
{
Reconnecting();
}
}
#endregion

#region Методы

public void Connect()
{
if (connected == false)
{
try
{
if (s == null)
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
s.Blocking = blocking;
s.BeginConnect(new IPEndPoint(IPAddress.Parse(ip), port), new AsyncCallback(ConnectCallBack), s);
}
catch (SocketException error)
{
if (OnError != null)
OnError(error, "Ошибка установления соединения с удаленным хостом.");
}
}
else
{
if (OnError != null)
OnError(new SocketException((int)SocketError.IsConnected), "Связь с удаленным хостом уже установлена.");
}
}

public void Connect(string IP_, int Port_)
{
ip = IP_;
port = Port_;
Connect();
}

public void Connect(ProxyType Type, string ProxyIP, int ProxyPort, string IP_, int Port_)
{

}

public void Disconnect()
{
if (OnClosing != null)
OnClosing(this, "Происходит закрытие сокета.");
if (connected == true)
{
try
{
connected = false;
stopreconnect = true;
s.Shutdown(SocketShutdown.Both);
s.Close();
s = null;
if (OnClose != null)
OnClose((object)this, "Сокет закрыт.");
OnDisconnect(this, "Дисконект");
}
catch (SocketException error)
{
if (OnError != null)
OnError(error, "Ошибка закрытия сокета.");
}
}
else
{
//connected = false;
stopreconnect = true;
s = null;
if (OnError != null)
OnError(new SocketException((int)SocketError.NotConnected), "Сокет не соединен с сервером.");
OnDisconnect(this, "Дисконект");
}
}

public void Send(string st)
{
if (connected == true)
{
try
{
byte[] temp_buffer = Encoding.ASCII.GetBytes(st);
s.BeginSend(temp_buffer, 0, temp_buffer.Length, 0, new AsyncCallback(SendCallBack), s);
}
catch (SocketException error)
{
if (OnError != null)
OnError(error, "Ошибка при отправлении данных соединненому сокету.");
//if ((error.SocketErrorCode == SocketError.ConnectionAborted) ||
// (error.SocketErrorCode == SocketError.Disconnecting))
// connected = false;
}
}
else
{
if (OnError != null)
OnError(new SocketException((int)SocketError.NotConnected), "Сокет не соединен с сервером.");
}
}

private void Reconnecting()
{
//if (stopreconnect == false)
{
if (countreconnect == 0)
{
OnReconnected(this, "Попытка установления нового соединения с удаленным хостом.");
Thread.Sleep(timereconnect*1000);
Connect();
}
else
{
if (i < countreconnect)
{
i++;
OnReconnected(this, "Попытка установления нового соединения с удаленным хостом.");
Thread.Sleep(timereconnect*1000);
Connect();
}
}
}
}

private void Receive(Socket s)
{
if (connected == true)
{
try
{
buffer = new byte[buffersize];
s.BeginReceive(buffer, 0, buffersize, 0, new AsyncCallback(ReceiveCallBack), s);
}
catch (SocketException error)
{
connected = false;
if (OnError != null)
OnError(error, "Ошибка получения данных от соединенного сокета.");
if (reconnect == true)
{
stopreconnect = false;
OnDisconnect(this, "Дисконект");
}
}
}
}

#endregion

#region Асинхронные Методы

private void ConnectCallBack(IAsyncResult ar)
{
try
{
Socket temp_socket = (Socket)ar.AsyncState;
temp_socket.EndConnect(ar);
connected = true;
if (OnConnected != null)
OnConnected(this, "Соединение установлено удачно.");
Receive(temp_socket);
}
catch (SocketException error)
{
connected = false;
if (OnError != null)
OnError(error, "Ошибка установления соединения с удаленным хостом. Ошибка вызова ConnectCallback.");
if (reconnect == true)
{
stopreconnect = false;
OnDisconnect(this, "Дисконект");
}

//if (reconnect == true)
// Reconnecting();
}
}

private void SendCallBack(IAsyncResult ar)
{
try
{
Socket temp_socket = (Socket)ar.AsyncState;
int count = temp_socket.EndSend(ar);
if (OnSend != null)
OnSend((object)this, "Отправлено " + count.ToString() + " байт.");
}
catch (SocketException error)
{
if (OnError != null)
OnError(error, "Ошибка при отправлении данных соединненому сокету. Ошибка вызова SendCallBack.");
}

}

private void ReceiveCallBack(IAsyncResult ar)
{
try
{
Socket temp_socket = (Socket)ar.AsyncState;
if ((temp_socket != null) && (temp_socket.Connected == true))
{
int count = temp_socket.EndReceive(ar);
if (count > 0)
{
if (OnReceive != null)
OnReceive((object)this, ASCIIEncoding.ASCII.GetString(buffer, 0, count), count);
Receive(temp_socket);
}
else
{
//throw new SocketException((int)SocketError.ConnectionAborted);
if (OnError != null)
OnError(new SocketException((int)SocketError.ConnectionAborted), "Прием пустых данных. Ошибка вызова ReceiveCallBack");
connected = false;
s.Shutdown(SocketShutdown.Both);
s.Close();
s = null;
if (reconnect == true)
{
stopreconnect = false;
OnDisconnect(this, "Дисконект");
}
}
}
}
catch (SocketException error)
{
if (OnError != null)
OnError(error, "Ошибка получения данных от соединенного сокета. Ошибка вызова ReceiveCallBack");
connected = false;
s.Shutdown(SocketShutdown.Both);
s.Close();
s = null;
if (reconnect == true)
{
stopreconnect = false;
OnDisconnect(this, "Дисконект");
}
}

}

#endregion

}

#endregion


вот выкладываю код

added later:
Если найдете глюки сообщите.
ava
Yama | 12.12.2007, 14:47 #
MuForum, используй асинхронные сокеты smile
И не надо будет тебе самому писать оберки на то, что в .нет уже давным давно завернуто за нас smile
Please register or login to write.
Firm of day
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Contributors
  Yama   shubert   tol05   MuForum
advanced
Submit