1   2   3   4   5   6   7   8   9   10
Ім'я файлу: Отчет.docx
Розширення: docx
Розмір: 396кб.
Дата: 08.06.2021
скачати

Створення керуючого процесу


В якості керуючого процесу створено консольний додаток.

Код представлено нижче:

namespace ControlProcess

{

using System;

using System.Collections.Generic;

using System.Diagnostics;

using System.Drawing;

using System.Linq;

using System.Threading;
using NamedPipeWrapper;
using Сommon;
public static class Program

{

///

/// Путь процессу своего поля

///


private const string OwnFieldProcessName = @"..\..\..\OwnField\bin\Debug\OwnField.exe";
///

/// Путь к процессу своего поля

///


private const string EnemyFieldProcessName = @"..\..\..\EnemyField\bin\Debug\EnemyField.exe";
///

/// Массив процессов

///


private static readonly Process[] Process = new Process[]

{

new Process

{

StartInfo = new ProcessStartInfo(

OwnFieldProcessName, $"1")

},

new Process

{

StartInfo = new ProcessStartInfo(

OwnFieldProcessName, $"2")

},

new Process

{

StartInfo = new ProcessStartInfo(

EnemyFieldProcessName, $"1")

},

new Process

{

StartInfo = new ProcessStartInfo(

EnemyFieldProcessName, $"2")

}

};
///

/// Именнованый канал

///


private static readonly NamedPipeServer
PipeServer = new NamedPipeServer
("SeaBattle");
///

/// Ходы игрока 1 для передачи по каналу

///


private static readonly List MoveData1 = new List();
///

/// Ходы игрока 2 для передачи по каналу

///


private static List MoveData2 = new List();
///

/// Корабли игрока 1 для передачи по каналу

///


private static readonly List ShipData1 = new List();
///

/// Корабли игрока 2 для передачи по каналу

///


private static readonly List ShipData2 = new List();
///

/// Мьютекс для синхронизации

///


private static readonly Mutex Mtx = new Mutex();
///

/// Корабли игрока 1

///


private static List shipsPlayer1 = new List();
///

/// Корабли игрока 2

///


private static List shipsPlayer2 = new List();

///

/// Игровое поле игрока 1

///


private static bool[,] gameFieldPlayer1;
///

/// Игровое поле игрока 2

///


private static bool[,] gameFieldPlayer2;
///

/// Игрок 1 закончил расстановку

///


private static bool done1 = false;
///

/// Игрок 2 закончил расстановку

///


private static bool done2 = false;
///

/// Очередь хода

///


private static int orderMove = 0;
///

/// Убито корблей игроком 1

///


private static int kill1 = 0;
///

/// Убито корблей игроком 2

///


private static int kill2 = 0;
///

/// Имеем победителя

///


private static bool hasWinner = false;
///

/// Обработчик закрытия консольного приложения

///


private static SignalHandler signalHandler;
// Главная функция

public static void Main(string[] args)

{

// подпись на события закрытия консоли

signalHandler += HandleConsoleSignal;

ConsoleHelper.SetSignalHandler(signalHandler, true);
// подпись на события каналов

PipeServer.ClientConnected += PipeServerOnClientConnected;

PipeServer.ClientMessage += PipeServerOnClientMessage;

PipeServer.ClientDisconnected += PipeServerOnClientDisconnected;
PipeServer.Start(); // запуск канала

Console.WriteLine("Сервер запущен.");

Console.WriteLine();

// запуск процессов

for (int i = 0; i < Process.Length; i++)

{

Process[i].Start();

Console.WriteLine($"Процесс {i + 1} запущен.");

}
Console.WriteLine();

Console.WriteLine("Для закрытия нажмите любою клавишу...");

Console.WriteLine();
Console.ReadKey();

Console.WriteLine();
// уничтожение процессов и остановка сервера

for (int i = 0; i < Process.Length; i++)

{

Process[i].Kill();

Console.WriteLine($"Процесс {i + 1} закрыт.");

}
Console.WriteLine();
PipeServer.Stop();

Console.WriteLine($"Сервер остановлен.");
Thread.Sleep(1000);

}
// событие завершение работы консольного приложения

private static void HandleConsoleSignal(ConsoleSignal consoleSignal)

{

// уничтожение процессов и остановка сервера

for (int i = 0; i < Process.Length; i++)

{

Process[i].Kill();

Console.WriteLine($"Процесс {i + 1} закрыт.");

}

}
// прием сообщений от клиентов

private static void PipeServerOnClientMessage(NamedPipeConnection
connection, PipeMessage message)

{

switch (message.PipeMessageType)

{

case PipeMessageType.EndArrange:

EndArrange(message);

break;
case PipeMessageType.Update:

Update(connection, message);

break;
case PipeMessageType.SetMove:

SetMove(connection, message);

break;

}

}
// установка хода

private static void SetMove(NamedPipeConnection
connection, PipeMessage message)

{

// если у нас есть победитель

if (hasWinner)

{

// отправка сообщения

connection.PushMessage(

new PipeMessage()

{

PlayerId = 0,

PipeMessageType = PipeMessageType.Winn

});

return;

}
// нет очереди хода

if (orderMove == 0)

{

Console.WriteLine("Расстановка кораблей не закончена!");

connection.PushMessage(

new PipeMessage()

{

PipeMessageType = PipeMessageType.Error, Message = $@"Расстановка кораблей не закончена!"

});

return;

}
// первый игрок не готов

if (!done1)

{

Console.WriteLine("Игрок 1 не готов!");

connection.PushMessage(

new PipeMessage() { PipeMessageType = PipeMessageType.Error, Message = @"Игрок 1 не готов!" });

return;

}
// второй игрок не готов

if (!done2)

{

Console.WriteLine("Игрок 2 не готов!");

connection.PushMessage(

new PipeMessage() { PipeMessageType = PipeMessageType.Error, Message = @"Игрок 2 не готов!" });

return;

}
// ход первого игрока

if (message.PlayerId == 1)

{

Console.WriteLine("Ходит игрок 1");
// если не его очередь

if (orderMove != 1)

{

Console.WriteLine($@"Очередь ходить игрока {orderMove}");

connection.PushMessage(

new PipeMessage()

{

PipeMessageType = PipeMessageType.Error, Message = $@"Очередь ходить игрока {orderMove}"

});

}

else

{

// если ход уже был

if (MoveData1.Contains(message.Index))

{

Console.WriteLine($"Ход {message.Index.ToString()} уже был");

connection.PushMessage(

new PipeMessage()

{

PipeMessageType = PipeMessageType.Error,

Message = $"Ход {message.Index.ToString()} уже был"

});

return;

}
// получение типа хода

var moveType = gameFieldPlayer2[message.Index.X, message.Index.Y] ? MoveType.Cross : MoveType.Circle;
// добавление в коллекцию ходов

MoveData1.Add(new MoveData(new Point(message.Index.X, message.Index.Y), moveType));
// проверка убийства корабля

CheckKillShip(1, shipsPlayer2, MoveData1);

connection.PushMessage(

new PipeMessage()

{

PlayerId = 1,

PipeMessageType = PipeMessageType.Update,

MoveType = moveType,

Index = message.Index,

ShipData = ShipData1

});
Console.WriteLine($"Ход {message.Index.ToString()} {moveType.ToStringName().ToLower()}");
orderMove = moveType == MoveType.Cross ? 1 : 2;
if (HaveWinner())

{

Console.WriteLine($"Ход {message.Index.ToString()} приносит игроку 1 победу");

connection.PushMessage(

new PipeMessage()

{

PlayerId = 1,

PipeMessageType = PipeMessageType.Winn

});

return;

}
Console.WriteLine($"Очередь ходить игрока {orderMove}");

}

}

else if (message.PlayerId == 2)

{

Console.WriteLine("Ходит игрок 2");
if (orderMove != 2)

{

Console.WriteLine($@"Очередь ходить игрока {orderMove}");

connection.PushMessage(

new PipeMessage()

{

PipeMessageType = PipeMessageType.Error, Message = $@"Очередь ходить игрока {orderMove}"

});

}

else

{

if (MoveData2.Contains(message.Index))

{

Console.WriteLine($"Ход {message.Index.ToString()} уже был");

connection.PushMessage(

new PipeMessage()

{

PipeMessageType = PipeMessageType.Error,

Message = $@"Ход уже был"

});

return;

}

var moveType = gameFieldPlayer1[message.Index.X, message.Index.Y] ? MoveType.Cross : MoveType.Circle;

MoveData2.Add(new MoveData(new Point(message.Index.X, message.Index.Y), moveType));

CheckKillShip(2, shipsPlayer1, MoveData2);

connection.PushMessage(

new PipeMessage()

{

PlayerId = 2,

PipeMessageType = PipeMessageType.Update,

MoveType = moveType,

Index = message.Index,

ShipData = ShipData2

});
Console.WriteLine($"Ход {message.Index.ToString()} {moveType.ToStringName().ToLower()}");
orderMove = moveType == MoveType.Cross ? 2 : 1;
if (HaveWinner())

{

Console.WriteLine($"Ход {message.Index.ToString()} приносит игроку 2 победу");

connection.PushMessage(

new PipeMessage()

{

PlayerId = 2,

PipeMessageType = PipeMessageType.Winn

});

return;

}
Console.WriteLine($"Очередь ходить игрока {orderMove}");

}

}

}
// проверка есть ли победитель (убиты все возможные корабли)

private static bool HaveWinner()

{

if (kill1 == Constants.ShipCount() || kill2 == Constants.ShipCount())

hasWinner = true;

return hasWinner;

}
// проверка убит ли корабль и добавление его в коллекцию для передачи

private static void CheckKillShip(int playerId, List ships, List moveData)

{

RecalculateShipData(ships, moveData);
foreach (var ship in ships)

{

if (ship.IsKilled)

{

if (playerId == 1 && !ShipData1.Contains(ship.PastePoint))

{

ShipData1.Add(new ShipData(ship.PastePoint, ship.ShipType, ship.ShipOrientation));

kill1++;

Console.WriteLine($"Игрок 1 потопил {ship.ShipType.ToStringName().ToLower()} противника");

Console.WriteLine($"Утоплено кораблей {kill1}");

}

else if (playerId == 2 && !ShipData2.Contains(ship.PastePoint))

{

ShipData2.Add(new ShipData(ship.PastePoint, ship.ShipType, ship.ShipOrientation));

kill2++;

Console.WriteLine($"Игрок 2 потопил {ship.ShipType.ToStringName().ToLower()} противника");

Console.WriteLine($"Утоплено кораблей {kill2}");

}

}

}

}
// перерасчет точек кораблей

private static void RecalculateShipData(List ships, List moveData)

{

var find = false;
foreach (var move in moveData)

{

if (move.MoveType != MoveType.Cross) continue;
foreach (var ship in ships.ToList())

{

// if (ship.ShipPoints.All(x => x != move.Point)) break;
foreach (var shipPoint in ship.ShipPoints.ToList())

{

if (shipPoint == move.Point)

{

ship.ShipPoints.Remove(shipPoint);

find = true;

}
if (find) break;

}
if (find) break;

}
if (find) break;

}

}
// обновление данных

private static void Update(NamedPipeConnection
connection, PipeMessage message)

{

if (message.PlayerId == 1 && MoveData2.Count != 0)

{

Mtx.WaitOne();

connection.PushMessage(

new PipeMessage() { PipeMessageType = PipeMessageType.Update, PlayerId = 1, MoveData = MoveData2 });

Mtx.ReleaseMutex();

}

else if (message.PlayerId == 2 && MoveData1.Count != 0)

{

Mtx.WaitOne();

connection.PushMessage(

new PipeMessage() { PipeMessageType = PipeMessageType.Update, PlayerId = 2, MoveData = MoveData1 });

Mtx.ReleaseMutex();

}

}
// окончание игроком расстановки кораблей

private static void EndArrange(PipeMessage message)

{

if (message.PlayerId == 1 && !done1)

{

gameFieldPlayer1 = message.GameField;

shipsPlayer1 = message.ShipData.ToShipList().ToList();

Console.WriteLine($"Получена расстановка кораблей игрока {message.PlayerId}");

if (orderMove == 0)

{

orderMove = 1;

Console.WriteLine($"Первый ходит игрок {message.PlayerId}");

}

done1 = true;

}

else if (message.PlayerId == 2 && !done2)

{

gameFieldPlayer2 = message.GameField;

shipsPlayer2 = message.ShipData.ToShipList().ToList();

Console.WriteLine($"Получена расстановка кораблей игрока {message.PlayerId}");

if (orderMove == 0)

{

orderMove = 2;

Console.WriteLine($"Первый ходит игрок {message.PlayerId}");

}

done2 = true;

}

}
///

/// Подключения пользователя к серверу

///


private static void PipeServerOnClientConnected(NamedPipeConnection
connection)

{

/*if (connection.Id > 2)

return;
Console.WriteLine($"Игрок {connection.Id} подключен к серверу.");*/

}
///

/// Отклчюение пользователя от сервера

///


private static void PipeServerOnClientDisconnected(NamedPipeConnection
connection)

{

/*if (connection.Id > 2)

return;
Console.WriteLine($"Игрок {connection.Id} отключен от сервера.");*/

}

}

}
    1. Результати роботи програми


