java之socket网络编程

网络编程(网络编程概述)(了解)
* A:计算机网络
    * 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
* B:网络编程
    * 就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。


网络编程(网络编程三要素之IP概述)(掌握)
* 每个设备在网络中的唯一标识
* ip的每个段都是0-255之间的
* 每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。 
* ipconfig:查看本机IP192.168.12.42
* ping:测试连接192.168.40.62
* 本地回路地址:127.0.0.1 255.255.255.255是广播地址
* IPv4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽。 
* IPv6:8组,每组4个16进制数。
* 1a2b:0000:aaaa:0000:0000:0000:aabb:1f2f
* 1a2b::aaaa:0000:0000:0000:aabb:1f2f
* 1a2b:0000:aaaa::aabb:1f2f
* 1a2b:0000:aaaa::0000:aabb:1f2f
* 1a2b:0000:aaaa:0000::aabb:1f2f

网络编程(网络编程三要素之端口号概述)(掌握)
* 每个程序在设备上的唯一标识
* 每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上,还要明确发到哪个程序。
* 端口号范围从0-65535
* 编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本上都被系统程序占用了。
* 常用端口
    * mysql: 3306
    * oracle: 1521
    * web: 80
    * tomcat: 8080
    * QQ: 4000
    * feiQ: 2425

网络编程(网络编程三要素协议)(掌握)
* 为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
* UDP
    * 面向无连接,数据不安全,速度快。不区分客户端与服务端。
* TCP
  * 面向连接(三次握手),数据安全,速度略低。分为客户端和服务端。
    * 三次握手: 第一次是客户端先向服务端发起请求,第二次 服务端响应请求,第三次 传输数据

网络编程(Socket通信原理)(了解)
* A:Socket套接字概述:
    * 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
    * 通信的两端都有Socket。
    * 网络通信其实就是Socket间的通信。
    * 数据在两个Socket间通过IO流传输。
    * Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和port。

网络编程(UDP传输)(了解)
* 1.发送Send
    * 创建DatagramSocket, 随机端口号
    * 创建DatagramPacket, 指定数据, 长度, 地址, 端口
    * 使用DatagramSocket发送DatagramPacket
    * 关闭DatagramSocket
* 2.接收Receive
    * 创建DatagramSocket, 指定端口号
    * 创建DatagramPacket, 指定数组, 长度
    * 使用DatagramSocket接收DatagramPacket
    * 关闭DatagramSocket
    * 从DatagramPacket中获取数据
* 3.接收方获取ip和端口号
    * String ip = packet.getAddress().getHostAddress();
    * int port = packet.getPort();

下面这是udp加GUI实现的聊天室

