Socket hướng kết nối TCP Socket Socket là một giao diện lập trình ứng dụng API mạng Thông qua giao diện này chúng ta có thể lập trình ñiều khiển việc truyền thông giữa hai máy sử dụng c
Trang 1CHƯƠNG 2: LẬP TRÌNH MẠNG TRONG NET FRAMEWORK
2.1 Socket hướng kết nối (TCP Socket)
Socket là một giao diện lập trình ứng dụng (API) mạng
Thông qua giao diện này chúng ta có thể lập trình ñiều khiển việc truyền thông giữa hai máy sử dụng các giao thức mức thấp là TCP, UDP…
Socket là sự trừu tượng hoá ở mức cao, có thể tưởng tượng nó như là thiết bị truyền thông hai chiều gửi – nhận dữ liệu giữa hai máy tính với nhau
Các loại Socket
Socket hướng kết nối (TCP Socket)
Socket không hướng kết nối (UDP Socket)
Raw Socket
ðặc ñiểm của Socket hướng kết nối
Có 1 ñường kết nối ảo giữa 2 tiến trình
Một trong 2 tiến trình phải ñợi tiến trình kia yêu cầu kết nối
Có thể sử dụng ñể liên lạc theo mô hình Client/Server
Trong mô hình Client/Server thì Server lắng nghe và chấp nhận một yêu cầu kết nối
Mỗi thông ñiệp gửi ñều có xác nhận trở về
Các gói tin chuyển ñi tuần tự
ðặc ñiểm của Socket không hướng kết nối
Hai tiến trình liên lạc với nhau không kết nối trực tiếp
Thông ñiệp gửi ñi phải kèm theo ñịa chỉ của người nhận
Thông ñiệp có thể gửi nhiều lần
Người gửi không chắc chắn thông ñiệp tới tay người nhận
Thông ñiệp gửi sau có thể ñến ñích trước thông ñiệp gửi trước ñó
Số hiệu cổng của Socket
Trang 2ðể có thể thực hiện các cuộc giao tiếp, một trong hai quá trình phải công
bố số hiệu cổng của socket mà mình sử dụng
Mỗi cổng giao tiếp thể hiện một ñịa chỉ xác ñịnh trong hệ thống Khi quá trình ñược gán một số hiệu cổng, nó có thể nhận dữ liệu gởi ñến cổng này từ các quá trình khác
Quá trình còn lại cũng yêu cầu tạo ra một socket
2.1.1 Giới thiệu về NameSpace System.Net và System.Net.Sockets
Cung cấp một giao diện lập trình ñơn giản cho rất nhiều các giao thức mạng
IsLoopback: Cho biết ñịa chỉ có phải ñịa chỉ lặp không
Parse: Chuyển IP dạng xâu về IP chuẩn
ToString: Trả ñịa chỉ IP về dạng xâu
TryParse: Kiểm tra IP ở dạng xâu có hợp lệ không?
Lớp IPEndPoint
Một số phương thức cần chú ý:
Phương thức khởi tạo
IPEndPoint (Int64, Int32)
IPEndPoint (IPAddress, Int32)
Create: Tạo một EndPoint từ một ñịa chỉ Socket
ToString : Trả về ñịa chỉ IP và số hiệu cổng theo khuôn dạng ðịaChỉ: Cổng, ví dụ: 192.168.1.1:8080
Trang 3GetHostName: Lấy về tên của máy tính cục bộ
NameSpace System.Net.Sockets
Một số lớp hay dùng: TcpClient, UdpClient, TcpListener, Socket,
NetworkStream, …
ðể tạo ra Socket
Socket(AddressFamily af, SocketType st, ProtocolType pt)
SocketType Protocoltype Description
IPEndPoint ie = new IPEndPoint(ia, 8000);
Socket test = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint iep = (IPEndPoint)test.LocalEndPoint;
Console.WriteLine("Local EndPoint: {0}", iep.ToString());
test.Close();
Console.ReadKey();
}
}
2.1.2 Viết chương trình cho phía máy chủ
Viết chương trình cho phía máy chủ
Trang 4đóng kết nối sau khi ựã hoàn thành và trở lại trạng thái lắng nghe chờ kết nối mới
Viết chương trình cho phắa máy chủ
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
Socket newsock = Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
newsock.Bind(ipep);
newsock.Listen(10);
Socket client = newsock.Accept();
//Gửi nhận dữ liệu theo giao thức ựã thiết kế
static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2008);
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(iep);
server.Listen(10);
Console.WriteLine("Cho ket noi tu client");
Socket client = server.Accept();
Console.WriteLine("Chap nhan ket noi tu:{0}",
client.RemoteEndPoint.ToString());
string s = "Chao ban den voi Server";
//Chuyen chuoi s thanh mang byte
byte[] data = new byte[1024];
data = Encoding.ASCII.GetBytes(s);
//gui nhan du lieu theo giao thuc da thiet ke
client.Send(data,data.Length,SocketFlags.None);
while (true) {
data = new byte[1024];
int recv = client.Receive(data);
if (recv == 0) break;
//Chuyen mang byte Data thanh chuoi va in ra man hinh
s = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine("Clien gui len:{0}", s);
//Neu chuoi nhan duoc la Quit thi thoat
Trang 52.1.3 Viết chương trình cho phắa máy khách
Viết chương trình cho phắa máy khách
Xác ựịnh ựịa chỉ của Server
Tạo Socket
Kết nối ựến Server
Gửi nhận dữ liệu theo giao thức ựã thiết kế
đóng Socket
Viết chương trình cho phắa máy khách
IPEndPoint ipep = new IPEndPoint(Ipaddress.Parse("192.168.1.6"), 9050);
Socket server = new Socket(AddressFamily.InterNetwork,SocketType.Stream,
static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2008);
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(iep);
byte[] data = new byte[1024];
int recv = client.Receive(data);
string s = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine("Server gui:{0}", s);
string input;
while (true) {
input = Console.ReadLine();
//Chuyen input thanh mang byte gui len cho server
data = new byte[1024];
Trang 62.1.4 Sử dụng các luồng nhập xuất với Socket
Từ Socket ta có thể tạo ra luồng ñể nhập xuất với Socket ñó là sử dụng lớp NetworkStream
DataAvailable Is true if there is data available to be read
Ví dụ chương trình Client/Server sử dụng NetworkStream ñể gửi và nhận dữ liệu Chương trình Client sử dụng NetworkStream:
Trang 7class Program {
static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2009);
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(iep);
NetworkStream ns = new NetworkStream(client);
byte[] data = new byte[1024];
data = new byte[1024];
int rec = ns.Read(data, 0, data.Length);
string s = Encoding.ASCII.GetString(data, 0, rec);
static void Main(string[] args) {
IPEndPoint iep=new IPEndPoint(IPAddress.Parse("127.0.0.1"),2009);
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
data = new byte[1024];
int rec = ns.Read(data, 0, data.Length);
string s = Encoding.ASCII.GetString(data, 0, rec);
Trang 8client.Close();
server.Close();
}
}
Trên cở sở của NetworkStream ta có thể nối thêm các luồng ñể nhập xuất theo hướng
ký tự như StreamReader, StreamWriter
Sau ñây là một ví dụ về chương trình Client/Server sử dụng luồng nhập xuất, chương trình Server chép phép Client gửi lên yêu cầu, nếu yêu cầu là GetDate không phân biệt chữ hoa chữ thường thì Server trả về cho Client ngày hiện tại, nếu yêu cầu là GetTime không phan biệt hoa thường thì Server trả về giờ hiện tại, nếu là Quit thì Server ngắt kết nối với Client, không phải các trường hợp trên thì thông báo không hiểu lênh Chương trình phía Client:
static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp); client.Connect(iep);
NetworkStream ns = new NetworkStream(client);
StreamReader sr = new StreamReader(ns);
StreamWriter sw = new StreamWriter(ns);
Trang 9using System.Net.Sockets;
using System.IO;
class DateTimeServer{
static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2009);
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp); server.Bind(iep);
server.Listen(10);
Socket client = server.Accept();
NetworkStream ns = new NetworkStream(client);
StreamReader sr = new StreamReader(ns
StreamWriter sw = new StreamWriter(ns);
2.2 Socket không hướng kết nối (UDP Socket)
Chương trình phía máy chủ
Tạo một Socket
Liên kết với một IPEndPoint cục bộ
Gửi nhận dữ liệu theo giao thức ñã thiết kế
Trang 10static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2008);
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
server.Bind(iep);
//tao ra mot Endpot tu xa de nhan du lieu ve
IPEndPoint RemoteEp = new IPEndPoint(IPAddress.Any, 0);
EndPoint remote=(EndPoint)RemoteEp;
byte[] data = new byte[1024];
int recv = server.ReceiveFrom(data, ref remote);
string s = Encoding.ASCII.GetString(data, 0, recv);
static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2008);
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
String s = "Chao server";
byte[] data = new byte[1024];
data = Encoding.ASCII.GetBytes(s);
client.SendTo(data, iep);
EndPoint remote = (EndPoint)iep;
Trang 11data = new byte[1024];
int recv = client.ReceiveFrom(data, ref remote);
data = new byte[1024];
recv = client.ReceiveFrom(data, ref remote);
public partial class Form1 : Form {
private Socket udp1;
private IPEndPoint ipremote, iplocal;
public Form1() {
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
private void btStart_Click(object sender, EventArgs e) {
udp1 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
Trang 12}
private void btSend_Click(object sender, EventArgs e) {
byte[] data = new byte[1024];
byte[] data = new byte[1024];
IPEndPoint ipe = new IPEndPoint(IPAddress.Any, 0);
EndPoint remote = (EndPoint)ipe;
int rec = udp1.ReceiveFrom(data, ref remote);
string s = Encoding.ASCII.GetString(data, 0, rec);
2.2.3 Sử dụng lớp System.IO.MemoryStream ñể tạo vùng ñệm nhập xuất
2.3 Sử dụng các lớp hỗ trợ ñược xây dựng từ lớp Soket
Trang 13+ Một số thuộc tính:
Name Description
Available Cho biết số byte ñã nhận về từ mạng và có sẵn
ñể ñọc
Client Trả về Socket ứng với TCPClient hiện hành
Connected Trạng thái cho biết ñã kết nối ñược ñến Server hay chưa ?+ Một số phương thức:
Trang 14GetStream Trả về NetworkStream ñể từ ñó giúp ta
gửi hay nhận dữ liệu (Thường làm tham
số khi tạo StreamReader và StreamWriter)
Khi ñã gắn vào StreamReader và StreamWriter rồi thì ta có thể gửi và nhận
dữ liệu thông qua các phương thức Readln, writeline tương ứng của các lớp này
Ta sử dụng lớp TcpClient viết lại chương trình DateTimeClient như sau:
static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);
TcpClient client = new TcpClient();
client.Connect(iep);
StreamReader sr = new StreamReader(client.GetStream());
StreamWriter sw = new StreamWriter(client.GetStream());
Trang 15AcceptSocket Chấp nhận một yêu cầu kết nối ñang chờ
AcceptTcpClient Chấp nhận một yêu cầu kết nối ñang chờ (Ứng dụng sẽ
dừng tại lệnh này cho ñến khi nào có một kết nối ñến) Pending Cho biết liệu có kết nối nào ñang chờ ñợi không ? (True
= có)
Start Bắt ñầu lắng nghe các yêu cầu kết nối
Stop Dừng việc nghe
Sử dụng lớp TcpListener ta viết lại chương trình DateTime Server như sau: using System;
static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2009);
TcpListener server = new TcpListener(iep);
server.Start();
TcpClient client = server.AcceptTcpClient();
StreamReader sr = new StreamReader(client.GetStream());
StreamWriter sw = new StreamWriter(client.GetStream());
string kq="";
while (true) {
Trang 16Trong NET, lớp UDPClient (nằm trong System.Net.Sockets) đóng gói các chức năng của giao thức UDP
Constructor methosd Description
UdpClient (Int32, AddressFamily)
Tạo một UdpClient và gán số hiệu cổng, AddressFamily
Trang 17UdpClient (String, Int32)
Tạo một UdpClient và thiết lập với một trạm từ xa mặc định
PUBLIC Method
BeginReceive Nhận dữ liệu Không đồng bộ từ máy ở xa
BeginSend Gửi không đồng bộ dữ liệu tới máy ở xa
Close Đóng kết nối
Connect Thiết lập một Default remote host
EndReceive Kết thúc nhận dữ liệu không đồng bộ ở trên
EndSend Kết thúc việc gửi dữ liệu không đồng bộ ở trên
Receive Nhận dữ liệu (đồng bộ) do máy ở xa gửi (Đồng bộ có
nghĩa là các lệnh ngay sau lệnh Receive chỉ đ−ợc thực thi nếu Receive đ9 nhận đ−ợc dữ liệu về Còn nếu nó ch−a nhận
đ−ợc – dù chỉ một chút – thì nó vẫn cứ chờ (blocking)) Send Gửi dữ liệu (đồng bộ) cho máy ở xa
Vớ dụ sử dụng UdpClient viết chương trỡnh Chat giữa 2 mỏy:
Do chương trỡnh ở 2 mỏy là như nhau ta chỉ cần viết một chương trỡnh copy ra ủể sử dụng
Hỡnh ảnh của nú như sau:
Trang 18private void btSend_Click(object sender, EventArgs e) {
UdpClient send = new UdpClient();
IPEndPoint iepRemote = new IPEndPoint(IPAddress.Parse(txtIp.Text),
int.Parse(txtRemotePort.Text));
byte[] data = new byte[1024];
data = Encoding.UTF8.GetBytes(txtSend.Text);
send.Send(data, data.Length, iepRemote);
txtReceive.Text += "Sender: "+txtSend.Text + "\r\n";
txtSend.Clear();
if (txtSend.Text.ToUpper().Equals("QUIT")) this.Dispose();
}
private void btConnect_Click(object sender, EventArgs e) {
Thread tuyen = new Thread(new ThreadStart(NhanDl));
tuyen.Start();
}
private void NhanDl() {
UdpClient receiver = new UdpClient(int.Parse(txtLocalPort.Text));
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 0);
while (true) {
byte[] data = new byte[1024];
data = receiver.Receive(ref iep);
Trang 19Mô hình xử lý sự kiện của Windows ñược thể hiện qua hình sau:
Như vậy thông qua mô hình này ta có thể ủy nhiệm cho một thủ tục nào ñó thực hiện khi sự kiện sảy ra trên các Control
private void button1_Click(object sender, EventArgs e) {
MessageBox.Show("Bac da nhan em");
}
private void NhanTiep(object sender, EventArgs e) {
MessageBox.Show("Bac lai nhan em roi");
Trang 20Các phương thức cho việc lập trình bất ñồng bộ ñược chia làm 2 lại thường bắt ñầu bằng Begin và End:
Phương thức bắt ñầu bằng Begin, bắt ñầu một chức năng và ñược ñăng
ký với phương thức AsyncCallback
Bắt ñầu bằng End chỉ chức năng hoàn thành khi AsyncCallback ñược gọi
EndSendTo()
To send data to a specific remote hostBeginSendTo()
BeginConnect()
EndAccept()
To accept an incoming connection
BeginConnect()
EndAccept()
To accept an incoming connection
BeginAccept()
Requests Ended BY…Description of Request
Requests Started By…
- ðể chấp nhận kết nối bất ñồng bộ ta sử dụng phương thức BeginAccept() và EndAccept() như sau:
Phương thức BeginAccept() và EndAccept()
IAsyncResult BeginAccept(AsyncCallback callback, object state)
Socket EndAccept(IAsyncResult iar);
Thường ñược dùng như sau:
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp); IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
sock.Bind(iep);
sock.Listen(5);
sock.BeginAccept(new AsyncCallback(CallAccept), sock);
Trong ñó phương thức CallAccept thường ñược viết như sau:
private static void CallAccept(IAsyncResult iar) {
Socket server = (Socket)iar.AsyncState;
Socket client = server.EndAccept(iar);
}
- ðể thiết lập kết nối theo cách bất ñồng bộ chúng ta sử dụng phương thức BeginConnect() và EndConnect() như sau:
Phương thức BeginConnect() và EndConnect()
Trang 21Socket newsock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
IPEndPoint iep =new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);
newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock);
Trong ñó phương thức Connected thường ñược viết như sau:
public static void Connected(IAsyncResult iar) {
Socket sock = (Socket)iar.AsyncState;
- ðể gửi dữ liệu bất ñồng bộ chúng ta làm như sau:
+ Phương thức BeginSend() và EndSend()
+ BeginSend()
IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags sockflag,
AsyncCallback callback, object state)
private static void SendData(IAsyncResult iar) {
Socket server = (Socket)iar.AsyncState;
int sent = server.EndSend(iar);
}
Tương tự như giao thức hướng kết nối nếu ta sử dụng gửi dữ liệu theo giao thức không hướng kết nối chúng ta cũng thực hiện tương tự như sau:
Phương thức BeginSendTo() và EndSendTo()
IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags
sockflag, EndPoint ep, AsyncCallback callback, object state)
Ví dụ:
IPEndPoint iep = new EndPoint(IPAddress.Parse("192.168.1.6"), 9050);
sock.BeginSendTo(data, 0, data.Length, SocketFlags.None, iep, new
AsynCallback(SendDataTo), sock);
int EndSendTo(IAsyncResult iar)
- ðể nhận dữ liệu bất ñồng bộ ta thực hiện như sau:
+ Nhận dữ liệu với giao thức hướng kết nối:
Phương thức BeginReceive và EndReceive()
Trang 22sock.BeginReceive(data, 0, data.Length, SocketFlags.None, new
AsyncCallback(ReceivedData), sock);
Với ReceivedData ñược ñịnh nghĩa như sau:
void ReceivedData(IAsyncResult iar) {
Socket remote = (Socket)iar.AsyncState;
int recv = remote.EndReceive(iar);
string receivedData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(receivedData);
}
+ Nhận dữ liệu bất ñồng bộ với giao thức không hướng kết nối
Phương thức BeginReceiveFrom() and EndReceiveFrom()
sock.BeginReceive(data, 0, data.Length, SocketFlags.None, ref iep, new
AsyncCallback(ReceiveData), sock);
void ReceiveData(IasyncResult iar){
Socket remote = (Socket)iar.AsyncState;
int recv = remote.EndReceiveFrom(iar);
string stringData = Encoding.ASCII.GetString(data, 0, recv);
Trang 23Chương trình phía Client:
private TextBox newText;
private TextBox conStatus;
private ListBox results;
private Socket client;
private byte[] data = new byte[1024];
private int size = 1024;
public AsyncTcpClient()
{
Text = "Asynchronous TCP Client";
Size = new Size(400, 380);
Label label1 = new Label();
label1.Parent = this;
label1.Text = "Enter text string:";
label1.AutoSize = true;
label1.Location = new Point(10, 30);
newText = new TextBox();
newText.Parent = this;
newText.Size = new Size(200, 2 * Font.Height);
newText.Location = new Point(10, 55);
results = new ListBox();
results.Parent = this;
results.Location = new Point(10, 85);
results.Size = new Size(360, 18 * Font.Height);
Label label2 = new Label();
label2.Parent = this;