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(); //建立一个接受消息的线程 } }