java学习笔记——socket编程

来源:互联网 发布:淘宝如何快速打造爆款 编辑:程序博客网 时间:2024/06/06 04:22

实现网络编程

要用java编写一段代码用来通信的话,需要用到些什么,如何实现?
这次学习了一个socket编程(也称网络编程,套接字编程),用来解决上述问题

网络编程三要素

我们与其他人交流对话,首先我们要确定要对话的对象,并且以恰当的途径传送给他,网络编程也是如此。在网络编程中,将其总结出几个要点,如果不能满足这几个要点,则不能正确的将想要表达的信息传送给目标计算机。
1. ip地址
ip地址代表的是计算机的地址,处以同一网络环境下,计算机可以通过ip地址来确定其他计算机的地址,在传送数据的时候,也是向相应的ip地址传输。
查询ip地址的时候可以使用命令指示符ipconfig来获得自己的ip地址
2. 端口
当数据到达计算机之后,得分发到需要这个数据的应用上,为了分辨应用是否需要这个数据,需要端口号来实现,应用开启了数据包所表示的端口,计算机才会把数据传送到该应用中。有效端口有:0~65535,其中0~1024系统使用或保留端口,我们一般使用1025~65535的端口,以防止和系统端口冲突
3. 协议
协议相当于是数据传输用的语言,如果数据包说的是一门“外语”,我们就不能很好的理解这段数据所表达的意思
我们这里只说最常用的udp和tcp协议

java中传输UDP协议的包

UDP协议的特性

  • 把数据打成一个数据包 , 不需要建立连接
  • 数据包的大小有限制不能超过64k
  • 因为无连接,所以属于不可靠协议(可能丢失数据)
  • 因为无连接 ,所以效率高

UDP协议最重要的特征就是不需要建立连接,发送端随时都可以发送数据给目标ip,可以节省创建连接的时间,但是目标ip也可能没有打算接受UDP协议的包,导致数据传送过去并没有被接受。

我们在发送端要做的事情就是创建发送数据包的socket对象,将数据打包,发送过去就行;在接受端也只用创建接收数据包的socket对象,获取数据即可。

需要用到的ip地址,java使用InetAddress类来封装IP地址对象
这个类私有化了构造方法,只能通过类方法来创建对象

static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。
–host内输入主机名或ip地址就可以构造出该计算机的Inetaddress对象

发送端

  1. 在发送端建立一个socket对象,这里使用DatagramSocket类
  2. 再建立一个数据包对象DatagramPacket(buf, length, address, port),buf是要传送的数据转化的byte[]对象,length是要传送的数据长度,address接目标Inetaddress对象,port设置为端口
  3. 调用DatagramSocket的send(DatagramPacket p) 方法来发送数据包p
  4. 释放资源
public class UdpClient {    public static void main(String[] args) throws Exception {        //1.创建udp协议发送端的socket对象        //public DatagramSocket() throws SocketException        DatagramSocket ds = new DatagramSocket();        byte[] buf = "hello".getBytes();        int length = buf.length;        InetAddress address = InetAddress.getByName("192.168.20.254");        int port = 8888;        //2.创建数据包        //public DatagramPacket(byte[] buf, int length,InetAddress address,int port)        DatagramPacket dp = new DatagramPacket(buf, length, address, port);        //发送数据        ds.send(dp);        //释放资源        ds.close();    }}

接收端

  1. 建立socket对象,这里也用DatagramSocket,但是要指定接受包所设定的端口,
  2. 创建新的包对象DatagramPacket(buf, length)来接受传送过来的数据
  3. 调用socket对象的receive(DatagramPacket p)
  4. 解析数据包——
    byte[] getData()
    返回数据缓冲区。
    int getLength()
    返回将要发送或接收到的数据的长度。
  5. 释放资源
public class UdpServer {    public static void main(String[] args) throws Exception {        //创建接收端的socket对象        DatagramSocket ds = new DatagramSocket(8888);        //创建一个数据包,用来接收来自发送端的数据,是一个空的数据包        byte[] buf = new byte[1024];        int length = buf.length;        DatagramPacket dp = new DatagramPacket(buf, length);        //接受来自发送端的数据        //public void receive(DatagramPacket p)throws IOException        //程序在这里接收到来自发送端的数据之前一直处于阻塞状态        ds.receive(dp);        //解析一下数据包中的数据        //public byte[] getData()返回数据缓冲区        byte[] data = dp.getData();        //public int getLength()返回将要发送或接收到的数据的长度        int len = dp.getLength();        System.out.println(new String(data,0,len));        //释放资源        ds.close();    }}

java中传输TCP协议的数据包

TCP的特性

  • 需要建立连接,形成连接通道
  • 数据可以使用连接通道直接进行传输,无大小限制
  • 因为有链接,所以属于可靠协议
  • 因为有链接,所以效率低

TCP是先建立连接,再进行通讯的,所以必须要接收端准备好了接收你的数据,发送端才能发送,接收端和发送端实现了连接之后,可以随意的发送任意大小的数据
这里的socket对象直接使用socket类
传输数据时,直接使用输入输出流来写入和读取数据

发送端

