网络编程之TCP通信
1、TCP协议是面向连接的、可靠的、有序的、以字节流的方式发送数据,通过三次握手方式建立连接,形成传输数据的通道,在连接中进行大量数据的传输,效率会稍低
2、Java中基于TCP协议实现网络通信的类
客户端的Socket类
服务器端的ServerSocket类
![](https://img2018.cnblogs.com/blog/1468796/201902/1468796-20190218005815550-1749766722.png)
3、Socket通信的步骤
① 创建ServerSocket和Socket(服务端和客户端)
② 打开连接到Socket的输入/输出流
③ 按照协议对Socket进行读/写操作
④ 关闭输入输出流、关闭Socket
4、服务器端:
View Code
View Code
View Code
View Code
View Code
View Code
① 创建ServerSocket对象,绑定监听端口
② 通过accept()方法监听客户端请求
③ 连接建立后,通过输入流读取客户端发送的请求信息
④ 通过输出流向客户端发送回音信息
⑤ 关闭相关资源(大型服务器一般不关闭)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package tcp通信; 2 3 import java.io.DataInputStream; 4 import java.io.DataOutputStream; 5 import java.io.IOException; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 9 /** 10 * 熟悉流程: 11 * 创建服务器 12 * 模拟登录界面,单向访问 13 * 1、指定端口,使用ServerSocket 创建服务器 14 * 2、阻塞式等待连接accept 15 * 3、输入输出流操作 16 * 4、释放资源 17 * @author liuzeyu12a 18 * 19 */ 20 public class LoginServer2 { 21 22 public static void main(String[] args) throws IOException { 23 System.out.println("---------服务端-----------"); 24 String uname = ""; 25 String upwd = ""; 26 // 1、指定端口 使用ServerSocket创建服务器 27 ServerSocket server = new ServerSocket(8888); 28 29 //2、阻塞时等待连接,返回一个连接的套接字 30 Socket client = server.accept(); 31 System.out.println("一个客户端建立了连接..."); 32 33 //3、输入输出操作,使用操作方便的数据流 34 DataInputStream dis = new DataInputStream(client.getInputStream()); 35 String utf = dis.readUTF(); 36 String str[] = utf.split("&"); 37 for(String info:str) { 38 String[] usrInfo = info.split("="); 39 if(usrInfo[0].equals("uname")) { 40 uname = usrInfo[1]; 41 System.out.println(usrInfo[0]+":"+usrInfo[1]); 42 }else{ 43 upwd = usrInfo[1]; 44 System.out.println(usrInfo[0]+":"+usrInfo[1]); 45 } 46 } 47 //向客户端返回结果,是否登录成功 48 DataOutputStream dos = new DataOutputStream(client.getOutputStream()); 49 if(uname.equals("liuzeyu12a")&&upwd.equals("10086")) { 50 dos.writeUTF("登录成功!!"); 51 }else { 52 dos.writeUTF("登录失败!!"); 53 } 54 //释放资源 55 server.close(); //大型服务器一般不关闭 56 57 } 58 }
5、客户端:
① 创建Socket对象,指明需要连接的服务器的地址和端口号
② 连接建立后,通过输出流想服务器端发送请求信息
③ 通过输入流获取服务器响应的信息
④ 关闭响应资源
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package tcp通信; 2 3 import java.io.BufferedReader; 4 import java.io.DataInputStream; 5 import java.io.DataOutputStream; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.net.Socket; 9 import java.net.UnknownHostException; 10 11 /** 12 * 熟悉流程: 13 * 模拟登录界面,单向访问 14 * 1、建立连接,使用Socket创建客户端 + 服务器地址和端口 15 * 2、输入输出操作 16 * 3、释放资源 17 * @author liuzeyu12a 18 * 19 */ 20 public class LoginClient2 { 21 22 public static void main(String[] args) throws UnknownHostException, IOException { 23 System.out.println("---------客户端登录界面-----------"); 24 BufferedReader reader = new BufferedReader( 25 new InputStreamReader(System.in)); 26 System.out.print("请输入用户名:"); 27 String uname = reader.readLine(); 28 System.out.print("请输入用户名:"); 29 String upwd = reader.readLine(); 30 31 32 //1、建立连接,使用Socket创建客户端 + 服务器地址和端口 33 Socket client = new Socket("localhost",8888); 34 35 //2、输入输出操作 36 DataOutputStream dos = new DataOutputStream(client.getOutputStream()); 37 dos.writeUTF("uname="+uname+"&"+"upwd="+upwd); 38 39 40 //接收服务器返回的结果 41 DataInputStream dis = new DataInputStream(client.getInputStream()); 42 String rst = dis.readUTF(); 43 System.out.println(rst); 44 //3、释放资源 45 dos.close(); 46 client.close(); 47 } 48 }
6、当然,这种客户端请求服务器,然后服务器回应客户端的请求只能实现一对一的服务,在现在的信息化时代,这种通信已经不能满足需求了。
一个服务器应该被多个客户端请求,然后对其一一回应,这才是我们想要的。
这就引出了多线程,在服务器上开启多线程,即一个Socket 多个客户端可以使用。
应用多线程实现服务器与多客户端之间的通信
① 服务器端创建ServerSocket,循环调用accept()等待客户端连接
② 客户端创建一个socket并请求和服务器端连接
③ 服务器端接受客户端请求,创建socket与该客户建立专线连接
④ 建立连接的两个socket在一个单独的线程上对话
⑤ 服务器端继续等待新的连接
实现代码:
6.1服务端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package tcp通信; 2 3 import java.io.DataInputStream; 4 import java.io.DataOutputStream; 5 import java.io.IOException; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 9 /** 10 * 服务端接收多个客户端的请求: 11 * 在没有使用多线程的情况下要想多个客户机访问服务器,必须等待上一个服务结束 12 * 后才能进行下一个客户机的连接操作 13 * 1、使用ServerSocket 建立服务端的套接字绑定本地端口 14 * 2、阻塞式等待连接Socket accept() 15 * 3、输入输出流操作 16 * 4、释放资源 17 * @author liuzeyu12a 18 * 19 */ 20 public class MultiServer { 21 22 public static void main(String[] args) throws Exception { 23 System.out.println("-------服务器--------"); 24 25 boolean isRunning = true; 26 //1、使用ServerSocket 建立服务端的套接字绑定本地端口 27 ServerSocket server = new ServerSocket(9999); 28 29 while(isRunning) { 30 //2、阻塞式等待连接Socket accept() 31 Socket client = server.accept(); 32 System.out.println("一个客户端建立了连接"); 33 new Thread(new Channel(client)).start(); 34 } 35 //4、释放资源 36 server.close(); 37 } 38 39 40 //多线程类 41 //将类改为静态:否则创建对象:new MultiServer().new Channel(client) 42 //一个Channel代表了一个客户端 43 static class Channel implements Runnable{ 44 //客户端套接字 45 private Socket client; 46 //输入流 47 private DataInputStream dis; 48 //输出流 49 private DataOutputStream dos; 50 //构造器 51 public Channel(Socket client) { 52 this.client = client; 53 try { 54 dis = new DataInputStream(client.getInputStream()); 55 dos = new DataOutputStream(client.getOutputStream()); 56 } catch (IOException e) { 57 e.printStackTrace(); 58 //如果出异常了 59 try { 60 client.close(); 61 } catch (IOException e1) { 62 e1.printStackTrace(); 63 release(); 64 } 65 } 66 } 67 68 //接收客户端信息函数 69 public String recvive() { 70 String datas = ""; 71 try { 72 datas = dis.readUTF(); 73 } catch (IOException e) { 74 e.printStackTrace(); 75 } 76 return datas; 77 } 78 79 //发送客户端信息函数 80 public void send(String msg) { 81 try { 82 dos.writeUTF(msg); 83 dos.flush(); 84 } catch (IOException e) { 85 e.printStackTrace(); 86 } 87 } 88 89 public void release() { 90 try { 91 if(null!=client) 92 client.close(); 93 } catch (IOException e) { 94 e.printStackTrace(); 95 } 96 try { 97 if(null!=dis) 98 dis.close(); 99 } catch (IOException e) { 100 e.printStackTrace(); 101 } 102 try { 103 if(null!=dos) 104 dos.close(); 105 } catch (IOException e) { 106 e.printStackTrace(); 107 } 108 } 109 110 //线程运行函数 111 @Override 112 public void run() { 113 //处理客户端发送过来的用户信息 114 String uname = ""; 115 String upwd = ""; 116 String info[] = recvive().split("&"); 117 for(String str: info) { 118 String[] s = str.split("="); 119 if(s[0].equals("uname")) { 120 uname = s[1]; 121 }else if(s[0].equals("upwd")) { 122 upwd = s[1]; 123 } 124 } 125 System.out.println("user:"+uname+"\r\n"+"password:"+upwd); 126 127 //向客户端返回登录信息 128 if(uname.equals("liuzeyu") &&upwd.equals("10086")) { 129 send("登录成功!!"); 130 }else { 131 send("登录失败!!"); 132 } 133 release(); 134 } 135 } 136 137 }
6.2客户端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package tcp通信; 2 3 import java.io.BufferedReader; 4 import java.io.DataInputStream; 5 import java.io.DataOutputStream; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.net.Socket; 9 import java.net.UnknownHostException; 10 11 /** 12 * 熟悉流程: 13 * 模拟登录界面,单向访问 14 * 1、建立连接,使用Socket创建客户端 + 服务器地址和端口 15 * 2、输入输出操作 16 * 3、释放资源 17 * @author liuzeyu12a 18 * 19 */ 20 public class LoginClient2 { 21 22 public static void main(String[] args) throws UnknownHostException, IOException { 23 System.out.println("---------客户端登录界面-----------"); 24 BufferedReader reader = new BufferedReader( 25 new InputStreamReader(System.in)); 26 System.out.print("请输入用户名:"); 27 String uname = reader.readLine(); 28 System.out.print("请输入用户名:"); 29 String upwd = reader.readLine(); 30 31 32 //1、建立连接,使用Socket创建客户端 + 服务器地址和端口 33 Socket client = new Socket("localhost",8888); 34 35 //2、输入输出操作 36 DataOutputStream dos = new DataOutputStream(client.getOutputStream()); 37 dos.writeUTF("uname="+uname+"&"+"upwd="+upwd); 38 39 40 //接收服务器返回的结果 41 DataInputStream dis = new DataInputStream(client.getInputStream()); 42 String rst = dis.readUTF(); 43 System.out.println(rst); 44 //3、释放资源 45 dos.close(); 46 client.close(); 47 } 48 }
7、客户端利用面向对象思想进行封装后
7.1 服务端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package tcp通信; 2 3 import java.io.DataInputStream; 4 import java.io.DataOutputStream; 5 import java.io.IOException; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 9 /** 10 * 服务端接收多个客户端的请求: 11 * 在没有使用多线程的情况下要想多个客户机访问服务器,必须等待上一个服务结束 12 * 后才能进行下一个客户机的连接操作 13 * 1、使用ServerSocket 建立服务端的套接字绑定本地端口 14 * 2、阻塞式等待连接Socket accept() 15 * 3、输入输出流操作 16 * 4、释放资源 17 * @author liuzeyu12a 18 * 19 */ 20 public class MultiServer { 21 22 public static void main(String[] args) throws Exception { 23 System.out.println("-------服务器--------"); 24 25 boolean isRunning = true; 26 //1、使用ServerSocket 建立服务端的套接字绑定本地端口 27 ServerSocket server = new ServerSocket(9999); 28 29 while(isRunning) { 30 //2、阻塞式等待连接Socket accept() 31 Socket client = server.accept(); 32 System.out.println("一个客户端建立了连接"); 33 new Thread(new Channel(client)).start(); 34 } 35 //4、释放资源 36 server.close(); 37 } 38 39 40 //多线程类 41 //将类改为静态:否则创建对象:new MultiServer().new Channel(client) 42 //一个Channel代表了一个客户端 43 static class Channel implements Runnable{ 44 //客户端套接字 45 private Socket client; 46 //输入流 47 private DataInputStream dis; 48 //输出流 49 private DataOutputStream dos; 50 //构造器 51 public Channel(Socket client) { 52 this.client = client; 53 try { 54 dis = new DataInputStream(client.getInputStream()); 55 dos = new DataOutputStream(client.getOutputStream()); 56 } catch (IOException e) { 57 e.printStackTrace(); 58 //如果出异常了 59 try { 60 client.close(); 61 } catch (IOException e1) { 62 e1.printStackTrace(); 63 } 64 } 65 } 66 67 //接收客户端信息函数 68 public String recvive() { 69 String datas = ""; 70 try { 71 datas = dis.readUTF(); 72 } catch (IOException e) { 73 e.printStackTrace(); 74 } 75 return datas; 76 } 77 78 //发送客户端信息函数 79 public void send(String msg) { 80 try { 81 dos.writeUTF(msg); 82 dos.flush(); 83 } catch (IOException e) { 84 e.printStackTrace(); 85 } 86 } 87 88 public void release() { 89 try { 90 if(null!=client) 91 client.close(); 92 } catch (IOException e) { 93 e.printStackTrace(); 94 } 95 try { 96 if(null!=dis) 97 dis.close(); 98 } catch (IOException e) { 99 e.printStackTrace(); 100 } 101 try { 102 if(null!=dos) 103 dos.close(); 104 } catch (IOException e) { 105 e.printStackTrace(); 106 } 107 } 108 109 //线程运行函数 110 @Override 111 public void run() { 112 //处理客户端发送过来的用户信息 113 String uname = ""; 114 String upwd = ""; 115 String info[] = recvive().split("&"); 116 for(String str: info) { 117 String[] s = str.split("="); 118 if(s[0].equals("uname")) { 119 uname = s[1]; 120 }else if(s[0].equals("upwd")) { 121 upwd = s[1]; 122 } 123 } 124 System.out.println("user:"+uname+"\r\n"+"password:"+upwd); 125 126 //向客户端返回登录信息 127 if(uname.equals("liuzeyu") &&upwd.equals("10086")) { 128 send("登录成功!!"); 129 }else { 130 send("登录失败!!"); 131 } 132 release(); 133 } 134 } 135 136 }
7.2 客户端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package tcp通信; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedReader; 5 import java.io.DataInputStream; 6 import java.io.DataOutputStream; 7 import java.io.IOException; 8 import java.io.InputStreamReader; 9 import java.net.Socket; 10 import java.net.UnknownHostException; 11 12 /**模拟多个客户端的请求 13 * 在没有使用多线程的情况下要想多个客户机访问服务器,必须等待上一个服务结束 14 * 后才能进行下一个客户机的连接操作 15 * 客户端 16 * 1、建立连接,使用多Socket建立客户端+绑定服务器的端口和地址 17 * 2、操作,输入流和输出流的操作 18 * 3、释放资源 19 * @author liuzeyu12a 20 * 21 */ 22 public class MultiClient { 23 24 public static void main(String[] args) throws UnknownHostException, IOException { 25 System.out.println("--------客户端---------"); 26 27 //1、建立连接,使用多Socket建立客户端+绑定服务器的端口和地址 28 Socket client = new Socket("localhost",9999); 29 30 //2、操作,输入流和输出流的操作 31 new Send(client).send();; 32 33 //处理登录信息的反馈 34 new Receive(client).receive(); 35 36 //释放资源 37 new Send().release(); 38 new Receive().release(); 39 } 40 41 //静态内部类:用于发送 42 static class Send{ 43 private String msg = ""; 44 private BufferedReader reader; 45 46 //输出流 47 private DataOutputStream dos; 48 //客户端套接字 49 private Socket client; 50 //构造器 51 public Send(Socket client) { 52 this.client =client; 53 this.reader= new BufferedReader( 54 new InputStreamReader(System.in)); 55 this.msg = init(); 56 try { 57 dos = new DataOutputStream(client.getOutputStream()); 58 } catch (IOException e) { 59 e.printStackTrace(); 60 } 61 } 62 //无参构造器 63 public Send() { 64 } 65 66 //发送函数 67 public void send() { 68 try { 69 dos.writeUTF(msg); 70 } catch (IOException e) { 71 e.printStackTrace(); 72 } 73 try { 74 dos.flush(); 75 } catch (IOException e) { 76 e.printStackTrace(); 77 } 78 } 79 //释放资源函数 80 public void release() { 81 try { 82 if(null!=dos) 83 dos.close(); 84 } catch (IOException e1) { 85 e1.printStackTrace(); 86 } 87 try { 88 if(null!=client) 89 client.close(); 90 } catch (IOException e) { 91 e.printStackTrace(); 92 } 93 } 94 95 //初始化信息 96 public String init() { 97 try { 98 System.out.print("请输入用户名:"); 99 String uname = reader.readLine(); 100 System.out.print("请输入密码:"); 101 String upwd = reader.readLine(); 102 return "uname="+uname+"&"+"upwd="+upwd; 103 } catch (IOException e) { 104 e.printStackTrace(); 105 } 106 return ""; 107 } 108 109 } 110 111 112 //静态内部类:用于接收 113 static class Receive{ 114 //客户端套接字 115 private Socket client; 116 //输入流 117 private DataInputStream dis; 118 //构造器 119 public Receive(Socket client) { 120 this.client =client; 121 try { 122 dis = new DataInputStream( 123 new BufferedInputStream(client.getInputStream())); 124 } catch (IOException e) { 125 e.printStackTrace(); 126 } 127 } 128 129 //接收函数 130 public void receive() { 131 try { 132 String rst = dis.readUTF(); 133 System.out.println(rst); 134 } catch (IOException e) { 135 e.printStackTrace(); 136 } 137 } 138 139 public Receive() { 140 } 141 //释放函数 142 public void release() { 143 try { 144 if(null!=client) 145 client.close(); 146 } catch (IOException e) { 147 e.printStackTrace(); 148 } 149 try { 150 if(null!=dis) 151 dis.close(); 152 } catch (IOException e) { 153 e.printStackTrace(); 154 } 155 } 156 157 } 158 }
附上参考资料
https://www.cnblogs.com/rocomp/p/4790340.html