Java网络编程浅谈

转载请注明出处:http://blog.csdn.net/github_39430101/article/details/77753870

TCP通信

Socket简介

这里写图片描述
socket通常称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄。其实就是客户端和服务端之间通信的一条管道。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口应用于不同的服务。
应用程序通常通过”套接字”向网络发出请求或者应答网络请求。Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。

获取本地地址和端口号

java.net.Socket为套接字类,其提供了很多方法,其中我们可以通过Socket获取本地的地址以及端口号。

int getLocalPort()     该方法用于获取本地使用的端口号
InetAddress getLocalAddress()    该方法用于获取套接字绑定的本地地址
String getCanonicalHostName()    获取此IP地址的完全限定域名
String getHostAddress()   返回IP地址字符串(以文本表现形式)

Demo

public class TestSocket {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",8080);
            InetAddress add = socket.getLocalAddress();
            System.out.println(add.getCanonicalHostName());
            System.out.println(add.getHostAddress());
            System.out.println(add.getLocalHost());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
获取远端地址和端口号

Socket也提供了获取远端的地址以及端口号的方法

int getPort()     该方法用于获取远端使用的端口号
InetAddress.getInetAddress()    该方法用于获取套接字绑定的远端地址

Demo

public void testSocket() throws Exception{
    Socket socket = new Socket("localhost",8088);
    InetAddress inetAdd = socket.getInetAddress();
    System.out.println(inetAdd.getCanonicalHostName());
        System.out.println(inetAdd.getHostAddress());
        System.out.println(socket.getPort()); 
}
获取网络输入流和网络输出流

通过Socket获取输入流与输出流,这两个方法是使用Socket通讯的关键方法。封装了TCP协议的Socket是基于流进行通讯的。所以我们在创建了双方连接后,只需要获取相应的输入与输出流即可。

InputStream    getInputStream()   该方法用于返回此套接字的输入流
OutputStream   getOutputStream()    该方法用于返回此套接字的输出流

Demo

public void testSocket() throws Exception {
    Socket socket = new Socket("localhost",8088);
    InputStream is = socket.getInputStream();
    OutputStream os = socket.getOutputStream();
}

Socket通讯模型

这里写图片描述

Server端ServerSocket监听

java.net.ServerSocket是运行于服务端应用程序中。通常创建ServerSocket需要指定服务端口号,之后监听Socket的连接。监听方法为:

Socket   accept()   该方法是一个阻塞方法
//创建ServerSocket并申请服务端口8088
ServerSocket server = new ServerSocket(8088);
//方法会产生阻塞,直到某个Socket连接,并返回请求连接的Socket
Socket socket = server.accept();
Client端Socket连接
Socket socket = new Socket("localhost",8088);
完整示例(单线程)
  1. 服务端创建ServerSocket
  2. 通过调用ServerSocket的accept方法监听客户端的连接
  3. 客户端创建Socket并指定服务端的地址以及端口来建立与服务端的连接
  4. 当服务端accept发现客户端连接后,获取对应客户端的Socket
  5. 双方通过Socket分别获取对应的输入与输出流进行数据通讯
  6. 通讯结束后关闭连接
/**************** Server端 *****************/
public class ServerDemo {
    public static void main(String[] args) {
        ServerSocket server = null;
        try {
            //创建ServerSocket并监听8088端口
            server = new ServerSocket(8088);
            //监听客户端的连接
            System.out.println("----等待客户端的连接----");
            Socket socket = server.accept();
            //客户端连接后,通过该Socket与客户端交互
            //获取输入流,用于读取客户端发送过来的消息
            InputStream is = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8"));
            //获取输出流,用于向客户端发送消息
            OutputStream os = socket.getOutputStream();
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(os,"UTF-8"),true);
            pw.write("我是服务端,我监听到你了哦");
            pw.flush();
            //读取客户端发送的消息
            String message = reader.readLine();
            System.out.println("客户端说:"+message);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(server != null) {
                try {
                    server.close();
                } catch (IOException e) {
                }
            }
        }
    }
}
/********** Client端 ************/
public class ClientDemo {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket("localhost",8088);
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
            //获取输出流,用于向服务端发送消息
            OutputStream os = socket.getOutputStream();
            OutputStreamWriter ow = new OutputStreamWriter(os,"UTF-8");
            PrintWriter pw = new PrintWriter(ow,true);
            //向服务端发送一个字符串
            pw.println("你好服务器");
            pw.flush();
            String message = br.readLine();
            System.out.println("服务器说:"+message);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(socket != null) {
                    socket.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }
}

