前言
这个小型的聊天系统我没有用swt去做出界面出来,因为之前用的32位的,现在换了个环境导致之前的包不能用了,只能以控制台进行信息的交互。另外,这里介绍的是基于TCP的,UDP的简单一些就不介绍了。
基本代码
服务端
package thread.net;
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 java.util.Scanner;
public class Server {
public static void main(String[] args) {
// 指定服务器启动的时候占用的端口号,端口号取值是0~65535,1024一下预留给系统
ServerSocket ssk = null;
Socket sk = null;
try {
ssk = new ServerSocket(8888);
System.out.println("服务器启动成功...");
while (true) {
sk = ssk.accept();
new Thread(new MyServer(sk)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class MyServer implements Runnable {
private Socket sk = null;
private BufferedReader read = null;
private PrintWriter pw = null;
public MyServer(Socket sk) {
this.sk = sk;
}
@Override
public void run() {
Scanner sc = new Scanner(System.in);
try {
read = new BufferedReader(new InputStreamReader(sk.getInputStream()));
pw = new PrintWriter(sk.getOutputStream());
String line = read.readLine();
System.out.println("客户端问:" + line);
while (!"bye".equalsIgnoreCase(line)) {
System.out.println("请输入回答:");
line = sc.nextLine();
pw.println(line);
pw.flush();
line = read.readLine();
System.out.println("客户端问:" + line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
if (read != null) {
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (sk != null) {
try {
sk.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端
package thread.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/**
* Socket和ServerSocket 面向应用层编程
*
* @author yuer
*
*/
public class Cilent2 {
public static void main(String[] args) {
Socket sk = null;
BufferedReader read = null;
PrintWriter pw = null;
@SuppressWarnings("resource")
Scanner sc = new Scanner(System.in);
try {
sk = new Socket("127.0.0.1", 8888);
read = new BufferedReader(new InputStreamReader(sk.getInputStream()));
pw = new PrintWriter(sk.getOutputStream());
System.out.println("请输入您的问题:");
String line = sc.nextLine();
while (!"bye".equalsIgnoreCase(line)) {
pw.println(line);
pw.flush();
line = read.readLine();
System.out.println("服务器回答说:" + line);
System.out.println("请输入您的问题:");
line = sc.nextLine();
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
if (read != null) {
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (sk != null) {
try {
sk.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
说明:
服务端将每个客户端的请求封装为一个线程,这里也可以使用线程池对这些线程进行管理,这个就自己补充吧。还有就是改进点就是应该将读和写分开用线程包装,这样就不会出现写之前必须先读了。还有这里服务器可以改进为只当作一个中转站。
升级版的
服务端:
package com.yuer.io.net;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
// 指定服务器启动的时候占用的端口号,端口号取值是0~65535,1024一下预留给系统
ServerSocket ssk = null;
Socket sk = null;
try {
ssk = new ServerSocket(8888);
System.out.println("服务器启动成功...");
while (true) {
sk = ssk.accept();
new Thread(new MySend(sk)).start();
new Thread(new MyReceive(sk)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
package com.yuer.io.net;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* Socket和ServerSocket 面向应用层编程
*
* @author yuer
*
*/
public class Cilent {
public static void main(String[] args) {
Socket sk = null;
try {
sk = new Socket("127.0.0.1", 8888);
new Thread(new MySend(sk)).start();
new Thread(new MyReceive(sk)).start();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
发送信息线程
package com.yuer.io.net;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* 发送信息的线程
* @author Yuer
*
*/
public class MySend implements Runnable {
private Socket sk = null;
private PrintWriter pw = null;
public MySend(Socket sk) {
this.sk = sk;
}
@SuppressWarnings("resource")
@Override
public void run() {
Scanner sc = new Scanner(System.in);
try {
pw = new PrintWriter(sk.getOutputStream());
String line = "";
while (true) {
System.out.println("我说:");
line = sc.nextLine();
pw.println(line);
pw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
if (sk != null) {
try {
sk.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
接受信息线程
package com.yuer.io.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 接受信息的线程
* @author Yuer
*
*/
public class MyReceive implements Runnable {
private Socket sk = null;
private BufferedReader read = null;
public MyReceive(Socket sk) {
this.sk = sk;
}
@Override
public void run() {
try {
read = new BufferedReader(new InputStreamReader(sk.getInputStream()));
String line = read.readLine();
System.out.println("他say:" + line);
while (!"bye".equalsIgnoreCase(line)) {
line = read.readLine();
System.out.println("他say:" + line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (read != null) {
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (sk != null) {
try {
sk.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
说明:
这个比之前的优化点在于将发送和接受两个逻辑分别封装为一个线程,解决了读写之间的阻塞。接下来的优化点在于服务端应该只充当一个中转方,使用@某人进行私聊。还有优化点在于心跳包机制保证socket长连接不掉线,每5秒发送一次心跳包,个人进行思考完成即可。哈哈哈