Всегда тянуло к програмкам хоть как-то связанным с взаимодействием пользователей. Первое и самое простенькое, что решил написать, это чат на сокетах, погуглил, благо в интернете много гайдов по этой теме, написал. Чат был ужасно примитивным, поэтому, ради практики, решил немного увеличить его функционал, введя список пользователей, небольшой фильтр мата и тому прочее. Это всё делалось не ради чего-то высшего, а ради себя. И вот между тем решил еще чуть более усложнить программку и рядом с самим окном чата сделать небольшую стену, для общего рисования, где можно творить всем и сразу.
О том как сделать сам чат говорить не буду, т.к. а) В интернете очень много гайдов по этой теме б) Первая программа которую я написал на сокетах и был тот самый чат по тем самым гайдам, и оттого работа рисовалки очень схожа с работой чата. Программа будет с очень бедным функционалом, только окошко для рисования, никакого списка юзеров и т.п..
И так!
Будем писать и сервер, и клиент, причем второй получился у меня более сложный по содержанию, поэтому начнём пожалуй с сервачка

От сервера нам многое и не надо. Он всего-то и нужен нам для того, чтобы рассылать полученные координаты всем остальным юзверям, ну и естественно работал, поэтому из компонентов понадобится только две кнопки ( для запуска и остановки ), любая компонента для ввода порта, я взял TEdit, ну и сердце нашего сервера ServerSocket ( кидайте куда угодно, в готовой скомпилированной программе Вы его не увидите) . Естественно в коде можно забить один порт без права его ввода, но давайте лучше дадим свободы.

Ну, с интерфейсом справились.
Перейдем к самому коду. Тут писать много не надо. Запуск сервера осуществляется по такой процедуре.
Код:
1 2 3 4 5 6 7 | procedure TForm1 . Button1Click(Sender: TObject);
var port,err: integer ;
begin
val (edit1 . text,port,err);
ServerSocket1 . Port:=port;
ServerSocket1 . Active:= True ;
end ;
|
Т.е. берем порт, из TEdit, преобразуем из строки в число, указываем его серверу и запускаем. Из этого кода будет интуитивно понятно, что же будет происходить по нажатию второй кнопки.
Код:
1 2 3 4 | procedure TForm1 . Button2Click(Sender: TObject);
begin
ServerSocket1 . Active:= False ;
end ;
|
На этом отложим наш сервер на недолгое время. Перейдем к клиенту. Тут, как я говорил раннее всё сложнее, нам понадобятся: две кнопки ( для коннекта и очистки окна ), ClientSocket, TImage ( ну или любой другой объект с канвой ), два TEdit ( один для IP, второй для порта)

Не красота конечно, ну ладно, переживём. Первое что надо сделать это коннект:
Код:
1 2 3 4 5 6 7 8 | procedure TForm1 . Button2Click(Sender: TObject);
var port,err: integer ;
begin
ClientSocket1 . Host:=Edit1 . text;
val (edit2 . text,port,err);
ClientSocket1 . Port:=port;
ClientSocket1 . Open;
end ;
|
Ip-адресс записывается как строка, поэтому переводить его не надо, а вот порт у нас целое число, поэтому пользуемся VAL. Ну вот, мы на сервере, если клиент не может присоединится к серверу ( не тот порт, сервер не работает и т.д. и т.п. ), то он вам обязательно об этом сообщит, а если по нажатию на кнопку Connect ничего не произошло, то это хороший знак. Ладно, мы на сервере, что дальше? Дальше самое интересное. На повестке дня три процедуры связанные с Image1 ( возможно дальше пойдёт мутный способ, и можно было обойтись легче, но моя голова решила так )
Код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | procedure TForm1 . Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer );
begin
moused:= True ;
end ;
procedure TForm1 . Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer );
begin
moused:= False ;
end ;
procedure TForm1 . Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer );
var x1,y1: string ;
var send: string ;
begin
if moused then
begin
str(x,x1);
str(y,y1);
send:=x1+ ' ' +y1;
ClientSocket1 . Socket . SendText(send);
end ;
end ;
|
Мы же не хотим чтобы во время любого движения мыши по Image1 у нас что-то рисовалось, нам же надо кнопочку зажать! Для этого вводим глобальную переменную ( у меня moused ) и при нажатии левой кнопкой мыши по Image1, делаем moused:=True, при отжатии соответственно False. Далее идем в процедуру по движению мыши. Проверяем, если ЛКМ зажата, то начинаем яростно слать координаты, преобразуя их в одну строку, где координаты разделены пробелом. Возвращаемся к серверу и получаем наши месседжи и возвращаем их обратно всем клиентам. Для получения сокета есть подходящее событие - OnSocketRead. Вот и добавляем на нашем сервере такую процедуру выполняущуюся по данному событию:
Код:
1 2 3 4 5 6 7 8 9 | procedure TForm1 . ServerSocket1ClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
var koordinati: string ;
var i: integer ;
begin
koordinati:=Socket . ReceiveText;
for i:= 0 to ServerSocket1 . Socket . ActiveConnections- 1 do
ServerSocket1 . Socket . Connections[i].SendText(Socket . ReceiveText);
end ;
|
Сервер получает координаты и рассылает их дальше всем клиентам. ServerSocket1.Socket.ActiveConnections возвращает количество активных клиентов, но нумерация в ServerSocket1.Socket.Connetions начинается с нуля, поэтому в цикле перебор начинаем с нуля и до (кол-во клиентов-1). Отлично, теперь осталось получить и обработать со стороны клиента пришедшие координаты. Опять возвращаемся к нашему клиенту. Пишем процедуру для ClientSocket по событию OnRead:
Код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | procedure TForm1 . ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var textik: string ;
x1,y1,i,err,j: integer ;
koor: string ;
begin
textik:=Socket . ReceiveText;
for i:= 1 to length(textik) do begin
if textik[i]<> ' ' then koor:=koor+textik[i]
else if textik[i]= ' ' then
begin val (koor,x1,err);
break;
end ;
end ;
koor:= '' ;
for j:=i+ 1 to length(textik) do
koor:=koor+textik[j];
val (koor,y1,err);
Image1 . Canvas . Pixels[x1,y1]:=clBlack
end ;
|
Чуть больше кода чем раньше, но тут все просто, из нашей строки достаем координату Х и Y, и ставим черную точку в то место, куда они указывают. Отрисовка по точкам конечно сильно страдает, очень много координаты идёт на сервер, не все успевают обрабатываться и высылаться, по-этому лучше бы было сделать через LineTo, но там появляются другие проблемы, с тем, чтобы для LineTo нужна начальная координата и более сложный код отправки и получения координат, т.к. если делать как у нас, то при рисовании двух и более клиентов, будут соединятся все точки, и получится кошмар. Да, это можно быстренько осуществить, но сейчас мы особо не заморачиваемся. Кнопка очистки пишется элементарно, мы заместо координат отправляем какое-нибудь определенное сообщение, например "clear", оно возвращается клиентам, и прежде чем что-то рисовать, проверяем, если вернувшаяся строка равна "clear", то очищаем наш Image1, иначе дальше отрисовываем пиксели.
И так, вроде бы всё готово, компилируем обе программы, запускаем сервер, вводим порт, жмем старт, запускаем клиент, вводим необходимые данные и коннектимся.
Тадааам!
http://justforfun27.livejournal.com/