Socket_一个简单的模拟聊天的代码_多线程实现收发信息收发信息无先后顺序_day_33

1.Socket

基于TCP的面向连接的  安全可靠但是效率就会变低;

不同的协议的端口号是可以重复的,同一个协议不可以;

Tcp客户端的端口是电脑自己指定的  不需要我们分配;

Tcp的1024一下的端口号不要使用是留给系统的;

Socket连接的示意图:


接收客户端连接   阻塞式

Socket socket =ser.accept(); 所谓阻塞式就是不接收到不能继续执行程序

一个服务器一个客户的示例:

package TCP;

import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
必须先启动服务器 后连接
1、创建服务器  指定端口   ServerSocket(int port) 
2、接收客户端连接  
3、发送数据+接收数据
* 
*/

public class Server {
	public static void main(String[] margs) throws IOException {
		//1、创建服务器  指定端口   ServerSocket(int port)
		ServerSocket ser = new ServerSocket(8888);
		//2、接收客户端连接   阻塞式
		Socket socket =ser.accept();  //特别要注意这个地方就是服务器端要接收 客户端的链接  而且他在调用getOutputStream()时要用socket.getOutputStream()而不是用ser.
		System.out.println("一个客户端建立连接");
		//3、返回数据
		String msg ="欢迎使用";//我们在这里发送一个字符串
		
		/*
		//这里我们用输出流来发送数据   因为我们发送的是String  为了更方便的处理他  我们使用bufferedWriter包装一下
		//我们用buffered的主要目的是为了有 写一行行分隔符。  来结束那个阻塞式的方法
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//
		bw.write(msg);//这个方法也是阻塞式的   所以他一定要加上  换行符
		bw.newLine();//newLine() 
		             //  写一行行分隔符。
		bw.flush();  //注意这个流不要关闭  因为这样就会把用户与服务器之间的链接给关掉;
		//这里我们处理字符串的方法有点复杂  我们可以直接使用处理数据加类型的DataOutputStream()
		//这里我们在改变流的时候要对应改变
        */
		
		DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
		dos.writeUTF(msg);
		dos.flush();
	}
}
package TCP;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
* 
* 1、创建客户端   必须指定服务器+端口    此时就在连接
* Socket(String host, int port) 
* 2、接收数据 +发送数据	
* @author Wang
*
*/
public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//1、创建客户端   必须指定服务器+端口    此时就在连接
		Socket client = new Socket("localhost",8888);
		//2、接收数据 +发送数据	
		/*BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
		String accept = br.readLine();
		System.out.println(accept);*/
		DataInputStream dis = new DataInputStream(client.getInputStream());
		String accept = dis.readUTF();
		System.out.println(accept);
		
	}
}

注意: 我们来说一下我们服务器与客户端交互的过程:他们是先建立一个链接,然后客户端发给服务器的发是客户端用Output发给内存,然后服务端从内存中读取;

package TCPchat;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 
 * 我们先来写一个简单的聊天室的交互
 * @author Wang
 *
 */

public class Server {
	public static void main(String[] args) throws IOException {
		/*//1.先创建一个服务器  并指定一个端口号
		ServerSocket server = new ServerSocket(6666);
		//2.与客户端建立连接
		Socket socket = server.accept();
		//3.接收客户端发回来的信息
		DataInputStream dis = new DataInputStream(socket.getInputStream());
		String accept = dis.readUTF();
		System.out.println(accept);
		//4.我们吧接收到的数据再返还给客户端
		DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
		dos.writeUTF("我是服务器收到后然后返还给你的: "+accept);
		dos.flush();*/
		
		
		ServerSocket server = new ServerSocket(6666);
		Socket socket = server.accept();
		DataInputStream dis = new DataInputStream(socket.getInputStream());
		DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
		//2.与客户端建立连接
		while(true) {
		//3.接收客户端发回来的信息
		String accept = dis.readUTF();
		//System.out.println(accept);
		//4.我们吧接收到的数据再返还给客户端
		dos.writeUTF("我是服务器收到后然后返还给你的: "+accept);
		dos.flush();
		}
	}
}
package TCPchat;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		/*//1.创建一个客户端   并于服务器建立连接
		Socket client = new Socket("localhost",6666);
		//2.准备一个数据 
		String sent = "Cilent send to Server";
		//3.使用流将这个数据发送给服务器;
		DataOutputStream dos = new DataOutputStream(client.getOutputStream());
		dos.writeUTF(sent);
		dos.flush();
		//4.我们接收服务器发送的数据
		DataInputStream dis = new DataInputStream(client.getInputStream());
		String accept = dis.readUTF();
		System.out.println(accept);*/
		
		
		
		/*//1.创建一个客户端   并于服务器建立连接
		Socket client = new Socket("localhost",6666);
		//2.准备一个数据 
		//我们来发送从控制台输入的数据
		BufferedReader console = new BufferedReader(new InputStreamReader(System.in));  //我们从这里可以看出System.in 属于字节的输入流
		//因为我们在这里用的转换流
		String input = console.readLine();//这里肯定是一个阻塞式的方法
		//3.使用流将这个数据发送给服务器;
		DataOutputStream dos = new DataOutputStream(client.getOutputStream());
		dos.writeUTF(input);
		dos.flush();
		//4.我们接收服务器发送的数据
		DataInputStream dis = new DataInputStream(client.getInputStream());
		String accept = dis.readUTF();
		System.out.println(accept);
		//我们在这里会发现  我们的程序只能运行一次    只是接收到客户发的一次信息就结束了
		 我们来改进一下
		 就是加一个while的循环呗*/	
		
		
		//1.创建一个客户端   并于服务器建立连接
		Socket client = new Socket("localhost",6666);
		//2.准备一个数据 
		//我们来发送从控制台输入的数据
		BufferedReader console = new BufferedReader(new InputStreamReader(System.in));  //我们从这里可以看出System.in 属于字节的输入流
		//因为我们在这里用的转换流
		//3.使用流将这个数据发送给服务器;
		DataOutputStream dos = new DataOutputStream(client.getOutputStream());
		DataInputStream dis = new DataInputStream(client.getInputStream());
		while(true) {
		String input = console.readLine();//这里肯定是一个阻塞式的方法
		dos.writeUTF(input);
		dos.flush();
		//4.我们接收服务器发送的数据
		String accept = dis.readUTF();
		System.out.println(accept);
		//注意我们的循环发送   服务器那边也要对应起来
		}
		
		
	
	}

}

