使用java网络编程模拟简单网络即时通信
通信流程图:
解析:
1.在上图中我们可以看出对于任何一个客户端,都由两部分构成,发送端和接收端(分别由两个线程来维系)
2.客户端在每一次请求链接时都会轮询,向服务器发送请求,服务器发回当前在线列表
3.服务器端使用线程池技术为每一个连接请求创建一个线程去处理。
4.在客户端与服务器之间使用TCP通讯,可靠通信
5.在用户之间使用UDP报文来传输数据。
代码实现:
Chat类:
package cn.csuft.poorguy.homework;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Scanner;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
public class Chat {
Thread sender;
Thread receiver;
DatagramSocket udpSocket;
Socket tcpSocket;
public Chat() {
try {
Scanner cin = new Scanner(System.in);
System.out.println("昵称:");
String nick = cin.nextLine();
//TODD C/S
tcpSocket = new Socket("127.0.0.1",9000);
InputStream in = tcpSocket.getInputStream();
OutputStream out = tcpSocket.getOutputStream();
//发送自己的昵称和端口
udpSocket = new DatagramSocket();
int udpPort = udpSocket.getLocalPort();
String msg = new String(nick+","+udpPort);
out.write(msg.getBytes());
out.flush();
//接收在线用户的列表
byte []buf = new byte[1024];
int size = in.read(buf);
String json = new String(buf,0,size);
//HashMap<String, Integer> users = new Gson().fromJson(json,HashMap.class);
HashMap<String, Integer> users = JSON.parseObject(json, HashMap.class);
System.out.println("在线列表 : "+users);
//double p = users.get(nick).doubleValue();
//把用户列表传给SenderTask
sender = new Thread(new SendTask(udpSocket,users,tcpSocket));
receiver = new Thread(new ReceiverTask(udpSocket));
sender.start();
receiver.start();
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
Chat chat = new Chat();
}
}
ChatServer类:
package cn.csuft.poorguy.homework;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ChatServer {
ServerSocket serverSocket;
int serverPort = 9000;
ExecutorService pool;
Map<String, Integer> users = new HashMap<String, Integer>();
public ChatServer() {
pool = Executors.newCachedThreadPool();
}
public void start() {
System.out.println("服务器启动:。。。。");
try {
serverSocket = new ServerSocket(serverPort);
while(true) {
Socket socket = serverSocket.accept();
OnlineService onlineService = new OnlineService(socket,users);
pool.execute(onlineService);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
ChatServer chatServer = new ChatServer();
chatServer.start();
}
}
OnlineService类:
package cn.csuft.poorguy.homework;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
public class OnlineService implements Runnable{
Socket socket;
Map<String, Integer> users;
public OnlineService() {
// TODO Auto-generated constructor stub
}
public OnlineService(Socket socket, Map<String, Integer> users) {
this.socket = socket;
this.users = users;
}
@Override
public void run() {
try (InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream()){
byte []buf = new byte[1024];
int size = in.read(buf);
String msg = new String(buf,0,size);
//解析出msg中的nick信息
String nick = msg.split(",")[0];
int port = Integer.parseInt(msg.split(",")[1]);
//将nick与端口放入在线列表中
users.put(nick, port);
//String json = new Gson().toJson(users);
String json = JSON.toJSONString(users);
System.out.println(json);
out.write(json.getBytes());
out.flush();
} catch (Exception e) {
// TODO: handle exception
}
}
}
ReceiveTask类:
package cn.csuft.poorguy.homework;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class ReceiverTask implements Runnable {
DatagramSocket socket = null;
public ReceiverTask() {
// TODO Auto-generated constructor stub
}
public ReceiverTask(DatagramSocket socket) {
this.socket = socket;
}
@Override
public void run() {
String msg = null;
byte []buf = new byte[1024*8];
do {
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try {
socket.receive(packet);
byte []data = packet.getData();
msg = new String(data,0,packet.getLength());
System.out.println("收到: "+msg);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} while (!msg.equalsIgnoreCase("bye"));
System.out.println("接收端关闭");
}
}
SendTask类:
package cn.csuft.poorguy.homework;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Scanner;
import com.alibaba.fastjson.JSON;
public class SendTask implements Runnable {
HashMap<String, Integer> users;
DatagramSocket socket = null;
Socket tcpSocket;
public SendTask() {
// TODO Auto-generated constructor stub
}
public SendTask(DatagramSocket socket, HashMap<String, Integer> users, Socket tcpSocket) {
this.socket = socket;
this.users = users;
this.tcpSocket = tcpSocket;
}
@Override
public void run() {
String msg = null;
Scanner cin = new Scanner(System.in);
DatagramPacket packet = null;
while (true) {
GetUsersFromServer();
System.out.println("输入接收方:");
String nick = cin.nextLine();
if (users.containsKey(nick)) {
do {
int port = users.get(nick);
System.out.print("发送:");
msg = cin.nextLine();
byte[] data = msg.getBytes();
try {
// 创建数据包
packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), port);
// 发送
socket.send(packet);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} while (!msg.equalsIgnoreCase("bye"));
System.out.println("发送结束");
} else {
System.out.println("该用户不在线");
}
}
}
private void GetUsersFromServer() {
// TODD C/S
try {
tcpSocket = new Socket("127.0.0.1", 9000);
InputStream in = tcpSocket.getInputStream();
OutputStream out = tcpSocket.getOutputStream();
// 发送自己的昵称和端口
int udpPort = socket.getLocalPort();
String ss = new String("######" + "," + udpPort);
out.write(ss.getBytes());
out.flush();
// 接收在线用户的列表
byte[] buf = new byte[1024];
int size = in.read(buf);
String json = new String(buf, 0, size);
// HashMap<String, Integer> users = new Gson().fromJson(json,HashMap.class);
users = JSON.parseObject(json, HashMap.class);
System.out.println("在线列表 : " + users);
} catch (UnknownHostException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
注意:
这里服务器发回的在线列表使用的是fastJson转换成JSON的,在这个过程中遇到了一些晓得问题,之前选用的Gson,但是发现Gson会默认将int转成double,会出现一些错误,所以改用fastJson,建议以后可以直接使用fastJson避免出现类似的错误。