package com.heima.socket;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo4_GUIChat extends Frame {

	private TextField tf;
	private Button send;
	private Button log;
	private Button clear;
	private Button shake;
	private TextArea viewText;
	private TextArea sendText;
	private DatagramSocket socket;
	private BufferedWriter bw;
	/**
	 * @param args
	 * GUI聊天
	 */
	public Demo4_GUIChat() {
		init();
		southPanel();
		centerPanel();
		event();
	}

	public void event() {
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				try {
					socket.close();
					bw.close();
				} catch (IOException e1) {
					
					e1.printStackTrace();
				}
				System.exit(0);
			}
		});
		
		send.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					send();
				} catch (IOException e1) {
					
					e1.printStackTrace();
				}
			}

		});
		
		log.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					logFile();
				} catch (IOException e1) {
					
					e1.printStackTrace();
				}
			}

		});
		
		clear.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				viewText.setText("");
			}
		});
		
		shake.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					send(new byte[]{-1},tf.getText());
				} catch (IOException e1) {
					
					e1.printStackTrace();
				}
			}

		});
		
		sendText.addKeyListener(new KeyAdapter() {
			@Override
			public void keyReleased(KeyEvent e) {
				//if(e.getKeyCode() == KeyEvent.VK_ENTER && e.isControlDown()) {	//isControlDown ctrl是否被按下
				if(e.getKeyCode() == KeyEvent.VK_ENTER) {
					try {
						send();
					} catch (IOException e1) {
						
						e1.printStackTrace();
					}
				}
			}
		});
	}
	

	private void shake() {
		int x = this.getLocation().x;							//获取横坐标位置
		int y = this.getLocation().y;							//获取纵坐标位置
		
		for(int i = 0; i < 20; i++) {
			try {
				this.setLocation(x + 20, y + 20);
				Thread.sleep(20);
				this.setLocation(x + 20, y - 20);
				Thread.sleep(20);
				this.setLocation(x - 20, y + 20);
				Thread.sleep(20);
				this.setLocation(x - 20, y - 20);
				Thread.sleep(20);
				this.setLocation(x, y);
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
		}
	}
	
	private void logFile() throws IOException {
		bw.flush();									//刷新缓冲区
		FileInputStream fis = new FileInputStream("config.txt");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();	//在内存中创建缓冲区
		
		int len;
		byte[] arr = new byte[8192];
		while((len = fis.read(arr)) != -1) {
			baos.write(arr, 0, len);
		}
		
		String str = baos.toString();				//将内存中的内容转换成了字符串
		viewText.setText(str);
		
		fis.close();
	}
	
	private void send(byte[] arr, String ip) throws IOException {
		DatagramPacket packet = 
				new DatagramPacket(arr, arr.length, InetAddress.getByName(ip), 9999);
		socket.send(packet);						//发送数据
	}
	
	private void send() throws IOException {
		String message = sendText.getText();		//获取发送区域的内容
		String ip = tf.getText();					//获取ip地址;
		ip = ip.trim().length() == 0 ? "255.255.255.255" : ip;
		
		send(message.getBytes(),ip);
		
		String time = getCurrentTime();				//获取当前时间
		String str = time + " 我对:" + (ip.equals("255.255.255.255") ? "所有人" : ip) + "说\r\n" + message + "\r\n\r\n";	//alt + shift + l 抽取局部变量
		viewText.append(str);						//将信息添加到显示区域中
		bw.write(str);								//将信息写到数据库中
		sendText.setText("");
		
		
	}
	
	private String getCurrentTime() {
		Date d = new Date();						//创建当前日期对象
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
		return sdf.format(d);						//将时间格式化
	}

	public void centerPanel() {
		Panel center = new Panel();					//创建中间的Panel
		viewText = new TextArea();
		sendText = new TextArea(5,1);
		center.setLayout(new BorderLayout());		//设置为边界布局管理器
		center.add(sendText,BorderLayout.SOUTH);	//发送的文本区域放在南边
		center.add(viewText,BorderLayout.CENTER);	//显示区域放在中间
		viewText.setEditable(false);				//设置不可以编辑
		viewText.setBackground(Color.WHITE);		//设置背景颜色
		sendText.setFont(new Font("xxx", Font.PLAIN, 15));
		viewText.setFont(new Font("xxx", Font.PLAIN, 15));
		this.add(center,BorderLayout.CENTER);
	}

	public void southPanel() {
		Panel south = new Panel();					//创建南边的Panel
		tf = new TextField(15);
		tf.setText("127.0.0.1");
		send = new Button("发 送");
		log = new Button("记 录");
		clear = new Button("清 屏");
		shake = new Button("震 动");
		south.add(tf);
		south.add(send);
		south.add(log);
		south.add(clear);
		south.add(shake);
		this.add(south,BorderLayout.SOUTH);			//将Panel放在Frame的南边
	}

	public void init() {
		this.setLocation(500, 50);
		this.setSize(400, 600);
		new Receive().start();
		try {
			socket = new DatagramSocket();
			bw = new BufferedWriter(new FileWriter("config.txt",true));	//需要在尾部追加
		} catch (Exception e) {
			
			e.printStackTrace();
		}
		this.setVisible(true);
	}
	private class Receive extends Thread {			//接收和发送需要同时执行,所以定义成多线程的
		public void run() {
			try {
				DatagramSocket socket = new DatagramSocket(9999);
				DatagramPacket packet = new DatagramPacket(new byte[8192], 8192);
				
				while(true) {
					socket.receive(packet);				//接收信息
					byte[] arr = packet.getData();		//获取字节数据
					int len = packet.getLength();		//获取有效的字节数据
					if(arr[0] == -1 && len == 1) {		//如果发过来的数组第一个存储的值是-1,并且数组长度是1
						shake();						//调用震动方法
						continue;						//终止本次循环,继续下次循环,因为震动后不需要执行下面的代码
					}
					String message = new String(arr,0,len);	//转换成字符串
					
					String time = getCurrentTime();		//获取当前时间
					String ip = packet.getAddress().getHostAddress();	//获取ip地址
					String str = time + " " + ip + " 对我说:\r\n" + message + "\r\n\r\n";
					viewText.append(str);
					bw.write(str);
				}
			} catch (Exception e) {
				
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		new Demo4_GUIChat();
	}

}


TCP协议socket客户端

package chat;

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

/**
 * 聊天室客户端
 * @author lenovo
 * 
 * ipconfig就能显示出来ip地址了  在控制台上输入
 *
 */
public class Client {
	/*
	 * java.net.Socket
	 * 封装了TCP协议,使用它就可以基于TCP协议         UDP
	 * 进行网络通讯
	 * Socket是运行在客户端的
	 */
	private Socket socket;
	/**
	 * 构造方法,用来初始化客户端
	 * 实例化Socket的时候需要传入两个参数:
	 * 1.服务端地址:通过IP地址可以找到服务的那的
	 * 				计算机
	 * 2.服务端端口:通过端口可以找到服务端计算机
	 * 			上的服务端应用程序
	 * 实例化Socket的过程就是连接的过程,若远端计
	 * 算机没有响应会抛出异常。
	 * 
	 */
	public Client()throws Exception{
		//127.0.0.1本机  localhost   端口0~65535  8088  使用4000以后的  前四千好多应用程序使用的
		System.out.println("正在连接服务端。。。");
		socket = new Socket("localhost",8088);
		System.out.println("已与服务端建立连接!");
	}
	/**
	 * 启动客户端的方法
	 */
	public void start(){
		try{
			Scanner scanner = new Scanner(System.in);

			/*
			 * 现要求用户输入一个昵称
			 */
			String nickName = null;
			while(true){
				System.out.println("请输入用户名:");
				nickName = scanner.nextLine();
				if(nickName.length()>0){
					break;
				}
				System.out.println("输入有误!");
			}
			
			System.out.println("欢迎你,"+nickName+"!开始聊天吧");
			
			
			
			
			
			/*
			 * Socket提供的方法:
			 * OutputStream getOutputStream
			 * 获取一个字节输出流,通过该流写
			 * 出的数据会被发送至远端计算机。
			 */
			OutputStream out = socket.getOutputStream();
			
			OutputStreamWriter osw = new OutputStreamWriter(out);
			
			PrintWriter pw = new PrintWriter(osw,true);
			
			//先将昵称发送给服务端
			
			pw.write(nickName);
			
			/*
			 * 启动读取服务端发送过来消息的
			 * 线程
			 */
			ServerHandler handler = new ServerHandler();
			Thread t = new Thread(handler);
			t.start();
			/*
			 * 将字符串发送至服务端
			 */
			Scanner input = new Scanner(System.in);
			while(true){
				String nextLine = input.nextLine();
				pw.println(nextLine+"\n");
			}
			
			
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args){
		try{
			Client client = new Client();
			client.start();
		}catch(Exception e){
			e.printStackTrace();
			System.out.println("客户端启动失败!");
		}
	}
	/**
	 * 该线程用来读取服务端发送过来的消息
	 * 并输出到客户端控制台显示。
	 * @author lenovo
	 *
	 */
	class ServerHandler implements Runnable{
		public void run(){
			try{
				InputStream in = socket.getInputStream();
				InputStreamReader isr = new InputStreamReader(in,"UTF-8");
				BufferedReader br = new BufferedReader(isr);
				while(true){
					String message = br.readLine();
					System.out.println(message);
				}
				
			}catch(Exception e){
				
			}
		}
	}
}

服务端

package chat;

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.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * 聊天室服务端
 * @author lenovo
 * 1小时40
 */
public class Server {
	/**
	 * 运行在服务端的ServerSocket主要负责:
	 * 1.向系统申请服务端口
	 * 	 客户端就是通过这个端口与之连接的
	 * 2.监听申请的服务端口,当一个服务端通过
	 *   该端口尝试建立连接时,ServerSocket
	 *   会在服务端创建一个Socket与客户端建立
	 *   连接。
	 */
	private ServerSocket server;
	/*
	 * b保存所有客户端输出流的集合
	 */
	private List<PrintWriter> allOut;
	/**
	 * 用来初始化服务端
	 */
	public Server()throws Exception{
		/*
		 * 初始化的同时申请服务端口
		 */
		server = new ServerSocket(8088);
		
		allOut = new ArrayList<PrintWriter>();
	}
	/**
	 * 将给定的输出流存入共享集合
	 * @param out
	 */
	private synchronized void addOut(PrintWriter out){
		allOut.add(out);
	}
	/**
	 * 将给定的输出流从共享集合中删除
	 * @param out
	 */
	private synchronized void removeOut(PrintWriter out){
		allOut.remove(out);
	}
	/**
	 * 将给定的消息发送给所有客户端
	 * @param message
	 */
	private synchronized void sendMessage(String message){
		for(PrintWriter out:allOut){
			out.println(message);
		}
	}
	
	/**
	 * 服务端开始工作的方法
	 */
	public void start(){
		try{
			/*
			 * ServerSocket的accept方法
			 * 是一个阻塞方法,作用是监听
			 * 服务端口,直到一个客户端连接
			 * 并创建一个Socket,使用该Socket
			 * 即可与刚连接的客户端进行交互
			 */
			while(true){
				System.out.println("等待客户端连接。。。。");
				Socket socket = server.accept();
				System.out.println("一个客户端连接了!");
				
				/*
				 * 启动一个线程,来完成与客户端的
				 * 交互
				 */
				ClientHandler handler = new ClientHandler(socket);
				Thread t = new Thread(handler);
				t.start();
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public static void main(String[] args){
		try{
			Server server = new Server();
			server.start();
		}catch(Exception e){
			e.printStackTrace();
			System.out.println("服务端启动失败!");
		}
	}
	/**
	 * 该线程负责处理一个客户端的交互
	 * 
	 *
	 */
	class ClientHandler implements Runnable{
		/*
		 * 该线程处理的客户端的Socket
		 */
		private Socket socket;
		
		//客户端地址信息
		private String host;
		
		//该用户的昵称
		private String nickName;
		
		public ClientHandler(Socket socket){
			this.socket = socket;
			/*
			 * 通过Socket可以获取远端计算机的
			 * 地址信息。
			 */
			InetAddress address = socket.getInetAddress();
			//获取ip地址
			host = address.getHostAddress();
		}
		
		public void run(){
			PrintWriter pw = null;
			try{
				//System.out.println(host+"上线了!");
				
				
				/*
				 * Socket提供的方法
				 * InputStream getInputStream()
				 * 该方法可以获取一个输入流,从该流读取的
				 * 数据就是从远端计算机发送过来的
				 */
				InputStream in = socket.getInputStream();
				
				InputStreamReader isr = new InputStreamReader(in);
				
				BufferedReader br = new BufferedReader(isr);
				
				//首先读取一行字符串为昵称
				
				nickName = br.readLine();
				
				sendMessage(nickName+"上线了!");
				;
				
				/*
				 * 通过Socket创建输出流用于将消息
				 * 发送给客户端
				 */
				OutputStream out = socket.getOutputStream();
				OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
				
				pw = new PrintWriter(osw,true);
				
				/*
				 * 将该客户端的输出流存入到共享
				 * 集合中
				 */
				allOut.add(pw);
				
				/*
				 * br.readLine在读取客户端发过来的
				 * 消息时,由于客户端短线,而其操作系统
				 * 的不同,这里读取后的结果不同:
				 * 当windows的客户端断开时:br.readLine
				 * 会抛出异常
				 * 当linux的客户端断开时:br.readLine会
				 * 返回null
				 */
				while(true){
					String message = br.readLine();
					//System.out.println(host+"说:"+message);
					//pw.println(host+"说:"+message);
					
					//广播消息
					sendMessage(nickName+"说:"+message);
					
				}
				
			}catch(Exception e){
				
			}finally{
				/*
				 * 处理当前客户端断开后的逻辑
				 */
				//将该客户端的输出流从共享集合中删除
				allOut.remove(pw);
				
				sendMessage(nickName+"下线了!");
				
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/kxj19980524/article/details/86572217