基于TCP通讯实现简单的群聊功能
TCP较于UDP来说,数据安全,但是效率没有UDP高;原因是TCP协议在传输上,会验证数据的完整性,而UDP不会。
- UDP只是单纯的负责发送信息和内容,不会去care你发送的内容这意味着,并且在传输过程中容易丢包
- 而TCP在发送的时候会验证数据的完整性,在发送的时候 如果出错了则不会进行发送,正是因为多了这一步操作,所以发送速度较于UDP会相对慢一些
且UDP的发送主要关注两个点:DatagramSocket、DatagramPacket 前者是负责发送数据包,而后者则是将数据倒成包
而TCP的发送是通过IO流的方式进行发送,且当服务启动以后,就不会区分服务器和客户端了
- 简单一对一聊天:
首先服务端调用ServerSocket对象,并且指定客户端端口,同时调用accept方法等待客户端进行连接
System.out.println("server");
ServerSocket serverSocket = new ServerSocket(8888);
while (true){
new Thread(()->{
boolean flg = true;
//接收消息
DataInputStream dis = null;
try {
Socket socket = serverSocket.accept();
System.out.println("客户端建立连接");
dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
while(flg){
String msg = dis.readUTF();
System.out.println(msg);
//返回消息
dos.writeUTF(msg);
flg = msg.equals("bye")?false:true;
}
dos.flush();
dos.close();
dis.close();
socket.close();
同时客户端指定服务端需要访问的端口,并通过IO进行流的输入和输出。
System.out.println("client");
//后期可以动态传入ip实现两个电脑端相互聊天
Socket server = new Socket("localhost",8888);
DataOutputStream dos = new DataOutputStream(server.getOutputStream());
DataInputStream dis = new DataInputStream(server.getInputStream());
Scanner sc = new Scanner(System.in);
System.out.println("请输入要传输的内容");
boolean flg= true;
while (flg){
String test = sc.next();
dos.writeUTF(test);
System.out.println(dis.readUTF());
flg = test.equals("bye")?false:true;
}
dos.close();
dis.close();
server.close();
dos.flush();
这样,双向聊天的功能就基本实现了,但是现在的代码,只能实现一问一答,也就是说你发送一条信息,必须等对方回复后你才能继续提问。这样就显得很被动。由此就可以看上面代码的鸡肋
所以 我们还需要在上面的代码上进行扩展,实现群聊以及解除一问一答限制的需求。
这里直接上代码,通过代码进行讲解。
服务端:
package com.test.javaUDPTCPtest.Level2TCP;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;
public class Server{
//不使用ArrayList的原因是在聊天过程中可能会对list进行各种操作,所以在这里结合情况,选择使用CopyOnWriteArrayList
private static CopyOnWriteArrayList<channel> channels = new CopyOnWriteArrayList<>();
public static void main(String[] args) throws IOException {
//创建接口
ServerSocket serverSocket = new ServerSocket(8888);
//循环接收
while (true) {
Socket socket = serverSocket.accept();
channel ch = new channel(socket);
channels.add(ch);
System.out.println("客户端建立连接");
new Thread(ch).start();
}
}
//一个channel代表一个用户
static class channel implements Runnable{
private Socket socket;
private ServerSocket server;
private DataInputStream dis;
private DataOutputStream dos;
private boolean flg = true;
private String name;
public channel(Socket socket) {
this.socket = socket;
this.name = name;
try {
//输出流
dos = new DataOutputStream(socket.getOutputStream());
//输入流
dis = new DataInputStream(socket.getInputStream());
this.name = receive();
} catch (IOException e) {
}
}
//接收
public String receive() throws IOException {
String msg = "";
msg = dis.readUTF();
System.out.println("客户端发送:"+msg);
return msg;
}
//发送
public void send(String msg) throws IOException {
dos.writeUTF(msg);
}
//对信息的发送人进行判断,确保信息只发给除了自己以外的人(后期可做扩张)
public void sendmy(String msg) throws IOException {
//约定私聊格式为@xxx:msg
boolean first = msg.startsWith("@");
if(true){
int idx = msg.indexOf(":");
String targetname = msg.substring(1,idx);
msg = msg.substring(idx+1);
for(channel c : channels){
if(c.name.equals(targetname)){
c.send(this.name+"私聊你说:"+msg);
}
}
}else{
for(channel c : channels){
if(c==this){
continue;
}else{
c.send(this.name+"对所有人说:"+msg);
}
}
}
}
@Override
public void run() {
while(flg){
String msg = "";
try {
msg = receive();
sendmy(msg);
} catch (IOException e) {
}
if(msg.equals("end")){
flg = false;
try {
dos.close();
dos.flush();
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
客户端:
package com.test.javaUDPTCPtest.Level2TCP;
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) throws IOException{
Socket server = new Socket("localhost",8888);
System.out.println("请输入用户名");
String name = new Scanner(System.in).next();
new Thread(new Send(server,name)).start();
new Thread(new Receive(server)).start();
}
}
客户端接收方法:
package com.test.javaUDPTCPtest.Level2TCP;
import java.io.*;
import java.net.Socket;
public class Receive implements Runnable{
private Socket socket;
private DataInputStream dis;
private boolean flg= true;
public Receive(Socket socket) throws IOException {
this.socket = socket;
dis = new DataInputStream(socket.getInputStream());
}
public void receive() throws IOException {
String msg = dis.readUTF();
System.out.println(msg);
flg = msg.equals("end")?false:true;
}
@Override
public void run() {
while(flg){
try {
receive();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端发送方法:
package com.test.javaUDPTCPtest.Level2TCP;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Send implements Runnable{
private BufferedReader bf;
private Socket socket;
private DataOutputStream dos;
private boolean flg= true;
private String name;
public Send(Socket socket,String name) throws IOException {
System.out.println("请输入内容");
this.socket = socket;
this.name = name;
bf = new BufferedReader(new InputStreamReader(System.in));
dos = new DataOutputStream(socket.getOutputStream());
//发送用户名
send(name);
}
public void send(String msg) throws IOException {
dos.writeUTF(msg);
flg = msg.equals("end")?false:true;
}
@Override
public void run() {
while(flg){
try {
String msg = bf.readLine();
send(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这样,一个乞丐版聊天室就做好了 这里并没有引进web场景和模块 而是通过console进行模拟交流,但是正常网页的嵌套客服聊天功能是可以在此基础上进行修改的 方法也比较简单在这里不多做赘述,有兴趣的朋友可以自己尝试一下。