网络典型例题
Java提供了两个不同层次的网络支持机制:
利用URL访问网络资源
利用Socket通信
一、统一资源定位符URL类
1、URL用来网络资源定位,它的值由5部分组成,格
式如下所示
<传输协议>://<主机名>:<端口号>/<文件名>#<引用>
其中传输协议(protocol)指明获取资源所使用的传输
协议,如http、ftp、file等。http表示通过HTTP访问网络
资源。ftp表示通过网络协议FTP访问网络资源。file表示
本机上文件,是所指定的资源。news表示通过NNTP访问指定新闻地址的资源。
主机名(hostname)指定资源所在的计算机,可以是IP地址,如127.0.0.1,也可以是主机名或域名,如https://www.wendangku.net/doc/bb15278605.html,。
一个计算机中可能有多种服务(应用程序),端口号(port)用来区分不同的网络服务,如http服务的默认端口号是80,ftp服务的默认端口号是21等。理论上有0-65535个端口,其中1024号以下端口一般都给定了用途,例如HTTP使用80端口;FTP默认21端口;Telnet 使用23端口。平常我们使用1024号以上端口。
文件名(filename)包括该文件的完整路径。在http协议中,缺省的文件名是index.html,因此,https://www.wendangku.net/doc/bb15278605.html,就相等同于https://www.wendangku.net/doc/bb15278605.html,/index.html。
引用(reference)为资源内的某个引用,用来定位显示文件内容的位置,如https://www.wendangku.net/doc/bb15278605.html,/index.html#chapter1。但并非所有的URL都包含这些元素。对于多数的协议,主机名和文件名是必需的,但端口号和文件内部的引用则是可选的。
2、URL类位于https://www.wendangku.net/doc/bb15278605.html,包中,是指向互联网资源的指针,使用URL可以通过给定的URL地址访问到需要的资源。资源可以是简单的文件和目录,也可以是对象的引用、对数据库或搜索引擎的查询。
URL类的对象代表一个URL地址。URL对象的创建示例:
其构造方法:
1)通过指定的URL字符串创建URL对象
URL gamelan= new URL("https://www.wendangku.net/doc/bb15278605.html,/pages/https://www.wendangku.net/doc/bb15278605.html,.html");
2)通过指定的协议、主机地址、路径字符串创建URL对象
URL gamelan =new URL("http", "https://www.wendangku.net/doc/bb15278605.html,", "/pages/https://www.wendangku.net/doc/bb15278605.html,.html");
3)通过指定的协议、主机地址、端口号、路径字符串创建URL对象
URL gamelan = new URL("http", "https://www.wendangku.net/doc/bb15278605.html,", 80, "pages/https://www.wendangku.net/doc/bb15278605.html,work.html"); 上两种方法将一个URL地址分解,按不同部分分别指定协议、主机、端口、文件。
4)通过指定的上下文中对指定的协议的解析创建对象:URL(URL context, String spec) 这种方法基于一个已有的URL对象创建一个新的URL对象,多用于访问同一个主机上不同路径的文件,例如:
URL u1=new URL(u, ”tutorial.intro.html”);
创建URL后,可以对其操作:
public int getDefaultItPort(); 获取与此URL关联的默认端口号
public String getFile();获取此URL的文件名
public String getHost();获取此URL的主机名
public String getPath();获取此URL的路径部分
public int getPort();获取此URL的端口号
public String getProtocol ();获取此URL的协议名称
public String getQuery();获取此URL的查询部分
public InputStream openStream();打开到此URL的连接并返回一个用于从该连接读入的InputStream。
3、URLConnection抽象类
该抽象类代表应用程序和URL之间的通信链接。
创建一个到URL的连接步骤:
1)对影响到远程资源连接的参数操作,通过在URL上调用openConnection方法创建连接对象。
2)除了设置参数和一般请求属性。
3)使用connect方法建立到远程对象的实际连接,与资源交互;查询头字段和内容。
4)远程对象变为可用。远程对象的头字段和内容变为可访问。
建立到远程对象的连接后,可以使用以下方法对头字段和内容进行访问。
public Object getContent() throws IOException; 获取此URL链接的内容
public String getHeaderField(int n); 获取指定头字段的值
public InputStream getInputStream() throws IOException; 获取输入流,如果读超时,会抛出SockeTimeoutException。
public OutputStream getOutputStream() throws IOException; 获取输出流
public String getContentEncoding(); 获取content-encoding的值
public int getContentLength(); 获取content-length的值
public String getContentType(); 获取content-type的值
public long getDate(); 获取date的值
public long getLastModified(); 获取last-modified的值,结果为距离格林威治标准时间的毫秒数。
以下是测试一例。
import java.io.*;
import https://www.wendangku.net/doc/bb15278605.html,.*;
import java.util.*;
//写URL的测试类,获取网站的信息,并将网站主页下载到本地磁盘上
public class URLT est
{//主方法
public static void main(S tring[] args)
{
try
{
//定义urlName的字符串,如果args数组长度大于0,则将args[0]的值赋给urlName
String urlName= "https://www.wendangku.net/doc/bb15278605.html,/index.asp";
if (args.length > 0)
{
urlName = args[0];
}
URL url = new URL(urlName);//创建URL对象
System.out.println("打印url的一些信息:");
System.out.println("getProtocol:" + url.getProtocol());//获取协议名称
System.out.println("getthost:" + url.getHost());//获取主机名称
System.out.println("gettfile:" + url.getFile());//获取此URL 的文件名
System.out.println("getPath:" + url.getPath());//获取路径
System.out.println("getPort:" + url.getPort());//获取端口号,未设置返回-1
System.out.println("getDefaultPort:" + url.getDefaultPort());//返回默认端口号
URLConnection connection = url.openConnection();//打开远程对象的连接,返回连接值
connection.connect();//连接到服务器,打开此URL引用的资源的通信链接
//获取并打印头字段的值
System.out.println("打印头字段信息:");
int n = 1;
String key;
while ((key = connection.getHeaderFieldKey(n)) != null)
{
String value = connection.getHeaderField(n);//返回第n个头字段的值
System.out.println(key + ": " + value);//打印头字段的键、值
n++;
}
//打印引用资源的一些属性
System.out.println("打印引用资源的一些属性");
System.out.println("getContentT ype: "
+ connection.getContentT ype());//返回引用资源的内容类型
System.out.println("getContentLength: "
+ connection.getContentLength());//返回引用资源的内容长度
System.out.println("getContentEncoding: "
+ connection.getContentEncoding());//返回引用资源的内容编码
//返回引用资源的发送日期,为距离格林威治标准时间1970年1月1日的毫秒数。
System.out.println("getDate: "+ connection.getDate());
//返回引用资源上次的修改日期,若未知返回0
System.out.println("getLastModifed: "+connection.getLastModified());
//下载引用文件到本地磁盘,读取文件
BufferedReader in = new BufferedReader(new
InputStreamReader(connection.getInputStream()));
// //打印输出源文件前10行
// String line;
// n = 1;
// while ((line = in.readLine()) != null && n <= 10)
// {//读出文件并输出
// System.out.println(line);
// n++;
// }
// if (line != null)
// System.out.println(". . . . . .");
//创建输出流,并指定目标文件
BufferedWriter bw=new BufferedWriter(new FileWriter("e://URLT est.html"));
//对输出流做进一步进行封装
PrintWriter pw=new PrintWriter(bw);
//声明临时字符串引用
String temps=null;
//从输入流中获取资源并测试是否读取完毕
while((temps=in.readLine())!=null)
{
//将获取的数据写如目标文件
pw.println(temps);
}
//打印提示信息
System.out.println("您好,网站主页已经下载完毕,已经写入了URLT est.html");
pw.close();//关闭输出流
in.close();//关闭输入流
}
catch (IOException exception)
{
exception.printStackT race();
}
}
}
二、Socket编程
传统的C/S模式的网络程序主要通过服务器端和客户端的网络连接来实现数据传输。服务器端和客户端的网络连接主要是TCP Socket连接。在https://www.wendangku.net/doc/bb15278605.html,包中提供了Socket和ServerSocket类来实现连接。
在Socket编程中发送方和接收方的两个Socket之间必须先连接,再才能在TCP协议的基础上进行通信。
服务器的任务就是等候建立一个连接,然后用那个连接产生的Socket 创建一个InputStream 以及一个OutputStream。之后,从InputStream 读入的所有东西都会反馈给
OutputStream,直到接收到行中止(END)为止,最后关闭连接。客户机连接与服务器的连接,然后创建一个OutputStream。文本行通过OutputStream 发送。客户机也会创建一个InputStream,用它收听服务器说些什么。服务器与客户机(程序)都使用同样的端口号,而且客户机利用本地主机地址连接位于同一台机器中的服务器(程序),所以不必在一个物理性的网络里完成测试。
注意,ServerSocket 只要一个端口编号,不需要IP 地址(因为它就在这台机器上运行)。调用accept()时,方法暂时陷入停顿状态,直到某个客户尝试同它建立连接。建好一个连接以后,accept()会返回一个Socket对象,它是那个连接的代表。假如ServerSocket 构建器失败,则程序简单地退出(注意必须保证ServerSocket 的构建器在失败之后不会留下任何打开的网络套接字)。针对这种情况,main()会“掷”出一个IOException 违例,所以不必使用一个try 块。若ServerSocket 构建器成功执行,则其他所有方法调用都必须到一个try-finally 代码块里寻求保护,以确保无论块以什么方式留下,ServerSocket 都能正确地关闭。
同样的道理也适用于由accept()返回的Socket。若accept() 失败,那么我们必须保证Socket 不再存在或者含有任何资源,以便不必清除它们。但假若执行成功,则后续的语句必须进入一个try-finally 块内,以保障在它们失败的情况下,Socket 仍能得到正确的清除。由于套接字使用了重要的非内存资源,所以在这里必须特别谨慎,必须自己动手将它们清除。无论ServerSocket 还是由accept()产生的Socket 都打印到System.out 里。这意味着它们的toString方法会得到自动调用。这样便产生了:
ServerSocket[addr=0.0.0.0,PORT=0,localport=8080]
Socket[addr=127.0.0.1,PORT=1077,localport=8080]
在后面的程序中大家会看到它们如何与客户程序做的事情配合。
服务器端程序:
mport java.io.*;
import https://www.wendangku.net/doc/bb15278605.html,.*;
//写服务器端,对端口进行监听,接受客户端请求并为客户端服务
public class TcpServer{
public static void main(String[] args)
{//服务器对8888端口进行监听,接受到客户端连接请求后,创建和启动服务线程,long count = 0;//声明用来计数的变量
ServerSocket serverSocket = null;//声明服务器套接字
try {
serverSocket = new ServerSocket(8888);//创建绑定到8888端口的ServerSocket 对象
System.out.println("服务器对8888端口实施监听......");//打印提示信息
//服务器循环接收客户端的请求,为不同的客户端提供服务
while (true) {
Socket socket = serverSocket.accept();//接收客户端的连接请求,若有连接请求返回连接对应的Socket对象
count++;
ServerThread serverThread = new ServerThread(socket, count);//创建服务器线程
serverThread.start();//启动服务器线程
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//定义ServerThread类,继承自Thread类
class ServerThread extends Thread
{//根据客户端和计数器来创建服务器线程
Socket socket;
long count;
//构造方法
public ServerThread(Socket socket, long c)
{
this.count = c;
this.socket = socket;
}
//重写run方法
public void run()
{
int timeCounter=0;
try
{
InputStream ins = socket.getInputStream();//获取套接字的输入流
InputStreamReader isr = new InputStreamReader(ins);//封装输入流
BufferedReader br = new BufferedReader(isr);//封装输入流
OutputStream os = socket.getOutputStream();//获取套接字的输入流
PrintStream pw = new PrintStream(os);//封装输出流
while (true) {
timeCounter++;//声明用来计数的变量
String str = br.readLine();//定义字符串为读取br的值
if(str.equals("exit"))
{//如果客户端输入的是“exit”,则关闭客户端
pw.println("exit");
pw.flush();
socket.close();
break;
}
//告知客户端是第几次转换,有几个客户端,并输出转换的字符串
pw.println("这是" + socket.toString() + "第"+timeCounter+"次发送转换请求,现在有"+count+"个客户在线,转换后的字符串为"
+ str.toUpperCase());
pw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端程序:
import java.io.*;
import https://www.wendangku.net/doc/bb15278605.html,.Socket;
//客户端程序
public class TcpClient
{
public static void main(String[] args)
{
try {
Socket socket = new Socket("127.0.0.1", 8888);//创建套接字,传入IP和端口
boolean flag=true;//定义布尔型变量
OutputStream os = socket.getOutputStream();//返回套接字输出流
PrintStream ps = new PrintStream(os);//封装输出流
InputStream ins = socket.getInputStream();//返回套接字输入流
InputStreamReader isr = new InputStreamReader(ins);//封装输入流
BufferedReader br = new BufferedReader(isr);//封装输入流
String str = null;//定义字符串
//获取输入的字符串
BufferedReader kbr = new BufferedReader(new InputStreamReader
(System.in));
while (flag) {
//提示用户输入小写字母,服务器将转换成大写
System.out.println("请输入您要转换的小写字母,服务器将为您转换成
大写字母");
System.out.print("请输入:");
String inputString = kbr.readLine();//定义获取的字符串
ps.println(inputString);//写到服务器端
if ((str = br.readLine()) != null) {//读取数据
if (str.equals("exit")){//如果输入exit,退出循环
flag=false;
}
System.out.println("服务器返回转换信息:"+str);
}
}
socket.close();//关闭客户端
} catch (Exception e) {
e.printStackTrace();
}
}
}
本程序操作方法:
开始、运行、CMD、cd到工作目录,打start,产生二个DOS命令操作界面,分别在二个界面中编译与执行服务器端程序和客户端程序。在客户端输入小写字母等,将显示将其中小写字符变成大写字符的效果。
三、UDP Socket编程
UDP Socket的信息传输方式类似于发传真,发送端先准备好要发送的资料,打电话给接收端,接收端准备好纸张,回一个信号,再开始传真。数据报套接字(DatagramSocket)不需要创建两个Socket,不可以使用输入、输出流。
import https://www.wendangku.net/doc/bb15278605.html,.*;
//定义UDP接收端
public class UDPServer {
public static void main(String[] args) {
byte[] bufferIn=new byte[26];
byte[] bufferOut;
try {
//建立信箱,创建数据包套接字并绑定到13端口
DatagramSocket ds=new DatagramSocket(13);
System.out.println("UDP接收端启动,在13端口..." );
int num=0;
while(num<100){
num++;
//准备收件信封,接收长度为length的数据包
DatagramPacket packetIn=new DatagramPacket(bufferIn,bufferIn.length);
ds.receive(packetIn);//收信,接受数据报
InetAddress ia=packetIn.getAddress();//通过信封得到发信人的地址和端口号
int port=packetIn.getPort();//获取端口
String str=new String(bufferIn);//获取字符串
str=str.toUpperCase();//把字符串变成大写
bufferOut=str.getBytes();//给输出数组赋值
System.out.println("收到的第"+num+"封信,来源ip是"+ia.toString()+",端口号为"+port);
//发信的信封
DatagramPacket packetOut=new DatagramPacket(bufferOut,bufferOut.length,ia,port);
ds.send(packetOut);//发信(回信)
}
ds.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端程序:
import java.io.*;
import https://www.wendangku.net/doc/bb15278605.html,.*;
//定义UDP发送端
public class UDPClient {
public static void main(String[] args) {
byte[] bufferOut=new byte[26];
byte[] bufferIn=new byte[26];
for(int i=0;i bufferOut[i]=(byte)('a'+i);//定义数组元素 } try { //建立信箱,创建数据包套接字并绑定到3333端口 DatagramSocket ds=new DatagramSocket(4444); InetAddress ia=InetAddress.getByName(args[0]);//通过主机名确定主机的IP地址 //准备发信信封 DatagramPacket packetOut=new DatagramPacket(bufferOut,bufferOut.length,ia,13); ds.send(packetOut);//发信 //准备收件信封 DatagramPacket packetIn=new DatagramPacket(bufferIn,bufferIn.length); ds.receive(packetIn);//收信 String str1=new String(packetIn.getData()); String str2=new String(bufferIn); System.out.println(str1); System.out.println(str2); ds.close();//6,关闭信箱 } catch (Exception e) { e.printStackTrace(); } } } 四、网络聊天室 注:本程序关于登录的程序部分使用了awt和swing。需要改相应的内容。 1、协议接口 public interface Protocol { final static String SOFTW ARE = "QQ聊天室";// 软件名 final static String VERSION = "1.4";// 软件版本号 final static String DEFAULT_IP = "127.0.0.1";// 默认IP地址 final static int DEFAULT_PORT = 8888;// 默认端口号 final static int NAME_MAX = 15;// 用户名最大长度 final static int NAME_MIN = 4;// 用户名最小长度 // 敏感词汇 final static String[] FORBID_WORDS = { "萨达姆侯赛因", "切格瓦拉", "恐怖分子" }; final static int TIME_BETWEEN_MSG = 2;// 防刷屏时间设置 final static String SYSTEM_MSG = "1k3N1k";// 系统信息 final static String ADD_USER = "d9Fa0j";// 新用户进入聊天室 final static String DELETE_USER = "9fIk3l";// 用户离开聊天室 final static String EXIST_USERS = "9A3kmc";// 已登陆用户 final static String MSG_FROM = "4lCcj8";// 信息来源标记 final static String NAME_END = "ci8Kja";// 名字信息结尾标记 final static String USER_LOGOUT = "k8O0z2";// 用户退出聊天室 final static String USER_EXIST = "USER_EXIST";// 用户名已有人使用 //登陆名已被使用的异常 final static class ExistException extends Exception { public ExistException() { super("用户名已存在"); } } //版本过期异常 final static class V ersionException extends Exception { public V ersionException(String ver) { super("版本过期,最新版本为v" + ver); } } } 2、服务器端 1)多现场 /** * QQ聊天室服务器端 */ import https://www.wendangku.net/doc/bb15278605.html,.*; import java.io.*; import java.util.*; public class Server implements Protocol { /* * 用户名存入key,线程存入对应value */ private static Map users = new HashMap(); /** * 用户线程类,每个用户一个线程 */ static class UserThread extends Thread { private Socket s; private String username = ""; private PrintWriter out; private static int online;// 统计在线人数 private static String lock = ""; public UserThread(Socket s) { this.s = s; } public void run() { try { /* * 创建流 */ InputStream is = s.getInputStream(); InputStreamReader ir = new InputStreamReader(is, "GBK"); // java.nio.charset.Charset.defaultCharset()); BufferedReader in = new BufferedReader(ir); OutputStream os = s.getOutputStream(); OutputStreamWriter or = new OutputStreamWriter(os, "GBK"); out = new PrintWriter(or); out.println(VERSION); out.flush(); /* * 判断版本是否过期 */ if (!in.readLine().equals(VERSION)) { throw new Exception("版本过期"); } https://www.wendangku.net/doc/bb15278605.html,ername = in.readLine(); synchronized (lock) { /* * 读取用户名,并判断是否已经存在 */ if (isExist(https://www.wendangku.net/doc/bb15278605.html,ername)) { throw new ExistException(); } /* * 登陆成功 */ out.println(SYSTEM_MSG); out.flush(); /* * 通知所有人有新用户登陆 */ sendAll(SYSTEM_MSG + ADD_USER + https://www.wendangku.net/doc/bb15278605.html,ername); /* * 刷新在线人数 */ System.out.print("\rOnline:" + ++online); /* * 给本线程用户发送在线用户列表 */ listAll(); /* * 将本用户加入集合 */ users.put(https://www.wendangku.net/doc/bb15278605.html,ername, this); } String msg = ""; String touser = "All"; while (!s.isClosed()) { while (!s.isClosed() && (msg = in.readLine()) != null && msg.length() > 0) { /* * 收到用户退出的系统信息,删除集合中对应项,通知所有用户 */ if (msg.startsWith(SYSTEM_MSG + USER_LOGOUT)) { synchronized (lock) { users.remove(https://www.wendangku.net/doc/bb15278605.html,ername); } sendAll(SYSTEM_MSG + DELETE_USER + https://www.wendangku.net/doc/bb15278605.html,ername); s.close(); System.out.print("\rOnline:" + --online + " "); } /* * 收到聊天信息,解析出发送对象和信息内容,并发送 */ else { touser = msg.substring(0, msg.indexOf(NAME_END)); msg = msg.replaceFirst(touser + NAME_END, ""); send(msg, touser); } } } } /* * 登陆时出现用户名已存在情况,通知用户 */ catch (ExistException e) { out.println(SYSTEM_MSG + USER_EXIST); out.flush(); } catch (Exception e) { } finally { try { s.close(); } catch (Exception e) { } } } /** * 发送信息给所有用户 */ 2、 private void sendAll(String msg) { Set s = users.keySet(); Iterator it = s.iterator(); while (it.hasNext()) { UserThread t = (UserThread) users.get(it.next()); if (t != this) t.sendUser(msg); } } * 给本线程发送在线用户列表 */ private void listAll() { Set s = users.keySet(); Iterator it = s.iterator(); while (it.hasNext()) { this.sendUser(SYSTEM_MSG + EXIST_USERS + it.next()); } } /** * 判断用户名是否已经有人使用 */ private boolean isExist(String name) { Set s = users.keySet(); Iterator it = s.iterator(); while (it.hasNext()) { if (name.equals((String) it.next())) { return true; } } return false; } /** * 给本线程对应的用户发信息 */ private void sendUser(String msg) { out.println(msg); out.flush(); // System.out.println("to " + https://www.wendangku.net/doc/bb15278605.html,ername + ": " + msg);// 调试用代码} /** * 给指定对象发送信息 */ private void send(String msg, String touser) { /* * 调用相应函数,给所有人发信息时 */ if (touser.equals("All")) { sendAll(https://www.wendangku.net/doc/bb15278605.html,ername + NAME_END + msg); return; /* * 根据发送目标的名字获得相应线程,调用目标线程的函数给目标发送信息 */ if (users.containsKey(touser))// 加判断,防止用户已经离线 ((UserThread) users.get(touser)).sendUser(MSG_FROM + https://www.wendangku.net/doc/bb15278605.html,ername + NAME_END + msg); } } /** * 主方法:启动服务器 */ public static void main(String[] args) { /* * 根据参数的情况,获得端口号,无效时使用默认值,并返回相应信息 */ int port = DEFAULT_PORT; if (args.length > 0) { int newport; try { newport = Integer.parseInt(args[0]); /* * 无效端口 */ if (newport > 65535 || newport < 0) { System.out.println("The port " + newport + " is invalid."); } /* * 操作系统预留端口 */ else if (newport <= 1024) { System.out.println("The port 0~1024 is not allowed."); } else { port = newport; } } /* * 不能转换成整数的参数 */ catch (NumberFormatException e) { System.out.println("Invalid port number!"); } } ServerSocket ss = new ServerSocket(port); System.out.print("Server is running.\nPort:" + port + "\nOnline:0"); while (true) { Socket s = ss.accept(); Thread t = new UserThread(s); t.start(); } } /* * 端口绑定失败 */ catch (IOException e) { System.out.println("Failed to bind " + port + "port."); } } } 3、客户端代码: //聊天室客户端 public class Client { //主方法:启动登陆线程 public static void main(String[] args) throws Exception { Thread login = new LoginThread(); login.start(); } } 4、客户端登录线程 import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import https://www.wendangku.net/doc/bb15278605.html,.Socket; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import lianxi.Protocol.ExistException; import lianxi.Protocol.V ersionException; /** * 登陆线程 */ public class LoginThread extends Thread implements Protocol { private JFrame loginf; private JTextField t; public void run() { /* * 设置登陆界面 */ loginf = new JFrame(); loginf.setLocation(300, 200); loginf.setSize(400, 150); loginf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); loginf.setTitle(SOFTW ARE + " - 登陆"); t = new JTextField("V ersion " + VERSION + " By Lindan"); t.setHorizontalAlignment(JTextField.CENTER); t.setEditable(false); loginf.getContentPane().add(t, BorderLayout.SOUTH); JPanel loginp = new JPanel(new GridLayout(4, 2)); loginf.getContentPane().add(loginp); JTextField t1 = new JTextField("登陆名:"); t1.setHorizontalAlignment(JTextField.CENTER); t1.setEditable(false); loginp.add(t1); final JTextField loginname = new JTextField(""); loginname.setHorizontalAlignment(JTextField.CENTER); loginp.add(loginname); JTextField t2 = new JTextField("服务器IP:"); t2.setHorizontalAlignment(JTextField.CENTER); t2.setEditable(false); loginp.add(t2); final JTextField loginIP = new JTextField(DEFAULT_IP); loginIP.setHorizontalAlignment(JTextField.CENTER); loginp.add(loginIP); JTextField t3 = new JTextField("端口号:"); t3.setHorizontalAlignment(JTextField.CENTER); t3.setEditable(false); loginp.add(t3); final JTextField loginport = new JTextField(DEFAULT_PORT + ""); loginport.setHorizontalAlignment(JTextField.CENTER); loginp.add(loginport); /* * 监听退出按钮(匿名内部类) */ JButton b1 = new JButton("退出"); loginp.add(b1); b1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); final JButton b2 = new JButton("登陆"); loginp.add(b2); loginf.setVisible(true); /** * 监听器,监听"登陆"Button的点击和TextField的回车 */ class ButtonListener implements ActionListener { private Socket s; public void actionPerformed(ActionEvent e) { if (checkName(loginname.getText())) { try { s = new Socket(loginIP.getText(), Integer .parseInt(loginport.getText())); /* * 连接服务器 */ try { InputStream is = s.getInputStream(); InputStreamReader ir = new InputStreamReader(is, "GBK"); // java.nio.charset.Charset.defaultCharset()); BufferedReader in = new BufferedReader(ir); OutputStream os = s.getOutputStream(); OutputStreamWriter or = new OutputStreamWriter(os, "GBK"); PrintWriter out = new PrintWriter(or); out.println(VERSION); out.flush(); out.println(loginname.getText()); out.flush(); String ver; if (!(ver = in.readLine()).equals(VERSION)) { throw new V ersionException(ver); } if (in.readLine().equals(USER_EXIST)) { throw new ExistException(); } /* * 启动聊天线程 */ Thread chat = new ChatThread(loginname.getText(), s, in, out); loginf.setVisible(false); loginf.setEnabled(false); chat.start(); } catch (IOException e1) {// 流操作异常 t.setText("通讯失败,请重试!"); try { s.close(); } catch (IOException e2) { } } catch (V ersionException e3) {// 用户存在异常(接口中定义) t.setText(e3.getMessage()); try { s.close(); } catch (IOException e4) { } } catch (ExistException e5) {// 用户存在异常(接口中定义) t.setText(e5.getMessage()); try { s.close(); } catch (IOException e6) { } } } catch (IOException e7) {// Socket连接服务器异常 t.setText("连接服务器失败,请重试!"); } } } } ButtonListener bl = new ButtonListener(); b2.addActionListener(bl); loginname.addActionListener(bl); loginIP.addActionListener(bl); loginport.addActionListener(bl); } /** * 判断登陆名是否有效 */ private boolean checkName(String name) { if (name.length() < NAME_MIN) { t.setText("错误:登陆名不能小于" + NAME_MIN + "字符"); return false; } if (name.length() > NAME_MAX) { t.setText("错误:登陆名不能大于" + NAME_MAX + "字符"); return false; } if (name.indexOf(" ") > -1) { t.setText("错误:登陆名不能包含空格"); return false; } for (int i = 0; i < FORBID_WORDS.length; i++) { if (name.indexOf(FORBID_WORDS[i]) > -1) { t.setText("错误:登陆名不能包含敏感信息"); return false; } } return true; } }