1. Trang chủ
  2. » Công Nghệ Thông Tin

Hướng dẫn chi tiết làm 1 chương trình chat đơn giản

20 425 2

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 20
Dung lượng 399,99 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Tiếp tục trên Solution hiện tại tạo thêm 2 project có dạng Class Library đặt tên là Command và Server.. Bước 2: vào project Command tạo 2 file có tên là Enum.cs và Command.cs.. Bước 3: m

Trang 1

Đại Học Quốc Gia Thành Phố Hồ Chí Minh

Trường Đại học Công nghệ Thông tin

Lớp K2C6 Hướng dẫn chi tiết làm 1 chương trình Chat đơn giản

Giáo viên hướng dẫn: Phạm Thi Vương

I Giới thiệu chung về cấu trúc.

- Chat theo mô hình Client-Server, sử dụng giao thức TCP.

- Đơn vị truyền thông tin là Command.

- Sử dụng các namespace chính là:

o System.Net;

o System.Net.Sockets;

o System.CompomentModel;

II Hướng dẫn chi tiết:

1 Lập trình Server:

Bước 1: vào Visual C# tạo 1 project mới có tên là UITChatServer, có dạng là

Windows Form Đặt tên cho class chính là frmServer Tiếp tục trên Solution hiện tại tạo thêm 2 project có dạng Class Library đặt tên là Command và Server.

Bước 2: vào project Command tạo 2 file có tên là Enum.cs và Command.cs.

Bước 3: mở file Enum.cs nhập vào đoạn code sau đây để định nghĩa các loại

command sẽ dùng, khi muốn update thì cần chú ý nội dụng của file này.

{

Message,// Để gửi message

ClientList,//Để gửi danh sách client online

NameExists,// thông báo tên vừa đăng nhập đã có người sử dụng

Login, //thông báo đăng nhập

Logout,//thông báo đăng xuất

}

Bươc 4: Mở file Command.cs và nhập vào đoạn code sau dùng định nghĩa 1 đối

tượng command dùng để truyền trên mạng, gồm có CommandType, SenderName, SenderIP, TargerIP,

Trang 2

{

private IPAddress senderIP;// địa chỉ IP máy gửi

public IPAddress SenderIP

{

get { return senderIP; }

set { senderIP = value; }

}

public string SenderName

{

get { return senderName; }

set { senderName = value; }

}

private CommandType cmdType;//loại Command được gửi

public CommandType CommandType

{

get { return cmdType; }

set { cmdType = value; }

}

private IPAddress target;// địa chỉ IP máy nhận

public IPAddress Target

{

get { return target; }

set { target = value; }

}

private string commandBody;//nội dung cần gửi

public string MetaData

{

get { return commandBody; }

set { commandBody = value; }

}

// 2 contructor

public Command(CommandType type, IPAddress targetMachine, string

metaData)

{

this.cmdType = type;

this.target = targetMachine;

this.commandBody = metaData;

}

public Command(CommandType type, IPAddress targetMachine)

{

this.cmdType = type;

this.target = targetMachine;

this.commandBody = "";

}

}

}

Bươc 5: Chuyển sang project Server Ta Reference đến project Command Tạo 3

file là ClientManager.cs , Server.cs và EventArgs.cs

Trang 3

Bước 6: Mở file EventArgs.cs, nhập vào nội dung sau, định nghĩa các class thừa kế

từ class EventArg và các delegate để dùng khai báo các Event trong 2 file còn lại.

using System;

using System.Net;

using System.Net.Sockets;

using UITChat;//chú ý phải using đến class command

namespace Server

