赞
踩
目录
2 Socket类与ServerSocket类编写一个C/S程序
掌握Socket的TCP通信、 Socket的UDP通信
Socket、ServerSocket类和DatagramPacket 、DatagramSocket类的使用
使用Windows操作系统;Internet连接
IDEA+Java开发环境
套接字是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口
套接字主要分为以下三种:
在具体使用套接字时,常常需要使用一对套接字而不是一个套接字,其中一个运行于客户端,即Client Socket,另一个运行于服务器端,即Server Socket。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:
① 服务器监听:所谓服务器监听,是指服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
② 客户端请求:所谓客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端接字提出连接请求。
③ 连接确认:所谓连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,就会响应客户端套接字的请求,建立一个新的线程,并把服务器端套接字的描述发送给客户端。一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,接收其他客户端套接字的连接请求。
此外,根据套接字的不同类型,可以将套接字调用分为面向连接服务和无连接服务。其中,面向连接的服务有着可靠性高,通信效率不高,数据传输过程必须经过建立连接、维护连接和释放连接3个阶段的特点,并且在传输过程中,各分组不需要携带目的主机的地址。而面向无连接的服务通信效率高,但可靠性相对较差,数据传输过程中不需要连接各个阶段,并且每个分组都携带完整的目的主机地址,在系统中独立传送。此外由于没有顺序控制,所以接收方的分组可能出现乱序、重复和丢失现象。
Java中提供了Socket和ServerSocket类作为标准的TCP套接字编程技术,通过它们实现主机与主机之间(应用程序间)的对话。这两个类位于:java.net包中。
ServerSocket 类是与 Socket 类相对应的用于表示通信双方中的服务器端,用于在服务器上开一个端口,被动地等待数据(使用 accept() 方法)并建立连接进行数据交互。服务器套接字一次可以与一个套接字连接,如果多台客户端同时提出连接请求,服务器套接字会将请求连接的客户端存入队列中,然后从中取出一个套接字与服务器新建的套接字连接起来。若请求连接大于最大容纳数,则多出的连接请求被拒绝;默认的队列大小是 50。ServerSocket类提供了如下的构造方法:
在上述方法的参数中 port 指的是本地 TCP 端口,backlog指的是监听backlog,bindAddr 指的是要将服务器绑定到的 InetAddress。此外,创建 ServerSocket 时可能会拋出 IOException 异常,所以要进行异常捕捉。
ServerSocket类有如下的常用方法:
调用 accept() 方法会返回一个和客户端 Socket 对象相连接的 Socket 对象,服务器端的 Socket 对象使用 getOutputStream() 方法获得的输出流将指定客户端 Socket 对象使用 getInputStream() 方法获得那个输入流。同样,服务器端的 Socket 对象使用的 getInputStream() 方法获得的输入流将指向客户端 Socket 对象使用的 getOutputStream() 方法获得的那个输出流。也就是说,当服务器向输出流写入信息时,客户端通过相应的输入流就能读取,反之亦然。
Socket 类表示通信双方中的客户端,用于呼叫远端机器上的一个端口,主动向服务器端发送数据(当连接建立后也能接收数据)。下面简单介绍一下 Socket提供了如下的构造方法:
在上述方法的参数中,address 指的是远程地址,port 指的是远程端口,localAddr 指的是要将套接字绑定到的本地地址,localPort 指的是要将套接字绑定到的本地端口。
Socket类有如下的常用方法:
UDP通信是一种无连接的数据报通信。使用该协议,两个程序进行通信时不用建立连接;数据以独立的包为单位发送,包的容量限定在64KB以内;每个数据报需要有完整的收/发地址,可以随时进行收/发数据报,但不保证传送顺序和内容准确;数据报可能会被丢失、延误等。UDP通信是不可靠的通信,但通信速度较快,常常被应用在某些要求实时交互,准确性要求不高,但传输速度要求较高的场合(如视频会议系统等)。
Java中,基于UDP协议实现网络通信的类有三个:
本次实验着重介绍前两个。
java.net 包中的 DatagramPacket 类用来表示数据报包,数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。DatagramPacket有如下的常用构造方法:
DatagramPacket类有如下的常用方法:
DatagramSocket 类用于表示发送和接收数据报包的套接字。数据报包套接字是包投递服务的发送或接收点。每个在数据报包套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。DatagramSocket有如下的常用构造方法:
DatagramSocket类有如下的常用方法:
综上,在 Java 中使用 UDP 协议发送数据时需要先使用 DatagramSocket() 创建一个数据包套接字,然后用 DatagramPacket() 创建要发送的数据包,最后用 DatagramSocket 类的 send() 方法发送数据包。接收 UDP 数据包时需要先使用 DatagramSocket 创建数据包套接字,并将其绑定到指定的端口,然后使用 DatagramPacket 创建字节数组来接收数据包,最后使用 DatagramPacket 类的 receive() 方法接收 UDP 包。
接下来,我们将利用前面所学,完成一个C/S程序的编写。C/S程序包括客户端和服务器端两部分。首先编写客户端代码如图 1:
图 1 C/S程序中客户端代码
首先,在前两行引入所必须的包。在第7行建立Socket套接字连接后,在10-12行通过套接字连接获得对应输入输出流。并通过循环读入,在第15行将本地的输入循环发送给服务器。第17行获取服务器返回的信息,输出并在第20行进行判断,如果是结束,则终止循环并结束,否则继续获取输入即可。
图 2 C/S程序中服务器端代码
编写服务器端代码如图 2,首先,在前三行引入所必须的包。在第8行创建ServerSocket对象对10001端口进行监听,并通过第10行的accept方法获取由客户端发来的数据包。在第12,13行获取数据包的输入和输出。然后利用第16行的循环获取数据包,当从客户端获得的信息是“Time” 时,则返回当前的服务器时间;当从客户端获得的信息是“Exit”时,返回“Bye”,并结束循环。退出循环时,在第32到第35行关闭所有流和socket。
接下来将对C/S程序进行测试。首先,运行Server.java开启服务端Socket
图 3 开启服务端
如图 3,开启服务端之后,服务端输出“Server is on”。然后再运行Client.java,开启客户端并建立Socket连接。
图 4 建立Socket连接
如图 4,开启客户端之后,服务器端的accept方法成功获取到了客户端的Socket。
图 5 客户端发送“Time”
如图 5,客户端发送Time后,服务器端返回当前的服务器时间。
图 6 C/S程序结束
如图 6,客户端发送Exit后,将返回“Bye”,并结束程序。至此,我们的C/S程序已经编程并测试完毕。
3 实现简单的数据报通信聊天软件
图 7 引入头文件
如图 7,首先引入实现数据报对应所需的包。
图 8 定义对应的变量
如图 8,定义对应的变量以及GUI元素,以便后面进行调用。
图 9 聊天软件的GUI设置
如图 9,定义了整个聊天软件的GUI设置。在第31行设置了UI界面的标题,32和33行设置了UI界面的大小和关闭操作。然后34-36行定义了选择按钮的面板,并将其加入到界面中。37-40行定义了文本框,并将可编辑设置为false,用以显示聊天记录。41-44行设置布局,采用BorderLayout进行布局。45-50行将“发送”,“清空”和“退出”按钮加入布局。51-59行获取IP和地址,并进行回显。最后将布局设置为可见,并调用获取信息方法以及为三个按钮加入监听。
图 10 获取消息方法
70-74行通过创建socket数据包,76-77行定义DatagramPacket对象。为了方便获取信息,在第78-96用Lambda表达式创建了线程对象以获取信息,并在第97行完成线程的启用。
图 11 定义鼠标事件方法
如图 11,定义了鼠标事件的方法,第103-109行为发送按钮,直接调用发送信息的方法并用catch捕获异常即可,第110-112行和第113-115行为清空和退出按钮,调用对应方法即可。
图 12 退出函数
如图 12,第120-122行,退出函数直接调用系统函数进行退出即可。
图 13 清空函数
如图 13,清空函数将文本框内的内容设置为空即可。
图 14 发送信息函数
如图 14,发送信息函数通过第133-138行,使用IP地址以及报文内容建立数据报对象。并在第139行将内容回显。在第140-144行,将数据报发送。完成发送数据报后,在145行清空输入框。
图 15 主函数
如图 15,主函数调用写好的类以及对应的函数,直接传入发送者和接受者的端口和昵称即可。
接下来,将进行测试。
图 16 运行聊天软件
首先,运行聊天软件,界面如图 16,可以看到,上方有我们设计好的发送,清空,退出三个按钮。中间是历史记录显示区,最下方是发送窗口。
图 17 测试发送一条消息
接着,我们测试发送一条消息,如图 17,两侧的消息记录区将同步显示消息记录。
图 18 使用另一个账户发送一条消息
接着,我们使用另一个账户发送一条消息,如图 18,两侧的消息记录区将同步显示消息记录,并且也会显示发送者的名字。
图 19 测试清屏
我们还设置了清屏功能,如图 19,点击“Clear”按钮即可进行清屏。
图 20 清屏后发送一条消息
清屏后,依然可以发送消息。不清屏的界面将显示全部历史记录,清屏的界面将显示从清屏之后的历史记录。
图 21 结束并退出
最后,点击“Exit”按钮即可退出程序,结果如图 21。至此,我们已经完成了所有的聊天软件编程。
- import java.net.*;
- import java.io.*;
- import java.util.*;
-
- public class Server {
- public static void main(String[] args) throws IOException {
- //在10001端口监听
- ServerSocket server_socket = new ServerSocket(10001);
- System.out.println("Server is on.");
- Socket socket = server_socket.accept();
- System.out.println("Connection to client is created");
- DataInputStream in = new DataInputStream(socket.getInputStream());
- DataOutputStream out = new DataOutputStream(socket.getOutputStream());
- //从客户端读取信息
- String str = in.readUTF();
- while (true) {
- //当客户端发送的信息是"Time"时服务器返回当前时间
- if (str.equals("Time")) {
- Date date = new Date();
- out.writeUTF("服务器当前时间为: " + date);
- System.out.println("服务器当前时间为: " + date);
- }
- //客户端发送的信息是"Exit"时服务器返回"Bye"并退出
- else if (str.equals("Exit")) {
- out.writeUTF("Bye");
- System.out.println("Bye");
- break;
- }
- str = in.readUTF();
- }
- //关闭所有流和socket
- in.close();
- out.close();
- socket.close();
- server_socket.close();
- }
- }
-
-
- import java.net.*;
- import java.io.*;
-
- public class Client {
- public static void main(String[] args) throws IOException {
- //创建socket对象并连接到本地10001端口的服务器
- Socket socket = new Socket("localhost", 10001);
- System.out.println("Connection created.");
- //获得socket的输入输出流和获得系统标准输入
- BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
- DataInputStream in = new DataInputStream(socket.getInputStream());
- DataOutputStream out = new DataOutputStream(socket.getOutputStream());
- String str = input.readLine();
- while (true) {
- out.writeUTF(str);
- //发送输入给服务器
- String out_str = in.readUTF();
- //读取信息
- System.out.println(out_str);
- if (out_str.equals("Bye")) {
- break;
- }
- str = input.readLine();
- }
- }
- }
- import java.awt.*;
- import java.awt.event.*;
- import java.io.IOException;
- import java.net.*;
- import javax.swing.*;
-
- public class ChatRoom extends JFrame implements ActionListener {
-
- private final JTextArea text;
- private final JTextField ip_text;
- private final JTextField send_text;
-
- private final JButton button_exit;
- private final JButton button_clear;
- private final JButton button_send;
-
- private DatagramSocket socket;
-
- private final int port_send;
- private final int port_receive;
- private final String sender;
- private final String receiver;
-
- /**************************** GUI代码 ******************************/
- public ChatRoom(String sender, String receiver, int port_sender, int port_receiver)
- throws UnknownHostException {
- port_send = port_sender;
- port_receive = port_receiver;
- this.sender = sender;
- this.receiver = receiver;
- setTitle("UDP Chat Program");
- setBounds(100, 100, 400, 300);
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- setLayout(new BorderLayout());
- JPanel select_bar = new JPanel(new FlowLayout());
- add(select_bar, BorderLayout.NORTH);
- text = new JTextArea();
- text.setEditable(false);
- JScrollPane textPanel = new JScrollPane(text);
- add(textPanel, BorderLayout.CENTER);
- JPanel panel = new JPanel();
- BorderLayout panelLayout = new BorderLayout();
- panelLayout.setHgap(8);
- panel.setLayout(panelLayout);
- button_send = new JButton("Send");
- button_clear = new JButton("Clear");
- button_exit = new JButton("Exit");
- select_bar.add(button_send);
- select_bar.add(button_clear);
- select_bar.add(button_exit);
- InetAddress inetaddress = InetAddress.getLocalHost();
- String local_address = inetaddress.getHostAddress();
- ip_text = new JTextField(local_address);
- JTextField host_text = new JTextField(sender);
- panel.add(host_text, BorderLayout.WEST);
- send_text = new JTextField();
- panel.add(send_text, BorderLayout.CENTER);
- add(panel, BorderLayout.SOUTH);
- setVisible(true);
- Get_Message();
- button_send.addActionListener(this);
- button_clear.addActionListener(this);
- button_exit.addActionListener(this);
- }
-
- /**************************** 获得信息的方法 ******************************/
-
- private void Get_Message() {
- //创建数据包socket
- try {
- socket = new DatagramSocket(port_receive);
- } catch (SocketException e) {
- e.printStackTrace();
- }
- //用buffer存储数据
- byte[] buffer = new byte[1024];
- final DatagramPacket data = new DatagramPacket(buffer, buffer.length);
- Runnable runnable = () -> {
- while (true) {
- try {
- //用线程控制命令
- Thread.sleep(100);
- } catch (InterruptedException e1) {
- e1.printStackTrace();
- }
- try {
- socket.receive(data);
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- //输出接收到的消息
- String message = new String(data.getData(), 0, data.getLength());
- text.append(receiver + ":\n " + message + "\n");
- }
- };
- new Thread(runnable).start();
- }
-
- /**************************** 定义鼠标事件的方法 ******************************/
-
- public void actionPerformed(ActionEvent ev) {
- if (ev.getSource() == button_send) {
- try {
- Send_Message();
- } catch (UnknownHostException e) {
- e.printStackTrace();
- }
- }
- if (ev.getSource() == button_clear) {
- Clear();
- }
- if (ev.getSource() == button_exit) {
- Exit();
- }
- }
-
- /**************************** Exit按钮 ******************************/
-
- private void Exit() {
- System.exit(0);
- }
-
- /**************************** Clear按钮 ******************************/
-
- private void Clear() {
- text.setText("");
- }
-
- /**************************** Send按钮 ******************************/
-
- public void Send_Message() throws UnknownHostException {
- String ip = ip_text.getText();
- InetAddress inetaddress;
- inetaddress = InetAddress.getByName(ip);
-
- byte[] data = send_text.getText().getBytes();
- DatagramPacket data_package = new DatagramPacket(data, data.length, inetaddress, port_send);
- text.append(sender + ":\n " + send_text.getText() + "\n");
- try {
- socket.send(data_package);
- } catch (IOException e) {
- e.printStackTrace();
- }
- send_text.setText(null);
- }
-
- public static void main(String[] args) throws UnknownHostException {
- //创建两个聊天室
- ChatRoom room_one = new ChatRoom("szudyh1", "szudyh2", 7654, 7655);
- room_one.setVisible(true);
- ChatRoom room_two = new ChatRoom("szudyh2", "szudyh1", 7655, 7654);
- room_two.setVisible(true);
- }
-
- }
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。