اتصال TCP
أساس TCP هو المقبس. في اتصال TCP، سنستخدم ServerSocket وSocket بعد إنشاء اتصال بين العميل والخادم، والباقي هو في الأساس التحكم في الإدخال / الإخراج.
دعونا نلقي نظرة أولاً على اتصال TCP البسيط، والذي ينقسم إلى عميل وخادم.
رمز العميل هو كما يلي:
يلقي الفراغ الرئيسي العام (String[] args) IOException
{
مأخذ التوصيل = فارغ؛
BufferedReader br = null;
PrintWriter pw = null;
BufferedReader brTemp = null;
يحاول
{
المقبس = جديد المقبس (InetAddress.getLocalHost(), 5678);
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
brTemp = new BufferedReader(new InputStreamReader(System.in));
بينما (صحيح)
{
خط السلسلة = brTemp.readLine();
pw.println(line);
pw.flush();
إذا (line.equals("end")) استراحة؛
System.out.println(br.readLine());
}
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
}
أخيراً
{
if (socket != null) المقبس.إغلاق();
if (br != null) br. Close();
if (brTemp != null) brTemp. Close();
if (pw != null) pw. Close();
}
}
}
يلقي الفراغ الرئيسي العام (String[] args) IOException
{
خادم ServerSocket = فارغ؛
عميل المقبس = فارغ؛
BufferedReader br = null;
PrintWriter pw = null;
يحاول
{
server = new ServerSocket(5678);
العميل = الخادم. قبول ()؛
br = new BufferedReader(new InputStreamReader(client.getInputStream()));
pw = new PrintWriter(client.getOutputStream());
بينما (صحيح)
{
خط السلسلة = br.readLine();
pw.println("الاستجابة:" + خط);
pw.flush();
إذا (line.equals("end")) استراحة؛
}
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
}
أخيراً
{
إذا (الخادم! = فارغ) server. Close();
إذا (العميل! = فارغ) client.Close();
if (br != null) br. Close();
if (pw != null) pw. Close();
}
}
}
يوضح الكود أعلاه بشكل أساسي الإطار الرئيسي للعميل والخادم أثناء عملية اتصال TCP. ويمكننا أن نجد أنه في الكود أعلاه، يمكن للخادم معالجة طلب واحد فقط من العميل في أي وقت لا يمكن موازنتها. هذه ليست نفس طريقة معالجة الخادم في انطباعنا. يمكننا إضافة سلاسل رسائل متعددة إلى الخادم، عندما يأتي طلب العميل، نقوم بإنشاء سلسلة رسائل لمعالجة الطلب المقابل.
الكود المحسن من جانب الخادم هو كما يلي:
فئة ServerThread تمتد الموضوع
{
مقبس المقبس الخاص = فارغ؛
موضوع الخادم العام (مأخذ التوصيل)
{
this.socket = المقبس;
}
تشغيل الفراغ العام () {
BufferedReader br = null;
PrintWriter pw = null;
يحاول
{
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
بينما (صحيح)
{
خط السلسلة = br.readLine();
pw.println("الاستجابة:" + خط);
pw.flush();
إذا (line.equals("end")) استراحة؛
}
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
}
أخيراً
{
إذا (المقبس! = فارغ)
يحاول {
المقبس. إغلاق ()؛
} التقاط (IOException e1) {
e1.printStackTrace();
}
إذا (ر != فارغة)
يحاول {
br. Close();
} قبض (IOException ه) {
printStackTrace();
}
if (pw != null) pw. Close();
}
}
}
في عملية البرمجة، لدينا مفهوم "المورد"، على سبيل المثال، يعد اتصال قاعدة البيانات موردًا نموذجيًا، ومن أجل تحسين الأداء، لا نقوم عادة بتدمير اتصال قاعدة البيانات مباشرة، ولكننا نستخدم تجمع اتصال قاعدة البيانات لإدارة عدة تتم إدارة الاتصالات وإعادة استخدامها. بالنسبة لاتصالات المقبس، فهي أيضًا مورد. عندما يتطلب برنامجنا عددًا كبيرًا من اتصالات المقبس، سيكون هذا أسلوبًا غير فعال للغاية إذا كانت هناك حاجة إلى إعادة إنشاء كل اتصال.
على غرار تجمع اتصال قاعدة البيانات، يمكننا أيضًا تصميم تجمع اتصال TCP. الفكرة هنا هي أننا نستخدم مصفوفة للحفاظ على اتصالات مأخذ توصيل متعددة، ومصفوفة حالة أخرى لوصف ما إذا كان كل اتصال مأخذ توصيل قيد الاستخدام عندما يتطلب البرنامج اتصال المقبس، نجتاز مصفوفة الحالة ونخرج أول اتصال مقبس غير مستخدم، إذا كانت جميع الاتصالات قيد الاستخدام، فسيتم طرح استثناء. هذه "إستراتيجية جدولة" بديهية وبسيطة للغاية في العديد من الأطر مفتوحة المصدر أو التجارية (Apache/Tomcat)، ستكون هناك "مجموعات موارد" مماثلة.
رمز تجمع اتصال TCP كما يلي:
عنوان InetAddress الخاص = فارغ؛
ميناء خاص؛
مقبس خاص[] arrSockets = null;
منطقية خاصة[] arrStatus = null;
عدد صحيح خاص؛
TcpConnectionPool العام (عنوان InetAddress، منفذ int، عدد int)
{
this.address = عنوان;
this.port = port;
هذا .count = العد؛
arrSockets = new المقبس[count];
arrStatus = new boolean[count];
الحرف الأول () ؛
}
الحرف الأول باطلة خاصة ()
{
يحاول
{
ل(int i = 0; i <count; i++)
{
arrSockets[i] = new المقبس(address.getHostAddress(), port);
arrStatus[i] = false;
}
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
}
}
getConnection المقبس العام ()
{
if (arrSockets == null) init();
كثافة العمليات ط = 0؛
ل(i = 0; i < العد; i++)
{
إذا (arrStatus[i] == خطأ)
{
arrStatus[i] = true;
استراحة؛
}
}
إذا (i == count) throw new RuntimeException("ليس هناك اتصال متاح في الوقت الحالي.");
إرجاع arrSockets[i];
}
اتصال إطلاق الفراغ العام (مأخذ التوصيل)
{
if (arrSockets == null) init();
ل(int i = 0; i <count; i++)
{
إذا (arrSockets[i] == المقبس)
{
arrStatus[i] = false;
استراحة؛
}
}
}
إعادة بناء الفراغ العام ()
{
الحرف الأول () ؛
}
تدمير الفراغ العام ()
{
if (arrSockets == null) return;
ل(int i = 0; i <count; i++)
{
يحاول
{
arrSockets[i].Close();
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
يكمل؛
}
}
}
}
UDP هي طريقة اتصال مختلفة عن TCP، وتُستخدم عادةً في المواقف التي تتطلب أداءً عاليًا في الوقت الفعلي ومتطلبات منخفضة للدقة، مثل الفيديو عبر الإنترنت. سيكون لـ UDP "فقدان الحزمة". في TCP، إذا لم يتم تشغيل الخادم، فسيتم الإبلاغ عن استثناء عندما يرسل العميل رسالة، ولكن بالنسبة لـ UDP، لن يتم إنشاء أي استثناء.
الفئتان المستخدمتان لاتصالات UDP هما DatagramSocket وDatagramPacket، حيث يقوم الأخير بتخزين محتوى الاتصال.
فيما يلي مثال بسيط لاتصالات UDP، مثل TCP، وهو مقسم أيضًا إلى قسمين: العميل والخادم.
الفراغ العام الثابت الرئيسي (String[] args)
{
يحاول
{
InetAddress host = InetAddress.getLocalHost();
منفذ إنت = 5678؛
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
بينما (صحيح)
{
خط السلسلة = br.readLine();
byte[] message = line.getBytes();
DatagramPacket packet = new DatagramPacket(message, message.length, host, port);
DatagramSocket المقبس = new DatagramSocket();
المقبس.إرسال(packet);
المقبس. إغلاق ()؛
إذا (line.equals("end")) استراحة؛
}
br. Close();
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
}
}
}
الفراغ العام الثابت الرئيسي (String[] args)
{
يحاول
{
منفذ إنت = 5678؛
DatagramSocket dsSocket = new DatagramSocket(port);
بايت[] المخزن المؤقت = بايت جديد[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
بينما (صحيح)
{
dsSocket.receive(packet);
String message = new String(buffer, 0, packet.getLength());
System.out.println(packet.getAddress().getHostName() + ":: + message);
إذا (message.equals("end")) استراحة؛
packet.setLength(buffer.length);
}
dsSocket. Close();
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
}
}
}
يستخدم البث المتعدد طريقة مشابهة لـ UDP. فهو يستخدم عناوين IP من الفئة D وتشير أرقام منافذ UDP القياسية إلى العناوين الواقعة بين 224.0.0.0 و239.255.255.255، باستثناء 224.0.0.0.
الفئة المستخدمة في البث المتعدد هي MulticastSocket، والتي لديها طريقتان يجب الانتباه إليهما: joinGroup و leaveGroup.
فيما يلي مثال على البث المتعدد، رمز العميل كما يلي:
الفراغ العام الثابت الرئيسي (String[] args)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
يحاول
{
عنوان InetAddress = InetAddress.getByName("230.0.0.1");
منفذ إنت = 5678؛
بينما (صحيح)
{
خط السلسلة = br.readLine();
byte[] message = line.getBytes();
DatagramPacket packet = new DatagramPacket(message, message.length, Address, port);
MulticastSocket multicastSocket = new MulticastSocket();
multicastSocket.send(packet);
إذا (line.equals("end")) استراحة؛
}
br. Close();
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
}
}
}
الفراغ العام الثابت الرئيسي (String[] args)
{
منفذ إنت = 5678؛
يحاول
{
MulticastSocket multicastSocket = new MulticastSocket(port);
عنوان InetAddress = InetAddress.getByName("230.0.0.1");
multicastSocket.joinGroup(address);
بايت[] المخزن المؤقت = بايت جديد[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
بينما (صحيح)
{
multicastSocket.receive(packet);
String message = new String(buffer, packet.getLength());
System.out.println(packet.getAddress().getHostName() + ":: + message);
إذا (message.equals("end")) استراحة؛
packet.setLength(buffer.length);
}
multicastSocket. Close();
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
}
}
}
NIO عبارة عن مجموعة جديدة من IO API تم تقديمها في JDK1.4 ولديها تصميمات جديدة في إدارة المخزن المؤقت واتصالات الشبكة والوصول إلى الملفات وعمليات مجموعة الأحرف. للاتصال بالشبكة، يستخدم NIO مفاهيم المخازن المؤقتة والقنوات.
فيما يلي مثال على NIO، والذي يختلف تمامًا عن أسلوب البرمجة الذي ذكرناه أعلاه.
الفراغ العام الثابت الرئيسي (String[] args)
{
مضيف السلسلة = "127.0.0.1"؛
منفذ إنت = 5678؛
قناة المقبس = فارغة؛
يحاول
{
عنوان InetSocketAddress = جديد InetSocketAddress(host,port);
مجموعة محارف الأحرف = Charset.forName("UTF-8");
وحدة فك ترميز CharsetDecoder = charset.newDecoder();
تشفير CharsetEncoder = charset.newEncoder();
ByteBuffer buffer = ByteBuffer.allocate(1024);
CharBuffer charBuffer = CharBuffer.allocate(1024);
القناة = SwitchChannel.open();
channel.connect(address);
طلب السلسلة = "GET / /r/n/r/n"؛
channel.write(encoder.encode(CharBuffer.wrap(request)));
بينما ((channel.read(buffer)) != -1)
{
buffer.flip();
decoder.decode(buffer, charBuffer, false);
charBuffer.flip();
System.out.println(charBuffer);
buffer.clear();
charBuffer.clear();
}
}
قبض (استثناء على سبيل المثال)
{
System.err.println(ex.getMessage());
}
أخيراً
{
إذا (القناة! = فارغة)
يحاول {
قناة. إغلاق ()؛
} قبض (IOException ه) {
// TODO كتلة الالتقاط التي تم إنشاؤها تلقائيًا
printStackTrace();
}
}
}
}