  1. 建立socket对象Socket(InetAddress address, int port)
  2. 使用getOutputStream()来获取此套接字的输出流,向这个输出流写入数据时,直接可以被读取出来
  3. 释放资源
public class TcpClient {    public static void main(String[] args) throws Exception {        //创建tcp协议发送端的socket对象        //public Socket(String host,int port)        Socket sk = new Socket("192.168.20.254", 10086);        //2.public OutputStream getOutputStream()throws IOException        //从通道中获取输出流对象        OutputStream os = sk.getOutputStream();        //3.给通道中写数据        os.write("hello".getBytes());        //4.释放资源        sk.close();     }}

接收端

1 . 先创建一个socket服务器对象ServerSocket并指定端口号
2. 调用ServerSocket的 accept() 方法,侦听并接受到此套接字的连接,并获取到发送端的socket对象
3. 调出socket对象的输入流,读取socket对象的数据
4. 释放资源

public class TcpServer {    public static void main(String[] args) throws Exception {        //创建服务器端的socket对象        ServerSocket ss = new ServerSocket(2000);        //监听来自于客户端的连接        Socket sk = ss.accept();        //创建BufferedReader一次读取一行数据        BufferedReader br = new BufferedReader(new InputStreamReader(sk.getInputStream()));        String line;        while ((line=br.readLine())!=null) {            System.out.println(line);        }        //释放资源        br.close();        sk.close();    }}

通过TCP协议来传输文本文件并用多线程实现接收多组数据

在实际情况中,会比上面演示的TCP传输复杂很多,针对不同的情况,要对上面的代码做出不同的修改,下面演示一个使用TCP协议来传输文本文件并用多线程实现接收多组数据的实例

  1. 编写一个命名类,使用UUID类来产生随机的字符串,以此来命名写出文件,保证了文件名不会重复
  2. 编写一个实现runnable接口的接收端功能类,这样接收端只要开启多个线程并调用接收端功能类的run()方法就可以实现接收多组数据
    3.编写一个接收端程序,调用接收端功能类
  3. 编写读取文本的发送端程序

命名类

使用UUID类下的静态方法static UUID randomUUID() 来生成一个随机值的UUID对象,将这个对象转化为字符串,产生的字符串会是用‘-’连接的一些随机字母和数字,我们通过String类的replace()方法将‘-’去掉,并在结尾加上‘.txt’的后缀

public class UUIDUtils {    @Test    public static String getFileName(){        String fileName = UUID.randomUUID().toString();        fileName = fileName.replaceAll("-", "")+".txt";        return fileName;    }}

接收端功能类

  1. 通过构造方法传入接到的socket对象,通过socket对象调取的字节输入流转化为高效字符输入流
  2. 构造一个高效字符输出流,输出到的文件名采用命名类给出的随机文件名
  3. 建立循环,输入流每读取一行数据,输出流就将其写进文件中并换行
  4. 释放资源

——注意抓一下异常

public class ServerThread implements Runnable{    Socket sk;    public ServerThread(Socket sk){        this.sk = sk;    }    @Override    public void run() {        try {            //创建BufferedReader一次读取一行数据            BufferedReader br = new BufferedReader(new InputStreamReader(sk.getInputStream()));            BufferedWriter bw = new BufferedWriter(new FileWriter(UUIDUtils.getFileName()));            String line;            while ((line=br.readLine())!=null) {                bw.write(line);                bw.newLine();                bw.flush();                //System.out.println(line);            }        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

接收端

  1. 建立一个ServerSocket对象用来接收传送过来的Socket对象
  2. 建立循环接受Socket对象,创建进程调用run方法
public class TcpServer {    public static void main(String[] args) throws Exception {        //创建服务器端的socket对象        ServerSocket ss = new ServerSocket(2000);        while (true) {            //蒋婷来自于客户端的连接            Socket sk = ss.accept();            //启动一个子线程,去执行复制文件的动作            new Thread(new ServerThread(sk)).start();        }    }}

发送端

发送端和前面介绍的例子区别不大,但是要读取文件,就要以socket对象创建一个高效字符输入流,以此来用行作为单位发送数据,这里我们为了演示只发送同一个文件的内容

public class TcpClient {    public static void main(String[] args) throws Exception {        //创建socket对象        Socket sk = new Socket("192.168.20.254", 2000);                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sk.getOutputStream()));        //读取文本一次读取一行        BufferedReader br = new BufferedReader(new FileReader("InetAddressDemo.java"));        String line;        while ((line=br.readLine())!=null) {            //line就是我读取到的数据,我需要将这个数据写入通道,一次写一行            bw.write(line);            bw.newLine();            bw.flush();        }        //释放资源        br.close();        bw.close();        sk.close();    }}

socket编程其实就是io流的一个用法。数据是在发送端和接收端的socket之间传输,从根本上,还是用的io流那一套,只是从操作文件对象,变成了操作socket对象。

原创粉丝点击