网络编程1-TCP编程(socket)
1 如何实现网络中的主机相互通信
一定的规则,有两套参考模型
(1)osi参考模型,过于理想化,未能在互联网上推行
osi有七层
(2)tcp/ip参考模型,有四层,各层之间通过不同的网络协议传输数据。
应用层: http协议
传输层:TCP UDP协议
网络层:IP协议
物理+数据链路层:Link协议
(3)截图?
2 通信要素一 定位计算机
(1)IP定位互联网上唯一计算机
(2)端口号定位计算机上的应用
(3)定位IP地址和端口号,java类InetAddress可以创建IP类
要素二 在按照协议传输
传输层有两个重要的协议,TCP/IP协议 UDP协议
TCP协议:三次握手,客户端服务端,可靠,大数据,效率低
(1)使用以前,先建立TCP链接,形成通道
(2)传输前采取三次握手,是可靠的,A给B传输,A先给B发信息,B回复A,A开始传输。
(3)TCP协议通信有两个进程,客户端,服务端
(4)可进行大数据传输
(5)传输完毕,需要释放已将建立的链接,效率低。
UDP协议:
(1)将数据、源地址、目的地址 封装成数据包,不需要建立链接
(2)每个数据包限制在64K以内
(3)不可靠
(4)无需释放资源,速度快
3、SOCKET套接字
(1)socket是两台机器间通信的端点
(2)socket允许把网络链接当成一个流,数据在两个socket之间通过IO传输
(3)主动发起请求的是客户端,等待请求的是服务端
4、(1)例1,客户端发送请求,服务端接收打印到控制台
package com.socket.test; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import org.junit.Test; //客户端给服务端发送信息,服务端输出信息到控制台上 public class TCPTest { //客户端 @Test public void client(){ //1、创建Socket套接字 Socket socket = null; //2、获取输出流 OutputStream os = null; try { socket = new Socket(InetAddress.getByName("127.0.0.1"),8090); os = socket.getOutputStream(); //3、向服务端写消息 os.write("I am Client".getBytes()); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ //4、关闭流,套接字 if(socket != null){ try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(os != null){ try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } @Test public void server(){ Socket socket = null; InputStream is = null; ServerSocket ss = null; try { //1、创建ServerSocket对象,指定端口号 ss = new ServerSocket(8090); //2、接收socket socket = ss.accept(); //3、获取输入流 is = socket.getInputStream(); //4、读取 byte[] b = new byte[20]; int len; while((len = is.read(b)) != -1){ String str = new String(b,0,len); System.out.print(socket.getInetAddress().getHostAddress()+"says"+str); } System.out.println("123"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ if(socket != null ){ try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(ss != null){ try { ss.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(is != null){ try { is.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
(2)例2,客户端向务务端发送请求,服务端应答
package com.socket.test; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import org.junit.Test; /* * 向服务端写消息,服务端收到消息后返回消息 */ public class TCPTest1 { @Test public void client(){ Socket socket = null; OutputStream os = null; InputStream is = null; try { InetAddress inetObj = InetAddress.getByName("127.0.0.1"); socket = new Socket(inetObj,8090); os = socket.getOutputStream(); os.write(("Hello I am " + inetObj.getHostName()).getBytes()); os.flush(); //注意,这里的os不关闭,服务端对应的is.read()方法就会一直处于阻塞状态,无法往下执行, //所以我们要告诉服务端的inputstream,我这里不会再写了,服务端的is就不会再等待, //执行此方法,显示告诉服务器,客户端发送完毕 socket.shutdownOutput(); is = socket.getInputStream(); byte[] b = new byte[20]; int len; while((len = is.read(b)) != -1){ System.out.println("收到服务状的消息"+new String(b,0,len)); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if( os!= null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if( socket!= null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void server() { ServerSocket serverSocket = null; Socket socket = null; try { serverSocket = new ServerSocket(8090); while(true){ socket = serverSocket.accept(); handleSocket(socket); } } catch (IOException e) { e.printStackTrace(); }finally{ if(serverSocket != null){ try { serverSocket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public void handleSocket(Socket socket){ InputStream is = null; OutputStream os = null; try { is = socket.getInputStream(); byte[] b = new byte[20]; int len; StringBuffer sbr = new StringBuffer(); while((len = is.read(b)) != -1){ sbr.append(new String(b,0,len)); } System.out.println("客户端说:"+sbr.toString()); os = socket.getOutputStream(); os.write(sbr.toString().toUpperCase().getBytes()); } catch (IOException e) { e.printStackTrace(); }finally{ if(os != null){ try { os.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(is != null){ try { is.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
(3)客户端发送文件到服务端,服务端接收完毕后,告诉客户端 ,接收完毕
package com.socket.test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import org.junit.Test; //客户端发送文件给服务端 public class TcpTest2 { @Test public void fileClient() { // 1、创建socket对象 Socket socket = null; // 2、获取输出流 OutputStream os = null; // 3、先读,把本地文件读入输入流 FileInputStream fis = null; InputStream is = null; try { socket = new Socket(InetAddress.getByName("127.0.0.1"), 8090); os = socket.getOutputStream(); fis = new FileInputStream(new File("d://env.conf")); byte[] b = new byte[64]; int len; while ((len = fis.read(b)) != -1) { // 4、往外写 os.write(b, 0, len); } // 5、告诉服务器写完了 socket.shutdownOutput(); // 6、读取从服务器返回的消息 is = socket.getInputStream(); byte[] b1 = new byte[20]; int len1; while ((len1 = is.read(b1)) != -1) { System.out.print(new String(b1, 0, len1)); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (os != null) { try { os.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (fis != null) { try { fis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (is != null) { try { is.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } @Test public void fileServer() { ServerSocket ss = null; try { ss = new ServerSocket(8090); while(true){ Socket socket = ss.accept(); handleClient(socket); } } catch (IOException e) { e.printStackTrace(); }finally{ try { if(ss != null){ ss.close(); } } catch (IOException e) { e.printStackTrace(); } } } public void handleClient(Socket socket){ //1、获取输入流 InputStream is = null; //2、创建fileoutputStream对象 FileOutputStream fos = null; OutputStream os = null; try { is = socket.getInputStream(); fos = new FileOutputStream(new File("d://env1.conf")); //3、读取服务器发来的字节 int len; byte b [] = new byte[64]; while((len = is.read(b)) != -1){ fos.write(b, 0, len); } fos.flush(); //4、写文件完毕,获取socket的输出流给客户端返回消息 os = socket.getOutputStream(); os.write("Accepted Over !".getBytes()); os.flush(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ if (socket != null) { try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (os != null) { try { os.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (is != null) { try { is.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
5、socket通信原理
客户端,创建出一个socket对象时(创建InetAddress对象时,传入的ip是服务器的ip),服务端也接受到一个socket对象
通过socket可以获取输入流和输出流
,客户端通过socket对象的输出流往服务器端写数据,
服务器端通过socket对象的输入流读数据
服务器服务端的通信,实际是sockey里面IO流的通信。
要注意的是,Inputsream的read方法,和ServetSocket的accept方法是阻塞方法。
6、聊天室的做法
1)客户端发送自己的名字a,消息,送达人的名字b,拼接成一个字符串。
2)服务端,接到a的请求,截取字符串,把a和a对应的socket对象放到map里面,然后启动一个线程,传入b .socket,
3)线程里面,通过b从map里面获取b的socket,获取b的输出流,把a和a说的话写出去
4)客户b的输入流接受到输出流发来的消息,结束。
7、需要注意的事项都写在注释里面,下面放一张时序图,画的不太标准