IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Java的TCP/UDP网络编程+多线程实现服务器端与客户端间的通信 -> 正文阅读

[网络协议]Java的TCP/UDP网络编程+多线程实现服务器端与客户端间的通信

写在前面:

Java为网络编程提供了丰富的库,我们能通过调用Socket套接字的方法实现服务器与客户端的双通信。

注意点:

需要注意的是端口的对应,端口可以理解为窗户,服务器只能通过某个端口(窗户)与外界进行数据通信,客户端也如此。

所以

客户端与服务器端的通信就可以理解为服务器端的一个端口<------>客户端的一个端口 (”<--->“代表是全双工通信,收发信息互不影响)

为什么要用多线程?

因为要想客户端和服务器端互相发数据且互不干扰(以在控制台发数据为例),必须让读取键盘输入流为另一个线程,否则就会一直在等待。

TCP实现客户端与服务器端的通信:

效果:

?

服务器类:

package com4_18;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;


//服务器接受客户端信息
class STR implements  Runnable {
	private BufferedReader br;
	public  static 	boolean sw=false;
	public STR(BufferedReader bufferedReader) {
		this.br = bufferedReader;
	}
	String mString = null;

	@Override
	public void run() {
		try {
			if (!sw) {
				while (!(mString = br.readLine()).equals("bye")) {
					System.out.println("接受来自客户端的消息:" + mString);
				}
				System.out.println("客户端关闭");
				br.close();
			}
		} catch(Exception e){
				System.out.println("异常");
		}
	}
}
//服务器端给客户端发送数据
class  STT  implements  Runnable{
	private  PrintWriter pWriter=null;
	Scanner in=new Scanner(System.in);
	public STT(PrintWriter p){
		this.pWriter=p;
	}
	@Override
	public void run() {
		while (!STR.sw) {
			String str = in.nextLine();
			pWriter.println(str);
			System.out.println("向客户端发送数据:" + str);
			if(str.equals("bye"))STR.sw=true;
			pWriter.flush();
			}
		pWriter.close();
		try {
			ServerTest.server.close();
			ServerTest.client.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	}
public class ServerTest {
	private int port=7890;
	public  static  ServerSocket server;
	public  static Socket  client;
	public void connect() {
		
		
//		1.构造方法:创建一个服务,并占用一个端口号
		try {
			Scanner scanner =new Scanner(System.in);
			 server = new ServerSocket (port);
			  client= server.accept();
			if (client!=null) {
				System.out.println("成功连接到客户端");
			}
			
			InputStream is = client.getInputStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			
			OutputStream oStream  = client.getOutputStream();
			PrintWriter pWriter = new PrintWriter(oStream);
			pWriter.println("你好!客户端,我是服务器端!");
			pWriter.flush();

			STR str=new STR(br);
			Thread thread=new Thread(str);
			thread.start();

			STT stt=new STT(pWriter);
			Thread thread2=new Thread(stt);
			thread2.start();




		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}//端口号一般从1025开始
//		2.调用accept方法,让服务处于等待状态,并获得Socket
//		    Socket client= server.accept();
//		3.接收客户端发送的数据
//		InputStream is = client.getInputStream();      //BufferReader
//		4.向服务器发送数据
//		OutputStream os = client.getOutputStream();    //PrintWriter
//		5.server.close(); //网络通信结束后关闭服务

	}
	
	
	
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		new ServerTest().connect();
	}

}

客户端类:

package com4_18;

import jdk.nashorn.internal.ir.CatchNode;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

import javax.sound.sampled.Port;


/**
 * 客户端从服务器接收数据并打印到控制台上的线程
 */

class CTR implements  Runnable {

	private BufferedReader br;
	public  static 	boolean sw=false;
	public CTR(BufferedReader bufferedReader) {
		this.br = bufferedReader;
	}

	String mString = null;

	@Override
	public void run() {
		try {
				while (!(mString = br.readLine()).equals("bye")) {
				System.out.println("客户端接受来自服务器的消息:" + mString);
				}
				br.close();
				System.out.println("服务器端关闭");
		} catch (Exception e) {
			System.out.println("异常");
		}
	}
}

/**
 * 客户端从控制台发送给服务器线程
 */
class  CTT  implements  Runnable{