注意:我们上面的代码只能实现客户端的先发送在接收,而服务器反之(这里是因为代码是按照顺序从上往下执行的);

这与我们实际的聊天情况不太一样   我们实际聊天是  你可以随意放松  根本不用接收完才能发送,那么这里就会让我们联想到多线程的问题,那么下面我们用多线程简单的模拟一下;

server还是上面的那个server

package TCPchatDemo02;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
	public static void main(String[] args) throws IOException {
		/*//1.先创建一个服务器  并指定一个端口号
		ServerSocket server = new ServerSocket(6666);
		//2.与客户端建立连接
		Socket socket = server.accept();
		//3.接收客户端发回来的信息
		DataInputStream dis = new DataInputStream(socket.getInputStream());
		String accept = dis.readUTF();
		System.out.println(accept);
		//4.我们吧接收到的数据再返还给客户端
		DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
		dos.writeUTF("我是服务器收到后然后返还给你的: "+accept);
		dos.flush();*/
		
		
		ServerSocket server = new ServerSocket(9999);
		Socket socket = server.accept();
		DataInputStream dis = new DataInputStream(socket.getInputStream());
		DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
		//2.与客户端建立连接
		while(true) {
		//3.接收客户端发回来的信息
		String accept = dis.readUTF();
		//System.out.println(accept);
		//4.我们吧接收到的数据再返还给客户端
		dos.writeUTF("我是服务器收到后然后返还给你的: "+accept);
		dos.flush();
		}
	}
}
package TCPchatDemo02;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 
 * 消息的发送
 * 
 * @author Wang
 *  
 */

public class Send implements Runnable{
	//控制台输入流
	private BufferedReader consoleInput; 
	//消息的发送
	private DataOutputStream dos;
	//设置一个消息发送的标志位
	boolean flag = true;
	
	public Send() {//无参构造先把控制台的输入传进来
		consoleInput = new BufferedReader(new InputStreamReader(System.in));
	}
	
	public Send(Socket socket) {
		this();
		try {
			dos = new DataOutputStream(socket.getOutputStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			//如果这里建立不了通道   我们关闭流和停止线程的运行
			flag =false;
			CloseUtil.closeAll(dos,consoleInput);
		}
	}
	
	/**
	 * 1.我们从控制台读取数据   如果读取不到就会返回一个"";(空串)
	 */
	public String getConsoleInput() {
		try {
			return consoleInput.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
	
	/**
	 * 2.我们把从控制台读取的数据发送给服务器
	 */
	public void send() {
		String message = getConsoleInput();
		if((message != null) && (message != "") ) {
			try {
				dos.writeUTF(message);
				dos.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				//e.printStackTrace();
				flag =false;
				CloseUtil.closeAll(dos,consoleInput);
			}
		}
	}
	
	
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(flag) {
			send();  //线程体里面的内容就是一直发送 
		}
	}
	
}
package TCPchatDemo02;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * 
 * 接收数据 与发送数据的思路相同
 * 
 * @author Wang
 *
 */

public class Receive implements Runnable{
	private DataInputStream dis;
	private boolean flag = true;

	Receive() {

	}

	Receive(Socket socket) {
		try {
			dis = new DataInputStream(socket.getInputStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			flag =false;
			CloseUtil.closeAll(dis);
		}
	}
	
	/**
	 * 我们来获取读取的信息
	 */
	
	public String getReaciveData() {
		try {
			return dis.readUTF();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			flag =false;
			CloseUtil.closeAll(dis);
		}
		return null;
	}
	
	/**
	 * 直接用线程体   输出读取的数据
	 */
	@Override
	public void run() {
		//线程体
		while(flag){
			System.out.println(getReaciveData());
		}
	}
	
	
	
	
	
	
}

package TCPchatDemo02;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 
 * 我们来用多线程来实现  聊天的发送消息和接收消息
 * 我们把他们包装成send  和  receive
 * 
 * @author Wang
 *
 */

public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		Socket client = new Socket("localhost",9999);//创建一个客户端并于服务器建立连接
		new Thread(new Send(client)).start();    //建立一个发送消息的线程
		new Thread(new Receive(client)).start(); //建立一个接受消息的线程
	}

	
}



猜你喜欢

转载自blog.csdn.net/qq_38053395/article/details/80707033