java网络编程(多线程TCP开发、多线程UDP开发)

1.用 java编写简单的客户端服务端程序

1.1java中有三种套接字类

java.net.Socket、java.net.ServerSocket和java.net.DatagramSocket。其中Socket跟SocketServer类是建立在TCP协议基础上,DatagramSocket类是建立在UDP协议基础上的。

1.2 简单的C/S架构网络编程示例(Socket、ServerSocket)

服务器代码:

  public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(5678);
        //反复监听
        while(true){
            //没有监听到时处于挂起状态
            Socket client = server.accept();
            PrintWriter pw = new PrintWriter(client.getOutputStream(),true);
            pw.print("你好,我是服务器!");
            pw.print("服务器给你发送了一条消息!");
            pw.close();
            client.close();
        }
    }

客户端代码:

public static void main(String[] args) throws IOException{
       Socket server = new Socket(InetAddress.getLocalHost(),5678);
       BufferedReader bf = new BufferedReader(new InputStreamReader(server.getInputStream()));
       System.out.println(bf.readLine());
        bf.close();
        server.close();
    }
    

2.java中URL类、URLConnection类、HTTPConnection类

2.1 URL简单介绍

URL是统一资源定位符。URL由协议,主机名,端口,路径等组成。
java中关于URL类的构造函数有:

1.Public URL (String protocol,String host, int port, String file)throws MalformedURLException 
2.Public URL (String protocol,String host,  String file)throws MalformedURLException 
3.Public URL (String protocol,String host, int port, String file, URLStreamHandler handler)throws MalformedURLException 
4.Public URL (String spec)throws MalformedURLException 通过URL字符串构造URL对象
5.Public URL (URL context, String spec)throws MalformedURLException 通过URL对象和URL部分字符串构造URL对象
6.Public URL (URL context, String spec,URLStreamHandler handler)throws MalformedURLException 通过URL对象和URL部分字符串构造URL对象,同时设定对象的流处理器

2.2 URL类

URL类提供一些方法来查询组成URL的各个部分,getQuery,getPath,getPort等等。还可以打开URL连接以及比较两个URL是否指向同一地址等等。

2.3 URLConnection类

java对超文本传输协议提供了很好的支持,在java.net包下提供了几个支持HTTP协议的类,主要是URLConnection和HTTPConnection。
URLConnection类可以被用来发送http请求以及读取http请求。构造函数如下所示:

Protected URLConnection(URL url)

该类只有一个构造方法还是受保护的,无法通过New来创建实例。此时就需要用到URL类了,重点操作构造URLConnection实例

URL url = new URL("http://www.csdn.net");
//创建URLConnection 对象
URLConnection urlConnection = url.openConnection();
//连接url
urlConnection.connect();
//获取连接的信息
urlConnection.getContentEncoding();
urlConnection.getContent();
urlConnection.getRequestProperties()

以上代码可以构造一个URLConnection实例,注意如果url是http请求,其实 url.openConnection()返回的是一个HTTPConnection对象。即:

HttpURLConnection urlConnection=(HttpURLConnection)url.openConnection();

2.4 HTTPConnection是URLConnection子类

3.java与TCP网络协议开发

3.1 Socket类

3.1.1 Socket类的构造方法

	Socket()
	Socket(InetAddress address,int port)
	Socket(InetAddress address,int port,InetAddeess localAddr,int localPort)
	Socket(Proxy proxy)
	Socket(String host, int port)

3.1.2 Socket设置等待建立连接的超时时间

Socket socket = new Socket();//默认构造方法
SocketAddress remoteAddress = new InetSocketAddress("localhost",5678);
socket.connect(remoteAddress,60000);//建立连接,60000为超时时间,超时抛SocketTimeoutException异常

3.1.3 Socket客户连接服务器可能抛出的异常

