Java网络编程基础:Socket客户端与服务端完整示例
本文将深入解析Java网络编程的核心概念,涵盖如何通过Socket建立TCP连接、设置合理的超时机制以防止程序无限阻塞,以及如何利用InetAddress类获取主机IP。同时,通过一个支持多线程处理和多用户聊天的完整ServerSocket示例,展示服务端程序的并发处理逻辑与生命周期管理。
一、使用Socket连接到服务器
在Java中,java.net.Socket及其相关类简化了TCP协议下网络连接和数据传输的复杂过程。要创建一个指向特定主机的连接,可以使用构造函数传入主机IP或域名以及目标端口号:
Socket sk = new Socket("210.0.235.14",13);
以上代码尝试连接香港授时服务器(端口13)。需要注意的是,此操作在连接成功前会导致当前线程阻塞。
第1步:配置超时参数防止死锁
目的:解决new Socket(host,port)在无响应时无限阻塞的问题。
方法A(推荐):先实例化空对象再连接并指定超时时间:
Socket sk = new Socket();
sk.connect(new InetSocketAddress(host,port),2000); // 设置2秒超时
方法B:对已建立的流设置读写超时:
sk.setSoTimeout(3000);
第2步:执行数据交互
连接成功后,通过输入输出流进行通信。以下代码演示连接授时服务器并打印返回内容:
try {
Socket sk = new Socket("210.0.235.14", 13);
sk.setSoTimeout(3000); // 设置超时保护
InputStream inStream = sk.getInputStream();
Scanner sc = new Scanner(inStream);
while(sc.hasNextLine()) {
String str = sc.nextLine();
System.out.println("Output : " + str);
}
sk.close();
} catch (SocketTimeoutException e) {
System.out.println("Time Out!");
} catch (Exception e) {
e.printStackTrace();
}
二、使用InetAddress解析主机地址
通过InetAddress类可以获取本地或远程主机的IP信息,常用于负载均衡场景下的多IP识别:
getByName(hostname):返回代表该主机的InetAddress对象。getAllByName():若主机对应多个IP以实现负载均衡,可获取所有地址数组。
示例工具类可接收命令行参数,若未传入则打印本机IP,否则打印指定域名的所有有效IP:
package cls;
import java.net.*;
public class ShowIP {
public static void main(String[] args) throws Exception {
if(args.length > 0) {
InetAddress[] addr = InetAddress.getAllByName(args[0]);
for(InetAddress address : addr) {
System.out.println(address.getHostAddress());
}
} else {
System.out.println(InetAddress.getLocalHost().getHostAddress());
}
}
}
三、编写支持并发服务的ServerSocket
服务端需使用ServerSocket绑定本地端口并监听连接。accept()方法会阻塞直到收到客户端请求,此时返回一个新的Socket实例代表该连接。
第1步:初始化服务端并开启循环监听
服务端在主线程中维持循环调用accept(),并为每个新连接生成唯一编号用于标识。
第2步:多线程隔离处理单个连接
为避免服务被单一长连接拖慢,应使用独立的线程处理每个客户端的消息收发与日志写入。以下是完整的并发服务端实现:
package cls;
import java.io.*;
import java.net.*;
import java.util.*;
public class ServerDemo {
public static void main(String[] args) {
try {
ServerSocket servSocket = new ServerSocket(8400);
int amount = 0;
while(true) {
Socket client = servSocket.accept();
++amount;
Date time = new Date();
String prompt = time.toString() + ": 第" + amount + "个用户 " + client.getInetAddress().getHostAddress() + " 已连接\n";
writeLog(prompt); // 记录上线日志
// 启动新线程为当前客户服
Thread th = new Thread(new ServThread(client, amount));
th.start();
}
} catch(Exception e) { e.printStackTrace(); }
}
public static void writeLog(String str) {
File logFile = new File("server-log.txt");
try {
FileWriter out = new FileWriter(logFile, true);
out.append(str);
out.close();
} catch(Exception e) { e.printStackTrace(); }
}
}
class ServThread implements Runnable {
private Socket client;
private int ix;
public ServThread(Socket soc, int ix) { client = soc; this.ix = ix; }
public void run() {
try {
InputStream inStream = client.getInputStream();
OutputStream outStream = client.getOutputStream();
Scanner recv = new Scanner(inStream);
PrintWriter send = new PrintWriter(outStream, true);
send.println("欢迎!输入'bye'关闭联接.");
while(recv.hasNextLine()) {
String str = recv.nextLine();
if(str.equals("bye")) {
send.println("See you later ~ ^-^");
break;
}
send.println("这是一个测试程序。");
}
// 下线记录
Date time = new Date();
String prompt = time.toString() + ": 第" + ix + "个用户已断开连接\n";
ServerDemo.writeLog(prompt);
client.close();
} catch(Exception e) { e.printStackTrace(); }
}
}
运行上述服务端后,可通过telnet localhost 8400命令连接并体验简单的文字对话服务。
总结
掌握Socket的超时控制与InetAddress的应用是构建可靠网络工具的基础;而理解ServerSocket与Thread的结合模式则是开发高性能并发网络应用的前提。以上就是Java网络编程中Socket连接与简单服务端应用的详细内容,更多关于Java进阶编程技巧的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









