【Java、TCP Socket】用JavaTCP Socket实现简单的基于C/S的对聊程序,一对一和一对多


一、TCP Socket编程的工作流程

Java提供ServerSocket和Socket类实现TCP Socket通信。ServerSocket类提供TCP连接服务;Socket类提供进行通信的Socket对象。
(1)服务器端程序中包含一个提供TCP连接服务的ServerSocket对象和一个参与通信的Socket对象,客户端程序中只包含一个参与通信的Socket对象。
(2)服务器端的ServerSocket对象提供TCP连接服务,连接成功后,实际进行通信的是服务器端的Socket对象和客户端的Socket对象。
(3)在服务器端创建一个 ServerSocket对象,指定端口号, ServerSocket类的accept()方法使服务器处于阻塞状态,等待用户请求;
(4)在客户端创建一个 socket对象,指定主机地址和端口号,连到服务器上;
(5)服务器端接收到客户端的连接请求,建立一条TCP连接,再创建一个Socket对象与客户端的Socekt对象进行通信;
(6)服务器端和客户端分别创建字节输入流和字节输出流,通过字节输入流获得对方发来的数据,通过字节输出流向对方发送数据;
(7)当一方决定结束通信时,向对方发送结束信息;另一方收到结束信息后,双方分别关闭各自的TCP连接;ServerSocket对象停止等待客户端的连接请求。
在这里插入图片描述
(图片来源网络,侵权联系删除)

二、使用TCP Socket设计一个简单的基于C/S的对聊程序。

1、客户端

代码如下:

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
 
public class client {
    
    
	public static void main(String[] args) {
    
    
		try{
    
    
			Socket client=new Socket("127.0.0.1",5555);
			Scanner in=new Scanner(client.getInputStream());
			System.out.println(in.nextLine());
			PrintWriter pw=new PrintWriter(client.getOutputStream());
			System.out.println("客户端1:");
			Scanner out=new Scanner(System.in);//用来读取键盘输入
			while(out.hasNextLine()){
    
    
				String data=out.nextLine();
				pw.println("客户端1:"+data);//将内容写到Socket
				pw.flush();//将缓冲区内容强制写入目标设备
				String indata=in.nextLine();//等待服务器发送内容。利用Scanner没有获取到服务器的数据则将线程阻塞。
				if(indata.equals("break")) break;
				System.out.println("服务器:"+indata);
				System.out.println("客户端1:");
			}
			in.close();
			pw.close();
			out.close();
			client.close();
		}catch(IOException e){
    
    
			e.printStackTrace();
		}
	}
}

2、服务器

代码如下:

package shiyan8;

import java.io.IOException;
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) throws IOException {
    
    
		try{
    
    
			System.out.println("等待客户端连接……");
			ServerSocket server=new ServerSocket(6666);
			Socket socket=server.accept();
			PrintWriter pw=new PrintWriter(socket.getOutputStream());
			pw.println("连接成功!请您先发言!");
			pw.flush();
			Scanner in=new Scanner(socket.getInputStream());//客户端的数据输入流
			while(in.hasNextLine()){
    
    
				String indata=in.next();//取客户端发送的内容
				System.out.println(indata);
				System.out.println("服务器:");
				Scanner outdata=new Scanner(System.in);//服务器发送给客户端的数据输出流
				String out=outdata.next();
				if(out.equals("break")) break;
				pw.println(out);//将内容写到socket中
				pw.flush();
			}
			in.close();
			pw.close();
			socket.close();
 		}catch(IOException e){
    
    
			e.printStackTrace();
		}
	}
}

3、测试结果

客户端连接上服务器后,服务器先发送连接成功的消息,让客户端先发送消息,服务器收到消息后,在输入消息回复给客户端。这样就实现了简单的聊天。

(1)客户端

在这里插入图片描述

(2)服务器

在这里插入图片描述

三、利用多线程扩展,实现一对多的对聊程序

1、客户端1、客户端2

代码如下:

//客户端1
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
 