{

public delegate void UserOnlineHandler(object sender, UserEventArg user);

public delegate void UserLogoutHandler(object sender, UserEventArg user);

public delegate void CommandReceivedEventHandler(object sender,CommandEventArgs e); public delegate void DisconnectedEventHandler(object sender, ClientEventArgs e); public class UserEventArg : EventArgs

{

private string _userName;//tên người dùng

private IPAddress _ipaddress;//địa chỉ IP

public string UserName

{

get

{

return _userName;

}

set

{

_userName = value;

}

}

public UserEventArg(string Name)

{

_ipaddress = null;

_userName = Name;

}

public UserEventArg(IPAddress address, string Name)

{

_ipaddress = address;

_userName = Name;

}

}

public class CommandEventArgs : EventArgs

{

private Command command;// đối tượng Command

public Command Command

{

get { return command; }

}

public CommandEventArgs(Command cmd)

{

this.command = cmd;

}

}

public class ClientEventArgs : EventArgs

{

private Socket socket; //Socket của client

public IPAddress IP

{

get { return ((IPEndPoint)this.socket.RemoteEndPoint).Address; }

}

public int Port

{

Trang 4

get { return ((IPEndPoint)this.socket.RemoteEndPoint).Port; }

}

public ClientEventArgs(Socket clientManagerSocket)

{

this.socket = clientManagerSocket;

}

}

}

Bước 7: Mở file ClientManager.cs, ta tạo 1 class có tên là ClientManager Trong

class này ta khai báo 1 số thuộc tính và contructor như sau:

{

get

{

try

{

return ((IPEndPoint)this.socket.RemoteEndPoint).Address;

}

catch

{

return IPAddress.None;

}

}

}

{

get

{

if (this.socket != null)

return ((IPEndPoint)this.socket.RemoteEndPoint).Port;

else

return -1;

}

}

{

set { this.clientName = value; }

}

{

this.socket = clientSocket;

this.networkStream = new NetworkStream(this.socket);

this.bwReceiver = new BackgroundWorker();

this.bwReceiver.DoWork += new DoWorkEventHandler(Receive);

this.bwReceiver.RunWorkerAsync();

Trang 5

#endregion

Bước 8: Ta viết hàm Receive chạy trên Thread bwReceiver Chú ý ta dùng

Encoding để chuyển dữ liệu sang kiểu dữ liệu cũ trước khi Encode ở phía máy gửi Đây là hàm để chạy Event DoWorkEventHandler trong BackgroudWorker Một class kế thừa từ Thread, giúp làm việc trên Thread đơn giản hơn.

private void Receive(object sender, DoWorkEventArgs e)

{

while (this.socket.Connected)

{

try

{

//Đọc commandtype

byte[] buffer = new byte[4];

int readBytes = this.networkStream.Read(buffer, 0, 4);

if (readBytes == 0)

break;

CommandType cmdType = (CommandType)(BitConverter.ToInt32(buffer, 0)); //Đọc kích thước của SenderIP

buffer = new byte[4];

readBytes = this.networkStream.Read(buffer, 0, 4);

if (readBytes == 0)

break;

int senderIPSize = BitConverter.ToInt32(buffer, 0);

//Đọc senderIP

buffer = new byte[senderIPSize];

readBytes = this.networkStream.Read(buffer, 0, senderIPSize);

if (readBytes == 0)

break;

IPAddress senderIP =

IPAddress.Parse(System.Text.Encoding.ASCII.GetString(buffer));

//Đọc kích thước của SenderName

buffer = new byte[4];

readBytes = this.networkStream.Read(buffer, 0, 4);

if (readBytes == 0)

break;

int senderNameSize = BitConverter.ToInt32(buffer, 0);

//Đọc SenderName

buffer = new byte[senderNameSize];

readBytes = this.networkStream.Read(buffer, 0, senderNameSize);

if (readBytes == 0)

break;

string senderName = System.Text.Encoding.Unicode.GetString(buffer);

//Đọc kích thước của TargetIp

string cmdTarget = "";

buffer = new byte[4];

readBytes = this.networkStream.Read(buffer, 0, 4);

if (readBytes == 0)

break;

int ipSize = BitConverter.ToInt32(buffer, 0);

//Đọc TargerIp

buffer = new byte[ipSize];

readBytes = this.networkStream.Read(buffer, 0, ipSize);

Trang 6

if (readBytes == 0)

break;

cmdTarget = System.Text.Encoding.ASCII.GetString(buffer);

//Đọc kích thước của Metadata

string cmdMetaData = "";

buffer = new byte[4];

readBytes = this.networkStream.Read(buffer, 0, 4);

if (readBytes == 0)

break;

int metaDataSize = BitConverter.ToInt32(buffer, 0);

//Đọc metadata

buffer = new byte[metaDataSize];

readBytes = this.networkStream.Read(buffer, 0, metaDataSize);

if (readBytes == 0)

break;

cmdMetaData = System.Text.Encoding.Unicode.GetString(buffer);

//đóng gói thành kiểu command

Command cmd = new Command(cmdType, IPAddress.Parse(cmdTarget), cmdMetaData); cmd.SenderIP = this.IP;

cmd.SenderName = this.ClientName;

//phát sự kiện đã nhận xong 1 command

this.OnCommandReceived(new CommandEventArgs(cmd));

}

catch

{

break;//gặp bất kỳ lỗi nào sẽ tự thoát

}

}

//phát sự kiện mất kết nối

this.OnDisconnected(new ClientEventArgs(this.socket));

//ngắt kết nối

this.Disconnect();

}

Bước 9: Ta viết hàm SendCommand để gửi 1 command đến Client mà nó quản lý

Quá trình gửi cũng được thực hiện trên 1 Thread riêng, là 1 đối tượng

BackgroundWorker Chú ý các hàm xử lý sự kiện cho đối tượng này được trình bày dưới hàm SendCommand.

{

if (this.socket != null && this.socket.Connected)

{

BackgroundWorker bwSender = new BackgroundWorker();

bwSender.DoWork += new DoWorkEventHandler(bwSender_DoWork);

bwSender.RunWorkerCompleted += new

RunWorkerCompletedEventHandler(bwSender_RunWorkerCompleted);

bwSender.RunWorkerAsync(cmd);

}

}

RunWorkerCompletedEventArgs e)

