更好的阅读体验
IP Address
网络编程模型
InetAddress
InetAddress
类主要是用来得到所指定的网络地址InetAddress
类没有直接显式的构造函数。要生成一个InetAddress对象,必须运用一个可用的工厂方法。工厂方法(factory method
)仅是一个类中的静态方法返回一个该类实例的约定。这是在一个带有各种参数列表的重载构造函数中完成的,当持有惟一方法名时可使结果更清晰。InetAddress
有三个方法可以用来创建InetAddress
的实例static InetAddress getLocalHost( ) throws UnknownHostException
static InetAddress getByName(String hostName) throws UnknownHostException
static InetAddress[ ] getAllByName(String hostName) throws UnknownHostException
InetAddress类的非静态方法
boolean equals(Object other)
如果对象具有和other相同的Internet地址则返回true。byte[ ] getAddress( )
返回此InetAddress对象的原始 IP 地址。String getHostAddress( )
返回与InetAddress对象相关的主机地址的字符串。String getHostName( )
返回与InetAddress对象相关的主机名的字符串。int hashCode( )
返回调用对象的散列码。boolean isMulticastAddress( )
如果Internet地址是一个多播地址则返回true;否则返回false。String toString( )
返回主机名字符串和IP地址。
URL
- URL类封装了使用统一资源定位器(Uniform Resource Locator)访问一个WWW上的资源的方法。这个类可以生成一个寻址或指向某个资源的对象。URL类生成的对象指向WWW资源(Web页、文本文件、图形文件、声频片段等等)
- URL的基本表示方法是:
Protocol://hostname:port/resourcename#anchor
,即协议://主机名:端口/资源名#标记
构造方法
- public URL (String spec)
- 通过一个表示URL地址的字符串可以构造一个URL对象。如以下语句:
URL urlBase=new URL("http://www. 263.net/");
- public URL(URL context, String spec)
- 通过基URL和相对URL构造一个URL对象。如以下语句:
- `URL net263=new URL (“http://www.263.net/”);``
- ``URL index263=new URL(net263, “index.html”)`
- public URL(String protocol, String host, String file);
URL url=new URL("http", "www.gamelan.com", "/pages/Gamelan.net. html");
- public URL(String protocol, String host, int port, String file);
URL gamelan=new URL("http", "www.gamelan.com", 80, "Pages/Gamelan.network.html");
URL常用方法
public String getProtocol()
获取该URL的协议名。public String getHost()
获取该URL的主机名。public int getPort()
获取该URL的端口号,如果没有设置端口,返回-1。public String getFile()
获取该URL的文件名。public String getQuery()
获取该URL的查询信息。public String getPath()
获取该URL的路径。public String getAuthority()
获取该URL的权限信息。public String getUserInfo()
获得使用者的信息。public String getRef()
获得该URL的引用。
URL类的应用
-
URL类经常用于下载网络资源,URL通过构造函数(构造函数为URL地址)可以得到一个对象,该对象的openStream()方法可以得到InputStream对象,得到InputStream就可以把网站上的资源下载下来了
-
下面是一个实例,使用URL类下载某个网站上的一张图片并保存到本地。
import java.net.*; import java.io.*; public class TestURL { public static void main(String aregs[ ])throws Exception { URL url=new URL("http://images.sohu.com/uiue/sohu_logo/beijing2008/sohu.gif"); InputStream in=url.openStream(); FileOutputStream fout=new FileOutputStream(new File("sohu.gif")); int a=0; while(a>-1) { a=in.read(); fout.write(a); } } }
运行成功后,在当前目录生成一个gif图片
URLConnection
- URL类中的openConnection()方法可生成一个URLConnection对象,URLConnection类的实例可用于读取和写入此URL引用的资源。
- 在网络编程工作中,JAVA的URLConnection是一个常用的类,它提供了一个非常方便的接口,只要提供需要连接的主机的URL地址,使用URL类的openConnection()方法就可以得到一个HttpURLConnection的对象,其中HttpURLConnection类是URLConnection类的子类,然后在此基础上分析HTTP内容,完成相关任务。在这种方式下,使用者不必考虑底层的实现细节,避免了烦琐的Socket类的代码编写,因此比较常用。
URLConnection常用方法
String getHeaderField(String name)
返回指定的头字段的值。InputStream getInputStream()
返回在此打开的连接读取的输入流。String getContentEncoding()
返回content-encoding头字段的值。int getContentLength()
返回content-length头字段的值。String getContentType()
返回content-type头字段的值。long getDate()
返回date头字段的值。
练习
-
使用InetAddress类的方法获取本地机的名称和IP地址
package com.ipaddress; import java.net.*; public class IPAddress { public static void main(String[] args) { try { InetAddress inetAddress = InetAddress.getLocalHost(); String hostName = inetAddress.getHostName(); String hostAddress = inetAddress.getHostAddress(); System.out.println(hostName); System.out.println(hostAddress); } catch (UnknownHostException e) { e.printStackTrace(); } } }
打印结果:
MacBook-Air.local 192.168.0.101
-
使用InetAddress类的方法获取网站www.csdn.net的IP地址,如果存在多个IP地址,要求全部返回。
package com.ipaddress; import java.net.*; public class CSDNAddress { public static void main(String[] args) { try { InetAddress[] inetAddresses = InetAddress.getAllByName("www.csdn.net"); for (InetAddress inetAddress: inetAddresses) { String hostName = inetAddress.getHostName(); String hostAddress = inetAddress.getHostAddress(); System.out.println(hostName + " " + hostAddress); } } catch (UnknownHostException e) { e.printStackTrace(); } } }
打印结果
www.csdn.net 39.106.226.142
-
使用URL类下载深圳大学首页http://www.szu.edu.cn,并统计下载得到网页文件的大小
package com.ipaddress; import java.net.*; import java.io.*; public class SZUurl { public static void main(String[] args) throws Exception { URL url = new URL("http://www.szu.edu.cn"); URLConnection connection = url.openConnection(); int size = connection.getContentLength(); InputStream in = url.openStream(); FileOutputStream fout = new FileOutputStream(new File("szu.html")); int a = 0; while(a > -1) { a = in.read(); fout.write(a); } fout.close(); System.out.println(size + "B"); } }
打印结果
137 B
此时目录结构,可以看到
szu.html
文件在此目录下$ tree . ├── Network.iml ├── out │ └── production │ └── Network │ └── com │ └── ipaddress │ ├── CSDNAddress.class │ ├── IPAddress.class │ └── SZUurl.class ├── src │ └── com │ └── ipaddress │ ├── CSDNAddress.java │ ├── IPAddress.java │ └── SZUurl.java └── szu.html 8 directories, 8 files
网络聊天室
套接字(Socket)
- 用于实现网络上客户端程序和服务器程序之间的连接。
- 套接字负责网络上进程之间的通信
- 客户端程序可以向套接字里写入请求,然后服务器会处理这个请求,并把处理结果通过套接字送回。
- 服务器应用程序一般会侦听一个特定端口,用来等待客户的连接请求,当一个连接请求到达时,客户和服务器会建立一个通信连接,在连接过程中,客户被分配一个本地端口号并与一个Socket连接,客户通过写Socket来通知服务器,再通过读取Socket来获取服务器发送过来的信息。
- 类似地,服务器也获取一个本地端口号,它需要一个新的端口号来侦听原始端口上的其他连接请求。服务器也给它的本地端口连接一个Socket,通过读写它来与客户通信。
- Socket可以根据通信性质分类,这种性质对于用户是可见的。
- 应用程序一般仅在同一类的套接字之间进行通信。不过只要底层的通信协议允许,不同类型的套接字之间也可以通信。
- 目前可以使用两种套接字,即流套接字和数据报套接字。
- 流套接字提供了双向的、有序的、无重复的并且无记录边界的数据流服务。
- TCP是一种流套接字协议
- 数据报套接字支持双向的数据流,但并不保证是可靠、有序、无重复的,也就是说,一个以数据报套接字来接收信息的进程有可能发现信息重复了,或者和发出的顺序不同了。数据报套接字的一个重要特点是它保留了记录边界。
- UDP即是一种数据报套接字协议。
端口
- 是一个逻辑概念。每一个服务器都运行在该主机的一个对外开放的端口上。
- 一个主机上可以有多种服务器,也就是有多个端口。程序员可以在创建自己的服务器程序时使用其它端口(即除了系统默认的端口)。
- 端口常以数字编号,作为客户可指定一个端口号,用这个端口号来连接相应的服务器以接收服务。
- 如果把 IP 地址看作电话号码,则端口类似电话的分机号码
cmd > netstat –na
可以查看本机的端口使用情况。
Socket
Socket是网络上运行的程序之间双向通信链路的最后终结点
IP与端口的组合得出一个套接字,可以完全分辨Internet上运行的程序
Socket通信—TCP
- 一对一的Socket C/S通信
- TCP是一种可靠的、基于连接的网络协议,在Internet上大都采用TCP/IP协议进行互联。网络上的两个进程采用C/S模式进行通信。
- 当两台主机准备进行交谈时,都必须建立一个Socket,其中一方作为服务器打开一个Socket并侦听来自网络的连接请求,另一个作为客户,它向网络上的服务器发送请求,通过Socket与服务器传递信息,要建立连接,只需指定主机的IP地址和端口即可。
Socket 工作模式
使用TCP协议的Socket编程
-
概述
- Java提供Socket和ServerSocket类作为标准的TCP套接字编程技术,通过它们实现主机与主机之间(应用程序间)的对话。位于:java.net包中。
- 使用Socket进行C/S程序设计的一般连接过程:
- Server端Listen(监听)某个端口是否有连接请求,
- Client端向Server端发出Connect(连接)请求,
- Server端向Client端发回Accept(接受)消息。
- 连接建立后,Server端和Client端都可以通过套接字类提供的一些方法与对方通信。
-
Socket类的构造方法:
public Socket(String host, int port) public Socket(InetAddress address, int port) public Socket(String host, int port, InetAddress localAddr, int localPort) //在指定的机器上的指定端口上运行
上述方法都将抛出例外IOException,程序中需要捕获处理。
-
Socket的常见方法:
//Socket的输入/输出流管理;抛出例外IOException。 public InputStream getInputStream() public void shutdownInput() public OutputStream getOutputStream() public void shutdownOutput() //关闭Socket public void close() throws IOException //设置/获取Socket数据: public InetAddress getInetAddress() 返回此套接字链接的地址对象 public InetAddress getLocalAddress() 返回此套接字本地的地址对象 public int getPort() 返回此套接字链接地址的端口
上述方法都将抛出例外SocketException,程序中需要捕获处理。
-
ServerSocket类
构造方法:
public ServerSocket(int port) public ServerSocket(int port, int backlog) //支持指定数目的连接 public ServerSocket(int port, int backlog, InetAddress bindAddr) //在指定的机器上运行
主要方法
public Socket accept():等待客户端的连接 public void close():关闭Socket
设置/获取Socket数据的方法
public InetAddress getInetAddress() public int getLocalPort(), ...
-
Socket通信程序基本结构都一样,包括以下四个基本步骤:
- 在客户方和服务器方创建Socket/ServerSocket实例。
- 打开连接到Socket的输入/输出流。
- 利用输入/输出流,按照一定的协议对Socket进行读/写操作。
- 关闭输入/输出流和Socket。
通常,程序员的主要工作是针对所要完成的功能在第3步进行编程,第1、2、4步对所有的通信程序来说几乎都是一样的。
Socket
-
创建Socket
下面是一个典型的创建客户端Socket的代码:
try { Socket socket=new Socket("127.0.0.1",1432); //127.0.0.1是TCP/IP协议中默认的本机地址 }catch(IOException e){ System.out.println("Error:"+e); }
-
创建服务器应用程序
-
用于服务器的类和方法
- 要创建服务器,你需要创建ServerSocket对象监听客户请求的特定端口。
- 当它认出有效请求,服务器Socket获得客户创建的Socket对象。用这Socket产生位于服务器和客户机之间的通信。
-
ServerSocket类
- ServerSocket类的构造函数带两个参数 :第一个是端口号码 ;第二个参数表示可连接的最大数。
- ServerSocket类提供如下方法:
- 监听连接
- 返回地址和本机端口
- 返回表示Socket的串
-
创建服务器
构造函数的代码给出如下:try { server = new ServerSocket(1432); //创建一个ServerSocket在端口1432监听客户请求 } catch (Exception e) { System.out.println("can not listen to:" + e); //出错,打印出错信息 } System.out.println("Server started…"); this.start(); //启动线程
-
监听客户请求
ServerSocket对象通过accept()方法来监听客户的请求,如果接收到客户的请求,则产生一个Socket对象,并继续执行后面的代码;如果没有接收到客户请求,则产生一个错误信息。
Socket socket = null; try { socket = server.accept(); //使用accept()阻塞等待客户请求,有客户 //请求到来则产生一个Socket对象,并继续执行 } catch (Exception e) { System.out.println("can not listen to:" + e); //出错,打印出错信息 }
-
服务器的输入和输出流
服务器端用到了以下的输入和输出流:- BufferedReader对象用来表示从客户端输入到服务器的流
- PrintWriter对象表示服务器端输出到客户端的流
- BufferedReader对象表示从系统标准输入设备输入到服务器端的流。
``java
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//由Socket对象得到输入流,并构造相应的BufferedReader对象
PrintWriter os = new PrintWriter(socket.getOutputStream());
//由Socket对象得到输出流,并构造PrintWriter对象
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
//由系统标准输入设备构造BufferedReader对象
{
new Server();
} -
完整的服务器程序
- 服务器程序创建了一个BufferedReader对象(is)和一个PrintStream对象(os)。is使用getInputStream()方法检索客户端的输入;os使用getOutputStream()方法使得服务器可以把输出写到客户端。这样一来,就开始了双向通信。
- 当客户连接到服务器时,服务器在标准输出上打印从客户端读入的字符串,然后由BufferedReader对象sin的readline()方法从标准输入读入一字符串,并发送到客户端程序。当有一方输入”bye”时,通信结束。
- 到了结束的时候,服务器中断循环。关闭客户套接字。关闭套接字很重要,如果让连接开着,很快就将耗尽服务器的内存。
-
ObjectInputStream类
-
ObjectInputStream类中包含从持久的存储介质中读出类的对象的功能。持久的存储介质可以是磁盘或套接字。
-
这由ObjectInputStream类的readObject()方法来完成。readObject()方法的语法如下:
FileInputStream fp=new FileInputStream(“data.txt”); ObjectInputStream istream = new ObjectInputStream(fp); Date date = (Date)istream.readObject();
-
-
-
一般步骤
- 选择创建网络服务器应用程序所需的类和方法。
- 确定用户定义的类,它们的目的,方法。
- 确定用户定义的类中的数据类型和变量。
- 确定服务器的IP地址和端口号。
- 确定查询参数。
- 确定要处理的异常。
- 确定异常的出错信息。
数据报通讯(UDP)
概述
-
UDP通信是一种无连接的数据报通信。使用该协议,两个程序进行通信时不用建立连接;数据以独立的包为单位发送,包的容量限定在64KB以内;每个数据报需要有完整的收/发地址,可以随时进行收/发数据报,但不保证传送顺序和内容准确;数据报可能会被丢失、延误等。UDP通信是不可靠的通信,但通信速度较快,常常被应用在某些要求实时交互,准确性要求不高,但传输速度要求较高的场合(如视频会议系统等)。
-
Java中,基于UDP协议实现网络通信的类有三个:
-
用于表示通信数据的数据报类:DatagramPacket
-
用于进行端到端通信的类:DatagramSocket
-
用于广播通信的类:MulticastSocket。
-
基于UDP通信的基本模式
- 将数据打包,发往目的地
- 接受别人发来的数据包,察看数据包内容
发送数据包
-
用DatagramPacket类将数据打包,即创建数据包对象。
DatagramPacket(byte data[], int length, InetAddtress address,int port) Address: 目标主机地址 Port:目标主机端口
如:
byte data[]=“近来好吗”.getByte(); InetAddress address=inetAddress.getByname(www.sina.com.cn); DatagramPacket data_pack=new DatagramPacket(data,data.length,address,980);
注: data_pack常用方法:
- Public int getPort();
- public InetAddress getAddress();
- public byte[] getData();
-
用DatagramSocket类的构造方法DatagramSocket()创建一个对象,该对象负责发送数据包。例:
- DatagramSocket mail_out=new DatagramSocket();
- Mail_out.send(data_pack);
接收数据
DatagramSocket mail_in=new DatagramSocket(int port);
注意:mail_in的主要方法:
- Receive(DatagramPacket pack) //接收数据包给pack, 例如:
- DatagramPacket pack=new DatagramPacket(data,length)
- Mail_in.receive(pack);
练习
练习1
-
利用Socket类和ServerSocket类编写一个C/S程序,实现C/S通信。
-
客户端向服务器端发送Time命令,服务器端接受到该字符串后将服务器端当前时间返回给客户端;客户端向服务器端发送Exit命令,服务器端向客户端返回“Bye”后退出。
-
编写完整程序;一个服务器端程序,一个客户端程序。服务器端和客户端都需要打印出接受到的消息和发出的命令。
-
下图为运行结果示例
Server.java
:
package com.csmode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class Server {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket;
DataOutputStream dataOutputStream;
DataInputStream dataInputStream;
System.out.println("Server Running ... ");
String time;
String goodbye = "Bye!";
String error = "No such Command!";
try {
//在端口号4096创建 serverSocket
serverSocket = new ServerSocket(4096);
} catch (IOException e) {
System.out.println("ServerSocket error");
e.printStackTrace();
}
try {
assert serverSocket != null;
socket = serverSocket.accept();
// 连接成功
dataOutputStream = new DataOutputStream(socket.getOutputStream());
dataOutputStream.writeUTF("服务器启动完毕\n创建客户连接\t\t");
// 标记什么时候退出
boolean flag = true;
while(flag) {
// 接收客户端发送过来的消息
dataInputStream = new DataInputStream(socket.getInputStream());
String str = dataInputStream.readUTF();
System.out.print(str + " : ");
if(str.equals("Time")) {
time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
dataOutputStream.writeUTF("服务器当前时间为:\t" + time);
System.out.println(time);
} else if (str.equals("Exit")) {
dataOutputStream.writeUTF(goodbye);
flag = false;
} else {
dataOutputStream.writeUTF(error);
}
}
} catch (IOException e) {
System.out.println("Socket data IO error");
e.printStackTrace();
}
}
}
Client.java
:
package com.csmode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 4096);
// 创建 Socket 管道
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
String string = dataInputStream.readUTF();
System.out.println(string);
while(true) {
// 输入命令
Scanner input = new Scanner(System.in);
String str = input.next();
dataOutputStream.writeUTF(str);
// 在服务器端传来的 数据
string = dataInputStream.readUTF();
System.out.println(string);
if(str.equals("Exit")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
先启动Server.java
, 再启动Client.java
结果如下:
客户端:
服务器端:
练习2
-
编写一数据报通信程序,实现简单的聊天功能。
-
“聊天内容”和“输入文本”分别为当前聊天的历史信息和当前要传送出去的聊天文本。“确定”、“清空”、“退出”三个按钮分别实现发送当前聊天文本、清空当前聊天文本和退出系统的功能。
-
界面可参考如下格式
PID.java
package com.chattingroom;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
public class PID {
// 获取 PID
public static int getProcessID() {
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
return Integer.parseInt(runtimeMXBean.getName().split("@")[0]);
}
}
chat.java
package com.chattingroom;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class chat extends JFrame implements ActionListener, Runnable, KeyListener {
private TextArea textArea;
private JTextField sendText;
private JTextField ip;
private JTextField port;
private JButton buttonServer;
private JButton buttonClient;
private JButton send;
private JButton exit;
private JButton clear;
private Socket socket;
public void keyReleased(KeyEvent f) {
}
// 监听键盘输入
public void keyPressed(KeyEvent f) {
if (f.getKeyCode() == KeyEvent.VK_ENTER) {
PrintWriter printWriter = null;
try {
printWriter = new PrintWriter(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
String string = sendText.getText();
if (string == null)
return;
int pid = PID.getProcessID();
String User = ip.getText() + " : " + port.getText();
// 自己发的消息
textArea.append(User + " : " + pid + " says: " + "\n" + string + "\n");
// 对面发来的消息
String string3 = User + " : " + pid + " says: " + "\n" + string;
assert printWriter != null;
printWriter.println(string3);
printWriter.flush();
sendText.setText("");
}
}
public void keyTyped(KeyEvent f) {
}
// 轮询
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
String string = br.readLine();
if (string == null)
break;
textArea.append(string + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == buttonServer) {
server();
}
if (e.getSource() == buttonClient) {
client();
}
if (e.getSource() == send) {
doSend();
}
if (e.getSource() == exit) {
doExit();
}
if (e.getSource() == clear) {
doClear();
}
}
// 作为服务器启动
public void server() {
try {
ServerSocket server = new ServerSocket(Integer.parseInt(port.getText()));
socket = server.accept();
textArea.append("连接服务器成功!\n");
new Thread(this).start();
} catch (Exception e) {
textArea.append("服务器启动失败!\n");
}
}
// 作为客户端启动
public void client() {
try {
socket = new Socket(ip.getText(), Integer.parseInt(port.getText()));
textArea.append("连接服务器成功!\n");
new Thread(this).start();
} catch (Exception e) {
textArea.append("连接失败!\n");
}
}
// 发送消息
public void doSend() {
try {
PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
String string = sendText.getText();
if (string == null)
return;
int pid = PID.getProcessID();
String User = ip.getText() + " : " + port.getText();
// 自己发的消息
textArea.append(User + " : " + pid + " says: " + "\n" + string + "\n");
// 对面发来的消息
String string3 = User + " : " + pid + " says: " + "\n" + string;
printWriter.println(string3);
printWriter.flush();
sendText.setText("");
} catch (Exception e) {
textArea.append("发送失败!\n");
}
}
// 清除聊天
public void doClear() {
textArea.setText("");
}
// 退出程序
public void doExit() {
System.exit(0);
}
// 连接键
public void addClient(JPanel panel) {
buttonClient = new JButton("连接");
buttonClient.setForeground(new Color(250, 131, 46));
buttonClient.setFont(new Font("宋体", Font.BOLD, 14));
panel.add(buttonClient);
}
// 监听键
public void addServer(JPanel panel) {
buttonServer = new JButton("监听");
buttonServer.setForeground(new Color(250, 131, 46));
buttonServer.setFont(new Font("宋体", Font.BOLD, 14));
panel.add(buttonServer);
}
// 清除键
public void addClear(JPanel panel) {
clear = new JButton("清除");
clear.setForeground(new Color(250, 131, 46));
clear.setFont(new Font("宋体", Font.BOLD, 14));
panel.add(clear);
}
// 退出键
public void addExit(JPanel panel) {
exit = new JButton("退出");
exit.setForeground(new Color(250, 131, 46));
exit.setFont(new Font("宋体", Font.BOLD, 14));
panel.add(exit);
}
// 设置 JPanel
public void setPanel(JPanel panel) {
panel.setLayout(new BorderLayout());
panel.add(ip, BorderLayout.WEST);
panel.setBackground(new Color(245, 161, 102));
sendText = new JTextField("");
panel.add(sendText, BorderLayout.CENTER);
send = new JButton("发送");
panel.add(send, BorderLayout.EAST);
}
// 设置按键监听
public void setListen() {
clear.addActionListener(this);
exit.addActionListener(this);
buttonServer.addActionListener(this);
buttonClient.addActionListener(this);
send.addActionListener(this);
sendText.addKeyListener(this);
}
public JPanel setJPanel1(Container container) {
JPanel panel = new JPanel();
container.add(panel, BorderLayout.NORTH);
panel.setBackground(new Color(231, 162, 112));
textArea = new TextArea();
container.add(textArea, BorderLayout.CENTER);
return panel;
}
public JPanel setJPanel2(Container container) {
JPanel panel = new JPanel();
container.add(panel, BorderLayout.SOUTH);
panel.setBackground(new Color(250, 180, 30));
textArea.setForeground(new Color(250, 131, 46));
return panel;
}
public void addButton(JPanel panel){
addServer(panel);
addClient(panel);
addClear(panel);
addExit(panel);
}
// 聊天界面
public chat(String IPAddress, String Port) {
this.setTitle("UDP聊天程序");
this.setBounds(200, 200, 500, 500);
Container container = this.getContentPane();
JPanel panel1 = setJPanel1(container);
JPanel panel2 = setJPanel2(container);
ip = new JTextField(IPAddress, 15);
port = new JTextField(Port, 4);
addButton(panel1);
setPanel(panel2);
setListen();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Run.java
package com.chattingroom;
public class Run {
public static void main(String[] args) {
new chat("127.0.0.1", "8192").setVisible(true);
}
}
目录结构如下:
$ tree chattingroom
chattingroom
├── PID.java
├── Run.java
└── chat.java
开启两个程序运行,效果如下:
左端输入123,右端输入456,结果如下:
TCP 文件传输
Java对网络编程的支持
java.net包中的主要的类和可能产生的例外包括:
- 面向应用层的类:
- URL
- URLConnection
- 面向传输层/IP层的类:
- TCP协议相关类:
- Socket
- ServerSocket
- UDP协议相关类:
- DatagramPacket
- DatagramSocket
- MulticastSocket
- TCP协议相关类:
- 表示IP 地址的类:
- InetAddress
- 可能产生的异常:
- BindException
- ConnectException
- MalformedURLException
- NoRouteToHostException
- ProtocolException
- SocketException
- UnknownHostException
- UnknownServiceException
练习
-
利用Socket类和ServerSocket类,编写一个C/S程序,实现网络文件传输。
-
客户端向服务器端发送请求,服务器端当接受到客户端的请求之后,先向其传输文件名,当客户端接受完毕之后,向客户端传输文件
-
客户端连上服务器后接收传输文件,并进行改名(文件名可自行定义)存在本地。
-
编写完整程序;一个服务器端程序,一个客户端程序。服务器端和客户端都需要打印出交互过程。
-
下图为运行结果示例
-
服务器端运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V42P3IHL-1648993394649)(https://cdn.jsdelivr.net/gh/Misaka-9982-coder/img_hosting/img/image-20220403132839233.png)]
-
客服端运行结果:
-
Server.java
package com.Transmission;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server implements Runnable {
private static final int Port = 4096;
private static final int TTL = 20;
private final Socket socket;
String fileName = "test.txt";
public Server(Socket socket) {
super();
this.socket = socket;
}
public static void server() {
try {
ServerSocket serverSocket = new ServerSocket(Port);
int i = 0;
do {
i ++ ;
Socket socket = serverSocket.accept();
System.out.println("服务器的线程1,启动,与客户端" + i + "连接成功");
new Thread(new Server(socket)).start();
} while (i <= TTL);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
OutputStream outputStream = null;
FileInputStream fileInputStream = null;
try {
outputStream = socket.getOutputStream();
System.out.println("要传输的文件为: " + fileName);
outputStream.write(fileName.getBytes());
System.out.println("开始传输文件");
fileInputStream = new FileInputStream(fileName);
int data;
while (-1 != (data = fileInputStream.read())) {
outputStream.write(data);
}
System.out.println("文件传输结束");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null)
fileInputStream.close();
if (outputStream != null)
outputStream.close();
this.socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server.server();
}
}
Client.java
package com.Transmission;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
public class Client {
private static final String SERVERIP = "localhost";
private static final int Port = 4096;
private static final int clientPort = 8192;
public static void main(String[] args) {
byte[] buffer = new byte[2048];
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress(SERVERIP, Port), clientPort);
System.out.println("与服务器连接成功");
InputStream inputStream = socket.getInputStream();
int read = inputStream.read(buffer);
String fileName = new String(buffer, 0, read);
String newName = "test1.txt";
FileOutputStream fileOutputStream = new FileOutputStream(newName);
int data;
while ((data = inputStream.read()) != -1) {
fileOutputStream.write(data);
}
System.out.println("接收到的文件为:" + fileName);
System.out.println("保存为为:" + newName);
inputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果:
客户端:
服务器端:
现在文件结构如下:
$ tree .
.
├── Network.iml
├── out
│ └── production
│ └── Network
│ └── com
│ ├── Transmission
│ │ ├── Client.class
│ │ └── Server.class
│ ├── chattingroom
│ │ ├── PID.class
│ │ ├── Run.class
│ │ └── chat.class
│ ├── csmode
│ │ ├── Client.class
│ │ └── Server.class
│ └── ipaddress
│ ├── CSDNAddress.class
│ ├── IPAddress.class
│ └── SZUurl.class
├── src
│ └── com
│ ├── Transmission
│ │ ├── Client.java
│ │ └── Server.java
│ ├── chattingroom
│ │ ├── PID.java
│ │ ├── Run.java
│ │ └── chat.java
│ ├── csmode
│ │ ├── Client.java
│ │ └── Server.java
│ └── ipaddress
│ ├── CSDNAddress.java
│ ├── IPAddress.java
│ └── SZUurl.java
├── szu.html
├── test.txt
└── test1.txt
14 directories, 24 files