public class client {
    
    
	public static void main(String[] args) {
    
    
		try{
    
    
			Socket client=new Socket("127.0.0.1",5555);
			Scanner in=new Scanner(client.getInputStream());
			System.out.println(in.nextLine());
			PrintWriter pw=new PrintWriter(client.getOutputStream());
			System.out.println("客户端1:");
			Scanner out=new Scanner(System.in);//用来读取键盘输入
			while(out.hasNextLine()){
    
    
				String data=out.nextLine();
				pw.println("客户端1:"+data);//将内容写到Socket
				pw.flush();//将缓冲区内容强制写入目标设备
				String indata=in.nextLine();//等待服务器发送内容。利用Scanner没有获取到服务器的数据则将线程阻塞。
				if(indata.equals("break")) break;
				System.out.println("服务器:"+indata);
				System.out.println("客户端1:");
			}
			in.close();
			pw.close();
			out.close();
			client.close();
		}catch(IOException e){
    
    
			e.printStackTrace();
		}
	}
}
//客户端2
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
 
public class client1 {
    
    
	public static void main(String[] args) {
    
    
		try{
    
    
			Socket client=new Socket("127.0.0.1",5555);
			Scanner in=new Scanner(client.getInputStream());
			System.out.println(in.nextLine());
			PrintWriter pw=new PrintWriter(client.getOutputStream());
			System.out.println("客户端2:");
			Scanner out=new Scanner(System.in);//用来读取键盘输入
			while(out.hasNextLine()){
    
    
				String data=out.nextLine();
				pw.println("客户端2:"+data);//将内容写到Socket
				pw.flush();//将缓冲区内容强制写入目标设备
				String indata=in.nextLine();//等待服务器发送内容。利用Scanner没有获取到服务器的数据则将线程阻塞。
				if(indata.equals("break")) break;
				System.out.println("服务器:"+indata);
				System.out.println("客户端2:");
			}
			in.close();
			pw.close();
			out.close();
			client.close();
		}catch(IOException e){
    
    
			e.printStackTrace();
		}
	}
}

2、多线程服务器

import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class multiServer {
    
    

	public static void main(String[] args) throws IOException {
    
    
		// TODO Auto-generated method stub
		ThreadPoolExecutor executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(4);
		ServerSocket server=new ServerSocket(5555);
		while(true){
    
    
			try{
    
     
				Socket socket=server.accept(); 
				//主线程获取客户端连接Thread 
				executor.submit(new Processor(socket));
			}catch(Exception e){
    
     
				e.printStackTrace(); 
			} 
			} 

	}
}
class Processor implements Runnable{
    
         
	private Socket socket;     
	public Processor(Socket socket){
    
             
		this.socket=socket;     
	}     
	public void run(){
    
             
	try{
    
                   
		PrintWriter pw=new PrintWriter(socket.getOutputStream());
		pw.println("连接成功!请您先发言!");
		pw.flush();
		Scanner in=new Scanner(socket.getInputStream());//客户端的数据输入流
		while(in.hasNextLine()){
    
    
			String indata=in.next();//取客户端发送的内容
			System.out.println(indata);
			System.out.println("服务器:");
			Scanner outdata=new Scanner(System.in);//服务器发送给客户端的数据输出流
			String out=outdata.next();
			pw.println(out);//将内容写到socket中
			pw.flush();
		}
		Thread.sleep(10); 
		if(socket!=null)socket.close();         
	}catch(IOException e){
    
                     
		e.printStackTrace();         
	} catch (InterruptedException e) {
    
    
		// TODO Auto-generated catch block
		e.printStackTrace();
	}     
	} 
}

3、测试结果

(1)客户端1

在这里插入图片描述

(2)客户端2

在这里插入图片描述

(3)多线程服务器

在这里插入图片描述


总结

客户端和服务器的通信,socket通过Ip地址、端口号实现客户端和服务器的数据传输。连接建立以后,可以通过OutputStream和InputStream进行通信,关闭连接时,先关闭其他所有的输入输出流,最后关闭socket。实现一对多的通信时,利用了线程池的概念,让服务器一直监听客户端的连接。之后利用客户端的socket创建一个Process进程,利用submit方法提交个线程池运行。实现Runable接口的run方法与之前编写一对一服务器的思路一致。
不足:一对多服务器客户端的实现过程还不是很完善,还存在一些问题。例如:客户端1和客户端2都发送给服务器时,服务器没办法选择将消息发送给指定的客户端。不符合实际的需求。采用Scanner阻塞客户端线程的方法,代表了服务器在要发送给两个客户端消息的时候,是没办法知道消息发送给了那个客户端,可能是客户端1也可能是客户端2。这取决于服务器发送之后,是客户端1还是客户端2抢占了CPU的资源。谁先抢到就收到这个消息。这也不符合实际的需求。
希望大佬能够帮助解决这个问题,感激不尽。

猜你喜欢

转载自blog.csdn.net/qq_51720181/article/details/125553812