{

//dọn rác sau khi đã gửi xong

((BackgroundWorker)sender).Dispose();

Trang 7

GC.Collect();

}

{

Command cmd = (Command)e.Argument;

e.Result = this.SendCommandToClient(cmd);

}

//Sử dụng semaphor để tránh xung đột khi gửi

System.Threading.Semaphore semaphor = new System.Threading.Semaphore(1, 1);

{

try

{

semaphor.WaitOne();

//Type

buffer = BitConverter.GetBytes((int)cmd.CommandType);

this.networkStream.Write(buffer, 0, 4);

this.networkStream.Flush();

//Sender IP

byte[] senderIPBuffer = Encoding.ASCII.GetBytes(cmd.SenderIP.ToString()); buffer = new byte[4];

buffer = BitConverter.GetBytes(senderIPBuffer.Length);

this.networkStream.Write(buffer, 0, 4);

this.networkStream.Flush();

this.networkStream.Write(senderIPBuffer, 0, senderIPBuffer.Length);

this.networkStream.Flush();

//Sender Name

byte[] senderNameBuffer =

Encoding.Unicode.GetBytes(cmd.SenderName.ToString());

buffer = new byte[4];

buffer = BitConverter.GetBytes(senderNameBuffer.Length);

this.networkStream.Write(buffer, 0, 4);

this.networkStream.Flush();

this.networkStream.Write(senderNameBuffer, 0, senderNameBuffer.Length); this.networkStream.Flush();

//Target

byte[] ipBuffer = Encoding.ASCII.GetBytes(cmd.Target.ToString());

buffer = new byte[4];

buffer = BitConverter.GetBytes(ipBuffer.Length);

this.networkStream.Write(buffer, 0, 4);

this.networkStream.Flush();

this.networkStream.Write(ipBuffer, 0, ipBuffer.Length);

this.networkStream.Flush();

//Meta Data

if (cmd.MetaData == null || cmd.MetaData == "")

cmd.MetaData = "\n";

byte[] metaBuffer = Encoding.Unicode.GetBytes(cmd.MetaData);

buffer = new byte[4];

buffer = BitConverter.GetBytes(metaBuffer.Length);

this.networkStream.Write(buffer, 0, 4);

this.networkStream.Flush();

Trang 8

this.networkStream.Write(metaBuffer, 0, metaBuffer.Length);

this.networkStream.Flush();

semaphor.Release();

return true;

}

{

semaphor.Release();

return false;

}

}

Bước 10: Tiếp tục viết thêm hàm Disconnect để ngắt kết nối, đóng socket và các

Event

{

if (this.socket != null && this.socket.Connected)

{

try

{

this.socket.Shutdown(SocketShutdown.Both);

this.socket.Close();

return true;

}

catch

{

return false;

}

}

else

return true;

}

