一 网络通讯要素
1 、找到对方发ip地址
2、数据发送到对方指定的应用程序上,为了标识这些应用程序,叫做端口,也叫逻辑端口。
3、定义通信规则,这个通讯规则称为协议。国际组织自定义了通用的协议TCP/IP.还有UDP协议。
二、网络模型
OSI参考模型
应用层 表示层 会话层 传输层(TCP UDP) 网络层(IP) 数据链路层 物理层
TCP/IP模型
应用层 传输层 网际层 网络接口层
三 InetAddress
此类表示互联网协议(IP)地址。IP地址是IP使用的32位或128位无符号数字,它是一种低级协议,UDP和TCP协议都是它的基础上构建的。因为InetAddress没有构造函数,所以其方法中有静态方法,并且返回的是InetAddress对象。
.1 InetAddress的方法
static InetAddress getAllByName(String host)在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。
static InetAddress getByName(String name)在给定主机名的情况下确定主机的 IP 地址。
String getHostAddress();返回 IP 地址字符串(以文本表现形式)。
String getHostName()返回本地主机。
static InetAddress getLocalHost()返回本地主机
String toString()将此 IP 地址转换为 String。
四 UDP(面向无连接)
1 UDP的特点
将数据源和目的封装成数据包中,不需要建立连接。
每个数据报的大小限制在64k。
是不可靠协议
不需要建立连接,速度快。
UDP用的到类有DatagramSocket和DatagramPacket(数据包类)
2 DatagramSocket
此类表示用来发送和接受数据报包的套接字。
构造函数:
DatagramSocket()构造数据报套接字并将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port)创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port,InetAddress laddr)创建数据报套接字,将其绑定到指定的本地地址。
常用方法
void close()关闭此数据报套接字。
InetAddress getInetAddress()返回此套接字连接的地址。
InetAddress getLocalAddress()获取套接字绑定的本地地址。
int getLocalPort()返回此套接字绑定的本地主机上的端口号
int getPort()返回此套接字的端口.
void send(DatagramPacket p)从此套接字发送数据报包
void receive(DatagramPacket p)从此套接字接收数据报包
3 DatagramPacket
此类表示数据报包.
构造函数
DatagramPacket(byte[] buf, int length)构造 DatagramPacket,用来接收长度为length 的数据包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int length, SocketAddress address)构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
常用方法
InetAddress getAddress()返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
int getLength()返回将要发送或接收到的数据的长度。
int getPort() 返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
void setAddress(InetAddress iaddr)设置要将此数据报发往的那台机器的 IP 地址
void setData(byte[] buf)为此包设置数据缓冲区。
void setData(byte[] buf,int offset,int length)为此包设置数据缓冲区。
void setLength(int length)为此包设置长度。
void setPort(int iport)设置要将此数据报发往的远程主机上的端口号。
4 Socket
Socket就是为网络服务提供的一种机制。
通信的两端都有Socket。
网络通信其实就是socket间的通信。
数据在两个socket间通过IO传输。
5 UDP发送
思路:1建立UDP socket服务
2提供数据,并将数据封装到数据包中
3通过socket服务的分发送功能,将数据包发出去。
4关闭资源。
例子:
public void sendSocket()throws Exception
{
DatagramSocket ds=new DatagramSocket();
byte[ ] buf="我来了".getBytes();
DatagramPacket dp=newDatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.110"),1000);
ds.send(dp);
ds.close();
}
UDP 接收
思路:1定义udp socket服务,通常会监听一个端口,其实就是给这个接受网络应用程序定义数字标识。
2定义一个数据包,因为要存储接收到的字节数据。
3通过socket服务的receive方法将收到的数据存入已定义好的数据包中。
4通过数据包对象的功能,将数据取出
5关闭资源。
例子:
public void recSocket()throws Exception
{
while(true)
{
DatagramSocket ds=new DatagramSocket(1000);
byte[ ] buf=new byte[1024];
DatagramPacket dp=new DatagramPacket(buf,buf.length);
ds.receive(dp);
//获取信息
String ip=dp.getAddress().getHostAddress();
String data=new String(dp.getData,0,dp.getLength());
int port=dp.getPort();
}
ds.close();
}
6 UDP联系(键盘录入)
发送端:
public void sendSocket()throws Exception
{
DatagramSocket ds=newDatagramSocket();
BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));
String str=null;
while((str=bfr.readLine())!=null)
{
if("over".equals(line))
break;
byte[] buf=line.getBytes();
DatagramPacket dp=newDatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.110"),1000);
ds.send(dp);
}
ds.close();
}
接收端:
public void recSocket()throws Exception
{
while(true)
{
DatagramSocket ds=new DatagramSocket(1000);
byte[ ] buf=new byte[1024*64];
DatagramPacket dp=new DatagramPacket(buf,buf.length);
ds.receive(dp);
String ip=dp.getAddress().getHostAddress();
String data=new String(dp.getData,0,dp.getLength());
int port= dp.getPort();
}
ds.close();
}
五 TCP
.1 TCP的特点:
建立连接,形成传输数据的通道。
在连接中进行大量数据传输
通过三次握手来完成连接,是可靠协议。
必须建立连接,效率低
.2 客户端(Socket)和服务器端(ServerSocket),建立连接后,通过Socket中的IO流进行数据传输。同样客户端与服务端是两个独立的应用程序。通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机,因为tcp是面向连接的,所以在建立socket服务时,就要有服务端存在,并连接成功后,形成通道,在该通道进行数据的传输。
.3 Socket
此类实现客户端套接字,套接字是两台机器间通信的端点。
构造函数
Socket(String host,int port)创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket(Inet address host,int port); 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
Socket(String host,int port,InetAddress localAddr,int localPort)创建一个套接字并将其连接到指定远程主机上的指定远程端口。
Socket(InetAddress address,int port,InetAddress localAddr,int localPort)创建一个套接字并将其连接到指定远程地址上的指定远程端口。
常用方法
void close()关闭此套接字
InetAddress getInetAddress()返回套接字连接的地址
InputStream getInputStream()返回此套接字的输入流。
InetAddress getLocalAddress()获取套接字绑定的本地地址
int getLocalPort()返回此套接字绑定到的本地端口。
OutputStream getOutputStream()返回此套接字的输出流
int getPort()返回此套接字连接到的远程端口
void shutdownInput()此套接字的输入流置于“流的末尾”。
4 ServerSocket
此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。
构造函数
ServerSocket(int port)创建绑定到特定端口的服务器套接字.
ServerSocket(int port,int backlog) 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号
常用方法
Socket accept() 侦听并接受到此套接字的连接。
void close()关闭此套接字。
InetAddress getInetAddress()返回此服务器套接字的本地地址。
.5 TCP 发送数据
步骤:1建立一个Socket端口,并指定要连接的主句和端口。
2为了发送数据,用该获取socket中的输出流,写入数据。
3关闭资源
例:
public void tcpClient() throws Exception
{
Socket s=new Socket("192.168.1.110",10000);
OutputStream out=s.getOutputStream();
out.write("我来了".getBytes);
s.close();
}
接收数据
步骤:1建立服务端的Socket服务,通过ServerSocket类,并监听一个端口。
2通过ServerSocket类的accept方法,获取客户端对象,这个方法时阻塞式的,没有连接就会等。
3通过客户端对象获取数据,也可以发送数据
4关闭资源(可选操作)
例
public void tcpServer() throws Exception
{
ServerSocket ss=new ServerSocket(10000);
Socket s=ss.accept();
String ip=s.getInetAddress.getHostAddress();
InputStream in=s.getInputStream();
byte[] buf=new byte[1024];
int len=in.read(buf);
String s=new String(buf.0.len);
s.close();
}
六 练习
.1 客户端与服务端的互访。
客户端代码:
package IoDemo; import java.io.*; import java.net.*; public class TCPClient { public static void main(String[] args)throws Exception { Socket s=new Socket("192.168.1.110",10000); OutputStream out=s.getOutputStream(); out.write("hello".getBytes()); s.shutdownOutput(); //发送结束标记
InputStream in=s.getInputStream(); byte[] buf=new byte[1024]; int num=0; while((num=in.read(buf))!=-1) { System.out.println(new String(buf,0,num)); } s.close(); } }
服务端代码:
package IoDemo; import java.io.*; import java.net.*; public class TCPServer { public static void main(String[] args)throws Exception { ServerSocket ss=new ServerSocket(10000); Socket s=ss.accept(); String str=s.getInetAddress().getHostAddress(); //获取传入的数据 InputStream in=s.getInputStream(); byte[] buf=new byte[1024]; int len=0; while((len=in.read(buf))!=-1) { System.out.println(new String(buf,0,len)); } //写入数据传出去 OutputStream out=s.getOutputStream(); out.write("知道了".getBytes()); s.close(); } }
2 客户端录入文字,发送到服务端,服务端转成大写后发回来。
客户端代码:
package IoDemo; import java.io.*; import java.net.*; public class TextClient { public static void main(String[] args)throws Exception { //创建键盘录入 BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in)); Socket s=new Socket("192.168.1.110",10000); //传入数据流 BufferedReader inbfr=new BufferedReader(new InputStreamReader(s.getInputStream())); //往服务器写出流 BufferedWriter bwf=new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); //PrintWriter pw=new PrintWriter(s.getOutputStream()); String str=null; while((str=bfr.readLine())!=null) { if("over".equals(str)) break; bwf.write(str); bwf.newLine();
bwf.flush();
//pw.println(str);
//pw.flush(); //读取服务器返回的数据 String outStr=inbfr.readLine(); System.out.println(outStr); } bfr.close(); s.close(); } }
服务端代码:
package IoDemo; import java.io.*; import java.net.*; public class TextServer { public static void main(String[] args)throws Exception { ServerSocket ss=new ServerSocket(10000); Socket s=ss.accept();
//客户端发送过来的数据读取流 BufferedReader bfr=new BufferedReader(new InputStreamReader(s.getInputStream()));
//发往客户端的写入流 BufferedWriter bfw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String str=null; while((str=bfr.readLine())!=null) { bfw.write(str.toUpperCase()); bfw.newLine();//添加换行符 bfw.flush(); } s.close(); } }
小结:BufferedWriter的write方法写入的是换行符之前的有效数据,不包括换行符,对方读取没读到换行符,就一直在等待,最后双方都在等待,所以在发送每行之后,在发送换行标志(newLine()方法),而且要记得刷新。
3 往服务器上传图片。
客户端代码:
public class ImgClient { public static void main(String[] args)throws Exception { Socket s=new Socket("192.168.1.110",10000); FileInputStream fis=new FileInputStream(1.bmp); OutputStream out=s.getOutputStream(); byte[] buf=new byte[1024]; int len=0; while((len=fis.read(buf))!=-1) { out.write(buf,0,len); } s.shutdownOutput();//关闭客户端的输出流,相当于给流中加入一个结束标记-1. BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream())); String str=bufIn.readLine(); buf.close(); s.close(); } }
服务端代码:
public class ImgServer { public static void main(String[] args)throws Exception { ServerSocket ss=new ServerSocket(10006); Socket s=ss.accept(); BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream())); InputStream in=s.getInputStream(); FileOutputStream fos=new FileOutputStream("server.bmp"); byte[] buf=new byte[1024]; int len=0; while((len=in.read(buf))!=-1) { fos.write(buf,0,len); } PrintWriter pw=new PrintWriter(s.getOutputStream()); pw.println("上传成功!"); fos.close(); s.close(); ss.close(); } }
小结:当客户端上传图片时,没有发送结束标记,服务端会一直在接收,所以不会给客户端返回信息,客户端也在等待,所以客户端向服务端发送数据时,可以定义一个结束标记,如“over”服务端一看到这个结束标记,就知道客户端发送完了,服务端停止接收信息,并返回信心。这个有一定的风险,英文不知发送的数据中是否有“over”,不安全,所以使用shutdownOutput()方法,关闭客户端的输出流,相当于给流中加入一个结束标记-1.
4 并发上传图片
应为要并发上传图片,要使用多线程。
服务器端代码
package IoDemo; import java.io.*; import java.net.*; public class PicThread implements Runnable {
private Socket s;
PicThread(Socket s)
{
this.s=s;
}
public void run()
{
int count=1;
String ip=s.getInetAddress.getHostAddress();
try
{
InputStream in=s.getInoutStream();
File file=new File(ip+"("+(count++)+")"+".jpg");
while(file.exisets())
file=new File(ip+"("+(count++)+")"+".jpg"); FileOutputStream fos=new FileOutputStream(file); byte[] buf=new byte[1024]; int len=0; while((len=in.read(buf))!=-1) { fos.write(buf,0,len); } PrintWriter pw=new PrintWriter(s.getOutputStream()); pw.println("上传成功!"); fos.close(); s.close();
}
catch(Exception e)
{
throw new RuntimeException(ip+"上传失败");
}
}
}
class PicServer { public static void main(String[] args)throws Exception { ServerSocket ss=new ServerSocket(10006); while(true) { Socket s=ss.accept(); new Thread(new PicThread(s)).atart(); } } }
本来只有一个主线程,ss.accept()是阻塞式,会等待,当进来上传者,会创建一个线程,现在就有两个线程,上传线程继续运行,主线程运行知道等待第二个上传,所以来一个,创建一个线程。
2460

被折叠的 条评论
为什么被折叠?