Результати роботи додатку показані на рис. 4-6 .



Рис. 4. Зовнішній вигляд додатку


Рис. 5. Розміщення кораблів


Рис. 6. Хід гри

Висновок

Потік (thread) являє собою незалежну послідовність інструкцій в програмі. Потоки грають важливу роль як для клієнтських, так і для серверних додатків. Наприклад, під час введення якогось коду C # у вікні редактора Visual Studio проводиться аналіз на предмет різних синтаксичних помилок. Цей аналіз здійснюється окремим фоновим потоком. Те ж саме відбувається і в засобі перевірки орфографії в Microsoft Word. Один потік очікує введення даних користувачем, а інший в цей час виконує у фоновому режимі деякий аналіз. Третій потік може зберігати записуються дані в тимчасовий файл, а четвертий - завантажувати додаткові дані з Інтернету.

У додатку, який функціонує на сервері, один потік завжди очікує надходження запиту від клієнта і тому називається потоком-слухачем (listener thread). При отриманні запиту він відразу ж пересилає його окремому робочому потоку (worker thread), який далі сам продовжує взаємодіяти з клієнтом. Потік-слухач після цього повинен бути відшкодований до своїх обов'язків по очікуванню надходження наступного запиту від чергового клієнта.

Кожен процес складається з ресурсів, таких як віконні дескриптори, файлові дескриптори і інші об'єкти ядра, має виділену область у віртуальній пам'яті і містить як мінімум один потік. Потоки плануються до виконання операційною системою. У будь-якого потоку є пріоритет, лічильник команд, який вказує на місце в програмі, де відбувається обробка, і стек, в якому зберігаються локальні змінні потоку. Стек у кожного потоку виглядає по-своєму, але пам'ять для програмного коду і купа поділяються серед всіх потоків, які функціонують усередині одного процесу.