	private  PrintWriter pWriter=null;
	Scanner in=new Scanner(System.in);
	public CTT(PrintWriter p){
		this.pWriter=p;
	}
	@Override
	public void run() {
		while (!CTR.sw) {
			String str = in.nextLine();
			pWriter.println(str);
			System.out.println("客户端向服务器发送数据:" + str);
			if(str.equals("bye")) CTR.sw=true;
			pWriter.flush();
		}
		pWriter.close();
		try {
			ClientTest.client.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

public class ClientTest {
	private int port=7890;
	public  static Socket client;
	public void connect() {
		Scanner scanner =new Scanner(System.in);
//		1.与服务器建立连接,如通过IP地址(字符串)和端口构造:
		try {
			InetAddress address = InetAddress.getLocalHost();
			  client = new Socket(address,port);
			if (client!=null) {
				System.out.println("成功联系到服务器!");
			}
			//2.接收服务器发送的数据
			InputStream is = client.getInputStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			OutputStream oStream  = client.getOutputStream();
			PrintWriter pWriter = new PrintWriter(oStream);
			pWriter.println("你好!服务器,我是客户端!");
			pWriter.flush();
			CTT ctt=new CTT(pWriter);
			Thread thread=new Thread(ctt);
			thread.start();
			CTR ctr=new CTR(br);
			Thread thread1=new Thread(ctr);
			thread1.start();
		} catch (UnknownHostException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		new ClientTest().connect();
	}
}

UDP客户端与服务器端的通信(和上面的TCP一样,也是通过多线程的方式实现)

与上面的TCP不同的是,我设定服务器端默认给客户端回执信息和接受信息,而上面的TCP则是能通过控制台输入发送给客户端信息。

效果:

?

?

与客户端相关类:

客户端接受数据线程类:

package com4_25;

import java.io.IOException;
import java.net.DatagramPacket;
import static com4_25.ClientUDP.datagramSocket;

//客户端接受数据包
class  ClientUDPR  extends  Thread{
    //从服务器端接受信息
    private DatagramPacket datagramPacket;
 
    public  ClientUDPR() {
        System.out.println("this is UDPR");

    }
    public  void  run(){
        while (true) {
            byte[] buf = new byte[50];
            datagramPacket = new DatagramPacket(buf, buf.length);
            try {
                //接受数据包
                datagramSocket.receive( datagramPacket);
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            byte[] redata;

            redata=datagramPacket.getData();
            String strRecieved = new String(redata);
            System.out.println("从服务器端:" +  datagramPacket.getAddress() +
                    " 的端口:" +  datagramPacket.getPort() +
                    "接受到的内容:" + strRecieved);
        }
    }
}

客户端发送数据线程类:

package com4_25;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;



public class ClientUDP extends  Thread {
	private int port;
	public static int remotePort;
	InetAddress serverAdress;
	public static DatagramSocket datagramSocket = null;
	private DatagramPacket datagramPacket = null;    //包
	Scanner in = new Scanner(System.in);

	public ClientUDP(int port, InetAddress serverAdress, int remotePort) {
		super();
		this.port = port;
		this.serverAdress = serverAdress;
		this.remotePort = remotePort;
		try {
			this.datagramSocket = new DatagramSocket(port);
		} catch (SocketException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}

	public void run() {

		while (true) {
			String sentStr = in.nextLine();//从控制台接受数据
			byte[] buf;
			buf = sentStr.getBytes();
			datagramPacket = new DatagramPacket(buf, buf.length,
					serverAdress, remotePort);//发送数据包
			try {
				datagramSocket.send(datagramPacket);
				System.out.println("已发送给服务器");
			} catch (IOException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
	}

}

启动客户端测试类:

package com4_25;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class UDPTest {

    public static void main(String[] args) {
        // TODO 自动生成的方法存根
        InetAddress address;
        try {
            address = InetAddress.getLocalHost();
            new ClientUDP(8080, address, 8001).start();
             new ClientUDPR().start();
        } catch (UnknownHostException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
}


服务器类:

package com4_25;

import com.sun.org.apache.bcel.internal.generic.NEW;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class SeverUDP extends Thread {

	private int port;
	private DatagramSocket datagramSocket= null;
	private DatagramPacket datagramPacket= null;
	
		
	public SeverUDP(int port) {
		super();
		this.port = port;
		try {
			datagramSocket = new DatagramSocket(port);
		} catch (SocketException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}

	public void run() {
		if (datagramSocket==null) {
			System.out.println("没有成功创建服务器!");
		}
		
		while (true) {
			
			try {
				/**
				 * 服务器从客户端接受消息
				 */
				byte[]buf =new byte[50];
				datagramPacket = new DatagramPacket(buf,buf.length );
				datagramSocket.receive(datagramPacket);
				InetAddress address = datagramPacket.getAddress();
				int remotePort = datagramPacket.getPort();
				buf = datagramPacket.getData();

				String recievedData = new String(buf);
				System.out.println("从地址:"+address+"端口:(客户端)"+
				remotePort+"内容:" );
				char []mystr=recievedData.toCharArray();
				for (int i = 0; i <mystr.length; i++) {

					if(mystr[i]==' ')break;
					System.out.print(mystr[i]);
				}


				String sendStr = "服务器的回执(表示服务器已经收到)";

				/**
				 * 回敬给客户端消息
				 */
				buf = sendStr.getBytes();
				System.out.println(new String(buf));
				datagramPacket = new DatagramPacket(buf, buf.length, address, remotePort);
				datagramSocket.send(datagramPacket);
				
			} catch (IOException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}

		}
	}
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		new SeverUDP(8001).start();
	}

}

值得注意的是:TCP服务器与客户端的通信需要建立连接,而UDP服务器与客户端的通信则是以接受数据包SocketPackage的形式获取报文,而报文的长度一开始再创建Socketpacket时又无从得知。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-05-01 16:06:29  更:2022-05-01 16:06:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 3:01:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码