(1).UnknownHostException:无法识别主机、ip地址
(2).ConnectionException:服务器拒绝连接,没有服务器进程监听端口
(3).SocketTimeoutException:等待连接超时
(4).BindException:

Socket socket = new Socket();
socket = new Socket("localhost",5678, InetAddress.getByName("172.23.34.56"),6789);

如上代码,把Socket本地ip设为172.23.34.56,端口6789,如果本地不支持此IP,或端口占用会抛出BindException

3.1.4 获取Socket信息

getInetAddress()返回套接字连接的地址
getPort()返回套接字连接的远程端口
getLocalPort()返回绑定的本地端口
getLocalAddress()返回套接字绑定的本地地址
getInputStream()返回输入流,如果输入流shutdownInput则抛出IOException
getOutputStream()同上,输出流

3.1.4 Socket半关闭

1.shutdownInput():关闭Socket输入流,输出流依然可用。
2.shutdownOutput():同上。
3.isInputShutdown(),isOutputShutdown():检查输入流输出流是否关闭。

3.1.4 Socket关闭

1.close()关闭socket
2.isClosed(),isConnected(),isBound()检查Socket连接状态

3.2 ServerSocket类

3.2.1 ServerSocket构造方法

ServerSocket();
ServerSocket(int port);
ServerSocket(int port,int backlog);
ServerSocket(int port,int backlog,InetAddress bindAddr);

port是服务器要监听的端口,backlog指定客户端连接请求队列的长度,bindAdr指定服务器要绑定的ip地址。

3.2.2 ServerSocket设定客户端请求队列的长度

客户端Client代码:

 public static void main(String[] args) throws IOException{
        final int length = 10;
        String host = "localhost";
        int port = 5678;
        Socket[] sockets = new Socket[length];
        for(int i = 0; i < length; i++){
            sockets[i] = new Socket(host,port);
            System.out.println("第"+(i+1)+"次连接成功");
        }
        try {
            Thread.sleep(3000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        for(int i = 0; i < length; i++){
            sockets[i].close();
        }
        }
    }

服务端Server代码:

public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(5678, 3);
        try {
        	//等待40秒关闭服务
            Thread.sleep(6000 * 40);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

控制台输出:

1次连接成功
第2次连接成功
第3次连接成功
Disconnected from the target VM, address: '127.0.0.1:55988', transport: 'socket'
Exception in thread "main" java.net.ConnectException: Connection refused: connect
	at java.net.DualStackPlainSocketImpl.connect0(Native Method)
	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
	at java.net.Socket.connect(Socket.java:589)
	at java.net.Socket.connect(Socket.java:538)
	at java.net.Socket.<init>(Socket.java:434)
	at java.net.Socket.<init>(Socket.java:211)
	at cn.rails.hr.sso.common.vo.ClassE.main(ClassE.java:19)

Process finished with exit code 1

因为队列中只允许3个请求。
如果将服务端代码做更改,接收到client请求之后,处理掉,不占用队列,则可以持续接收请求。服务端代码改动如下:

 public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(5678, 3);
        //反复监听
        while(true){
            //没有监听到时处于挂起状态
            Socket client = server.accept();
            //处理客户端请求	            		     
            System.out.println(client.getInetAddress()+":"+client.getPort());
            client.close();
        }
    }

client控制台输出:

Connected to the target VM, address: '127.0.0.1:56680', transport: 'socket'1次连接成功
第2次连接成功
第3次连接成功
第4次连接成功
第5次连接成功
第6次连接成功
第7次连接成功
第8次连接成功
第9次连接成功
第10次连接成功

3.2.3 ServerSocket设定绑定的IP地址

如果一个主机有一个IP地址,则默认与该IP地址绑定。如果一个主机有两个 网卡,一个网卡用于连接Internet,IP地址为234.33.22.11,一个网卡用于连接本地局域网,IP地址为192.168.11.22。此时服务器仅仅允许被局域网的Client访问,则创建ServerSocket如下:

ServerSocket serverSocket = new ServerSocket(5678,10,InetAddress.getByName("192.168.11.22"));

3.2.4 ServerSocket默认构造方法的作用

默认构造方法是创建不绑定任何端口的ServerSocket(new ServerSocket()),意义:一旦服务器与端口绑定,有些选项无法设置。
需要在绑定接口之前设置选项。例如:

ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(8000),10);