Це дозволяє потокам всередині одного процесу швидко взаємодіяти між собою, оскільки всі потоки процесу звертаються до однієї і тієї ж віртуальної пам'яті. Однак це також і ускладнює справу, оскільки дає можливість безлічі потоків змінювати одну і ту ж область пам'яті.

Список використаної літератури
1. Побегайло А. «Системное программирование в Windows».  СПб.: БХВ-Петербург, 2006.  1056 с.

2. Шилдт Г. Полное руководство по С# 4.0.: Пер. с англ. - М.: ООО «И. Д. Вильямc», 2012. - 1047 с.

3. Джеффри Рихтер «Программирование на платформе Microsoft.NET Framework 4.5 на языке C#».

4. Потоки (threads) и многопоточное выполнение программ, http://www.intuit.ru/studies/courses/641/497/lecture/11284?page=1

5. Многопоточность и синхронизация, https://professorweb.ru/my/csharp/thread_and_files/1/1_7.php

6. Общие сведения о платформе .NET Framework, https://msdn.microsoft.com/

7. Преимущества многопоточности, https://www.opennet.ru/docs/RUS/ipcbook/node43.html

8. Многопоточность в ООП, https://habrahabr.ru/post/164487/

9. Пространство имен System.Threading. https://msdn.microsoft.com/ ru-ru/ library/ system.threading(v=vs.110).aspx

10. Класс Thread. https://msdn.microsoft.com/ru-ru/library/system. threading.thread(v=vs.110).aspx

  1. Класс Process. https://msdn.microsoft.com/ru-ru/library/system. diagnostics.process(v=vs.110).aspx

  2. Синхронизация потоков (C# и Visual Basic). https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms173179 (v=vs.120). aspx

1   2   3   4   5   6   7   8   9   10

скачати

© Усі права захищені
написати до нас