先启动服务器,后连接。

Server端多线程模型

我们已经知道如何使用ServerSocket与Socket进行通讯了,但是只能点对点,一个服务端对一个客户端。若我们想让一个服务端可以同时支持多个客户端应该怎么做呢?这时我们需要分析之前的代码。我们可以看到,当服务端的ServerSocket通过accept方法监听到一个客户端连接后,就获取该Socket并与该客户端通过流进行双方的通讯了,这里的问题在于,只有不断的调用accept方法,我们才能监听到不同客户端的连接。但是若我们循环监听客户端的连接,又无暇顾及与连接上的客户端交互,这时我们需要做的事情就是并发。我们可以创建一个线程类ClientHandler,并将于客户端交互的工作全部委托线程来处理。这样我们就可以在当一个客户端连接后,启动一个线程与客户端交互,而我们也可以循环监听客户端的连接了。

/*************** Server端应用程序 **************/
package com.code.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
    public static void main(String[] args){
        ServerSocket server = null;
        try{
            server = new ServerSocket(8088);
            while(true){
                //循环监听客户端的连接
                Socket socket = server.accept();
                //当一个客户端连接后,启动线程来处理该客户端的交互
                new ClientHandler(socket).start();
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            if(server != null){
                try{
                    server.close();
                } catch (IOException e){
                }
            }
        }
    }
}
/*
 * 线程类   该线程的作用是并发与客户端进行交互
 */
class ClientHandler extends Thread{
    private Socket socket;
    public ClientHandler(Socket socket) {
        this.socket = socket;
    }
    public void run() {
        try {
            //获取输入流,用于读取客户端发送过来的消息
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
            String info = null;
            while(null != (info=br.readLine())) {
                System.out.println("客户端说:"+info);
            }
            //获取输出流,用于向客户端发送给消息
            OutputStream os = socket.getOutputStream();
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(os,"UTF-8"),true);
            pw.println("你好客户端");
            pw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

UDP通信

创建接受包

DatagramPacket:UDP数据报给予IP建立的,每台主机有65536个端口号可以使用。数据报中字节数限制为65536-8.包含8字节的头信息。

DatagramPacket(byte[] buf,int length)

将数据包中Length长的数据装进buf数组

DatagramPacket(byte[] buf,int offset,int length)

将数据包中从offset开始,length长的数据装进buf数组

创建发送包
DatagramPacket(byte[] buf,int length,InetAddress clientAddress,int clientPort)

从buf数组中,取出length长的数据创建爱你数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。

DatagramPacket(byte[] buf,int offset,int length,InetAddress clientAddress,int clientPort)

从Buf数组中,取出Offset开始的、Length长的数据创建数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。

DatagramSocket
服务端接收

DatagramSocket用于接收和发送UDP的Socket实例

DatagramSocket(int port)

创建实例,并固定监听port端口的报文。通常用于服务端。
其中方法:

receive(DatagramPacket d)

接收数据报文到d中。receive方法产生阻塞。会一直等待直到有数据被读取到。

客户端发发送

无参的构造方法DatagramSocket()通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。程序会让操作体统分配一个可用的端口。

send(DatagramPacket dp)

该方法用于发送报文dp到目的地
代码如下:

/*****************  Server端   *****************/
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpServer {
    public static void main(String[] args) {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(8088);
            byte[] data = new byte[1024];
            DatagramPacket dp = new DatagramPacket(data,data.length);
            socket.receive(dp);//会产生阻塞,读取发送过来的数据
            String str = new String(dp.getData(),0,dp.getLength());//从包中取数据
            System.out.println(str);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(socket != null) {
                socket.close();
            }
        }
    }
}
/*********************** 客户端  **********************/
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UdpClient {
    public static void main(String[] args) {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(); //创建socket
            byte[] data = "你好".getBytes();
            //创建爱你发送包
            DatagramPacket dp = new DatagramPacket(data,data.length,InetAddress.getByName("localhost"),8088);
            socket.send(dp); //发送数据
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(socket != null) {
                socket.close();
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值