3.2.5 ServerSocket接收与关闭客户端连接

ServerSocket的accept()方法从请求队列中取出一个客户的连接请求,然后创建与客户端连接的Socket对象,并返回。如果队列中没有连接请求则会一直等待。
ServerSocket的close()方法,用于释放服务器占用的端口。
ServerSocket的isClosed()方法,判断ServerSocket是否关闭。
ServerSocket的isBound()方法,判断ServerSocket是否与一个端口绑定,只要ServerSocket已经与一个端口绑定,即使已经关闭调用sClosed()方法,依然返回true。
例子:判断一个ServerSocket与端口绑定,并且未关闭,则采用如下方式:

ServerSocket serverSocket = new ServerSocket();
boolean isOpen = serverSocket.isBound() && !serverSocket.isClosed();

3.3 基于TCP的C/S架构程序

3.3.1 单线程的C/S架构程序

C/S架构程序基于TCP的通信协议,是网络通信模型的基础,流程设计如下:
1.客户端:

(1).用服务器的IP地址以及端口实例化Socket对象。
(2).调用connect方法,连接到服务器。
(3).将发送到服务器的I/O流填充到I/O对象中,例如BufferReader/PrinterWriter。
(4).利用Socket提供的getInputStream和getOutputStream方法,通过I/O流对象,向服务器发送数据流。
(5).通信结束后,关闭打开的I/O对象以及Socket。

2.服务器:

(1).用一个端口实例化一个ServerSocket对象。
(2).调用accept()方法,监听连接对象从端口发送来的连接请求。
(3).通信结束,关闭流以及Socket对象。

3.服务器代码

public static void main(String[] args) throws IOException{
        ServerSocket serverSocket = new ServerSocket(5678);
        Socket socket = serverSocket.accept();
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
            while (true) {
                String str =  in.readLine();
                System.out.println(str);
                out.println(str);
                if (str.equals("end")) {
                    in.close();
                    out.close();
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            socket.close();
            serverSocket.close();
        }
    }

3.客户端代码

public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost",5678);
        try{
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
            out.println("你好,我是客户端");
            out.println("客户端消息");
            out.println("end");
            while(true){
              String str = in.readLine();
              System.out.println(str);
              if(str.equals("end")){
                  in.close();
                  out.close();
                  break;
              }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            socket.close();
        }
    }

3.3.2 多线程的C/S架构程序

服务器端代码:
ServerThreadUtil 线程类:

public class ServerThreadUtil extends Thread {
   private BufferedReader in;
   private Socket s;
   //构造方法
   public ServerThreadUtil(Socket s) throws IOException {
       this.s = s;
       in = new BufferedReader(new InputStreamReader(s.getInputStream()));
       start();
   }
   public void run() {
       try {
           while (true) {
               String str = in.readLine();
               System.out.println(str);
               if(str.equals("end")){
                   break;
               }
           }
       }catch (Exception e){
           e.printStackTrace();
       }finally {
           try {
               in.close();
               this.s.close();
           }catch (IOException ioe){
               ioe.printStackTrace();
           }
       }
   }
}

服务端主类

public static void main(String[] args) throws IOException {
     ServerSocket serverSocket = new ServerSocket(5678);
     try {
         while (true) {
             Socket socket = serverSocket.accept();
             new ServerThreadUtil(socket);
         }
     } catch (Exception e) {
         e.printStackTrace();
     } finally {
         serverSocket.close();
     }
 }

客户端代码:
ClientThreadUtil 线程类

public class ClientThreadUtil extends Thread {
    private static int count = 0;
    private int clientId = ++count;
    private PrintWriter out;
    private Socket c;
    public ClientThreadUtil(Socket c) throws IOException{
        out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(c.getOutputStream())),true);
        this.c = c;
        start();
    }

    public void run() {
            out.println("你好,我是客户端" + clientId);
            out.println("客户端" + clientId + "发送了一条消息");
            out.println("end");
        try{
            this.c.close();
        }catch (IOException ioe){
            ioe.printStackTrace();
        }
    }
}