{

if (CommandReceived != null)

CommandReceived(this, e);

}

{

if (Disconnected != null)

Disconnected(this, e);

}

Bước 11: Chuyển qua file Server.cs và lập trình các chức năng cơ bản cho 1 server

chat Bao gồm forward tin nhắn, quản lý các client, và các hàm hỗ trợ.

Trước tiên là khai báo các thuộc tính như sau:

Trang 9

private IPAddress serverIp;//địa chỉ IP của server

Và Contructor.

{

serverIp = Dns.GetHostAddresses(Dns.GetHostName())[0];

listClient = new List<ClientManager>();

}

Bước 12: Ta viết hàm chạy Server Chú ý server chạy trên Thread độc lập dựa vào

đối tượng BackgroundWorker Có 1 số hàm private kèm theo để quản lý listClient như

thêm, xóa các client khi mất kết nối, login hay logout.

public void Start()

{

bwServer = new BackgroundWorker();

bwServer.DoWork += new DoWorkEventHandler(bwServer_DoWork);

bwServer.RunWorkerAsync();

}

private void bwServer_DoWork(object sender, DoWorkEventArgs e)

{

listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,

ProtocolType.Tcp);

IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Any, port);

listenerSocket.Bind(serverEndPoint);

listenerSocket.Listen(200);

while (true)

{

//tao 1 ClientManager mới bất kỳ khi nào có kêt nối mới

ClientManager newClient = new ClientManager(listenerSocket.Accept());

newClient.CommandReceived += new CommandReceivedEventHandler(newClient_CommandReceived); newClient.Disconnected += new DisconnectedEventHandler(newClient_Disconnected);

//xóa client trong list nếu IP của nó đã được lưu trong list

this.RemoveClientManager(newClient.IP);

//thêm Client mới vào list

listClient.Add(newClient);

}

}

//Dọn dẹp, xóa client trong list nếu server mất kêt nối với nó và gửi clientlist mới

private void newClient_Disconnected(object sender, ClientEventArgs e)

{

ClientManager client = (ClientManager)sender;

listClient.Remove(client);

UserEventArg userEvent = new UserEventArg(client.ClientName);

SendUserOnlineList();

}

private void newClient_CommandReceived(object sender, CommandEventArgs e)

{

ClientManager client = (ClientManager)sender;

Command cmd = e.Command;

switch (cmd.CommandType)

{

// khi đang nhập

case CommandType.Login:

{

Trang 10

//đặt tên cho Client mới bằng tên đã đăng nhập

string name = SetManagerName(cmd.SenderIP, cmd.MetaData);

//kiểm tra xem tên có trùng với tên đã có không

if (!IsNameExists(cmd.SenderIP, name))

{

//nêu ko thi gửi clientlist mới cho tất cả các máy

SendUserOnlineList();

}

else

{

//nếu có thì gửi thông báo là tên đã có người khác dùng

Command sendcmd = new Command(CommandType.NameExists, cmd.SenderIP);

sendcmd.SenderIP = serverIp;

sendcmd.SenderName = "server";

client.SendCommand(sendcmd);

}

break;

}

//khi đang xuât

case CommandType.Logout:

{

SendUserOnlineList();

break;

}

//forward tin nhắn

case CommandType.Message:

{

string targetname = cmd.MetaData.Split(new char[] { '|' })[0]; SendCommandToTarget(targetname, cmd);

break;

}

}

}

//Đặt tên cho Client

private string SetManagerName(IPAddress remoteClientIP, string nameString)

{

int index = this.IndexOfClient(remoteClientIP);

if (index != -1)

{

string name = nameString.Split(new char[] { ':' })[0];

this.listClient[index].ClientName = name;

return name;

}

return "";

}

//Lấy vị trí của Client cho địa chỉ là IP trong list

private int IndexOfClient(IPAddress ip)

{

int index = -1;

foreach (ClientManager cMngr in listClient)

{

index++;

if (cMngr.IP.Equals(ip))

return index;

}

return -1;

}

//Xóa 1 client có địa chỉ là IP

private bool RemoveClientManager(IPAddress ip)

{

lock (this)

{

int index = this.IndexOfClient(ip);

Ngày đăng: 25/08/2015, 12:26

TỪ KHÓA LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w