客户端主类

public static void main(String[] args) throws IOException {
      for(int i = 0; i<3; i++){
          Socket socket = new Socket("localhost",5678);
          new ClientThreadUtil(socket);
      }
}

最终控制台输出:

Connected to the target VM, address: '127.0.0.1:50089', transport: 'socket'
你好,我是客户端1
客户端1发送了一条消息
end
你好,我是客户端2
客户端2发送了一条消息
end
你好,我是客户端3
客户端3发送了一条消息
end

3.4 基于UDP的通信程序

3.4.1 DatagramSocket类

1.构造方法:

DatagramSocket():创建实例。绑定本机默认ip,端口号随机选择
DatagramSocket(int port):创建实例。绑定默认ip,绑定指定端口
DatagramSocket(int port, InetAddress addr):创建实例。绑定指定ip,指定端口

2.接收、发送方法

receive(DatagramPacket p):从DatagramSocket中接收数据报
send(DatagramPacket p):从DatagramSocket中向外发送数据报

3.4.2 DatagramPacket类

从上面DatagramSocket方法可以看出,使用DatagramSocket对象发送数据时,并不知道要将数据报发送到哪儿,这些是由DatagramPacket对象决定的。

1.构造方法

DatagramPacket(byte[] buf,int length):以一个空数组来创建DatagramPacket对象,指定对象长度,通常用于接收数据报
DatagramPacket(byte[] buf,int length,InetAddress addr, int port):以一个包含数据的数组来创建DatagramPacket对象,指定对象长度,目标地址以及端口,通常用于发送数据报
DatagramPacket(byte[] buf, int offset, int length):以空数组构造DatagramPacket对象,指定接收到的数据放在buf数组中时从offset开始,最多存放length字节,通常用于接收数据。
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):构建一个DatagramPacket对象,指定目标地址以及端口,指定buf数组从offset开始发送数据,一共length长度个字节,用于发送数据。

2.在接收数据之前通常采用第一个或者第三个构造器来初始化一个DatagramPacket对象,给出接收数据的数组以及长度,然后采用receive()方法来等待数据的到来,receive()将一直等待(该方法会阻塞调用该方法的线程),直到接收到一个数据报。

3.4.3 基于多线程的UDP程序

多个客户端发送消息,服务端接收,输出

服务端代码

 public static void main(String[] args) throws IOException {
        DatagramSocket server = new DatagramSocket(5678, InetAddress.getByName("localhost"));
        byte[] buf = new byte[10];
        while(true){
            DatagramPacket packet = new DatagramPacket(buf,1,buf.length-1);
            server.receive(packet);
            String content = new String(packet.getData());
            System.out.println(content);
            if(content.equals("end")){
                break;
            }
        }
    }

客户端代码:

public class ClientUtil  implements Runnable {
    static String content;
    static DatagramSocket client;
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        for (int i = 5679; i < 5682; i++) {
            client  = new DatagramSocket(i, InetAddress.getByName("localhost"));
            content = in.readLine();
            new Thread(new ClientUtil()).start();
        }
    }
    @Override
    public void run () {
        try {
            client.send(new DatagramPacket(content.getBytes(), content.getBytes().length, InetAddress.getByName("localhost"), 5678));
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

至此java关于TCP、UDP编程的基本知识点就写完了,喜欢的可以点赞收藏!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值