网络编程

概述

IP

  • 网络中计算机设备的标识

  • 表示方式
    ipv4:4组8位的二进制数,转换成4组十进制数显示为XXX.XXX.XXX.XXX
    ipv6:8组
    16位的二进制数,转换成8组十六进制数显示为XXXX:XXXX:XXXX:XXX

  • cmd指令
    ipconfig
    ping 域名/服务器IP地址

  • 本机IP地址: localhost 127.0.0.1

端口

  • 计算机设备中应用的标识
  • 写法:Ip:端口号
  • 分类
    0~1023 特殊/系统应用
    其他:普通应用
    端口号冲突会导致应用无法使用

传输层协议

网络传输规则(格式/速度/大小)
HTTP TCP/IP

  • TCP Transmission Control Protocal 传输控制协议
    面向字节流的
    有缓冲区的
    连接的(所以接收时也需要指定Ip地址端口号)可靠的(三次握手,socket与serversocket建立连接时如果serversocket还未开启就绪,会报错)

  • Internet Protocal互联网协议

  • UDP User Datagram Protocal 用户数据报协议
    面向数据报的
    无缓冲区的
    非连接的,不管对方状态,直接发送数据(DatagramSocket直接通过send 和 receive 方法发送读写数据。收件地址未就绪就没有效果而已,不会爆异常)

UDP不可靠(会丢包),快速即时通信、在线
UDP不可靠(会丢包),快速即时通信、在线
TCP可靠,速率较慢下载
TCP可靠,速率较慢下载

java.net中封装的网络通信相关类

InetAddress

构造方法私有化的,需要通过静态方法获取对象getByName(“域名/IP地址”)
获取当前域名的域名网址+IP地址;

getAllByName(“主机名/IP地址”)
一个域名可能有多个服务器,可获取当前域名下的所有主机名+IP地址的数组(InetAddress[])

getByAddress(byte[] addr)
通过"IP地址".geyBytes 获取InetAddress对象

getHostName()
获取域名网址

getLocalHost()
返回本地主机的InetAddress对象

getHostAddress()
获取服务器地址
在这里插入图片描述

DatagramSocket

发送接收者对象,用于进行数据发送和接收(send 和 receive 方法)

  1. 创建发送者对象,通过参数指定自身端口号
  2. ①调用receive方法,传入DatagramPacket对象(含字节数组缓冲区及其大小即可),实现接收
    ②调用send方法,传入DatagramPacket对象(包含字节数组本身,字节数组的长度,InetAddress对象,端口号),实现发送
  3. 通过datagram对象的getData方法获取数据内容,getLength获得长度

DatagramPacket

封装要传输的数据和发送的地址

字节数组本身字节数组的长度InetAddress对象端口号备注
发送者需要的DatagramPacket 对象
接受者需要的DatagramPacket 对象
//案例:多线程模拟双人会话(每个线程是一个客户端,各自都可读可写且互不同步,即无需互斥),以下为其中一个线程,另一个线程只需发送接收的IP和端口号与其对调即可
public static void main(String[] args) throws IOException {
		System.out.println("客户端1");
		Scanner sc1 = new Scanner(System.in);
		//指定发送者对象和自己的端口号
		DatagramSocket ds  = new DatagramSocket(7788);
		
		new Thread(){
			byte[] buf = new byte[1024];
			@Override
			public void run()  {
				while(true){
					try {
						//准备要发送的数据(转换成字节数组)
						System.out.println("zoya请输入");
						String string ="\t"+ sc1.next();
						buf = string.getBytes();
						//创建要发送的数据包对象(含数据、长度、目标地址IP、目标端口号)
						DatagramPacket dp = new DatagramPacket(buf, buf.length,InetAddress.getLocalHost(), 8899);
							//创建发送数据包对象
						//执行发送
						ds.send(dp);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread(){
			byte[] buf = new byte[1024];
			DatagramPacket dp = new DatagramPacket(buf, buf.length);
			@Override
			public void run() {
				while(true){
					try {
						//走到recevie方法,程序阻塞,等待发送者发送,等待接收
						ds.receive(dp);
						//显示接收到的数据(收到消息)
						System.out.println(new String(dp.getData()));
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
		}
	}.start();
}

ServerSocket

Socket

在这里插入图片描述
socket创建时绑定的IP地址可以用以下任意形式
在这里插入图片描述

多人聊天室

  • 服务端
package com.hw.study;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;

public class Server {
	public static void main(String[] args) throws IOException {
		System.out.println("=====聊天室开启=====");
		//存储所有连接过来的客户端socket
		LinkedList<Socket> sockets = new LinkedList<>();
		//指定服务器段server socket
		ServerSocket serverSocket = new ServerSocket(61000);
		
		//死循环不断接收客户端连入
		new Thread(){
			@Override
			public void run(){
				//可以不停接收新的客户端加入
				while(true){
					try {
						//不停等待接收新的客户端socket
						Socket recievedFromSocket = serverSocket.accept();
						//加入socket集合
						sockets.add(recievedFromSocket);
						//接收客户端的消息,接收到时群发给所有客户端
						//为每一个客户端都开一个线程,与其进行网络通信
						new Thread(){
							@Override
							public void run(){
								//初次进入的状态设置,后面不会再走到这一句
								int status=0;
								//死循环向客户端发消息(没有消息可发时阻塞在read方法)
								while(true){
									String msg=null;
									String clientIP = recievedFromSocket.getInetAddress().getHostAddress();
									try {
										//初次连接(客户端进入)
										if(status == 0){
											msg = "\t"+getTime()+"\t"+clientIP+"加入";
											sendToAll(msg,sockets);
											status=1;
										//再次连接(客户端发消息了)
										}else{
											InputStream ips = recievedFromSocket.getInputStream();
											int len = 0;
											byte[] b = new byte[1024];
											while((len=ips.read(b))!=-1){
												msg = clientIP+":"+new String(b,0,len);
												//剩下的一切必须在read所在的while循环内执行
												//因为在socket连接时网络字节流不会关闭,read也一直处于阻塞状态,不会出这个循环。
												//循环后面的语句除非异常则执行不到。
												sendToAll(msg,sockets);
											}
										}
									} catch (IOException e) {
										//当有一个客户端退出时,由于服务器还在等待其输入,其流关闭,会报异常。
										//如果只是打印异常信息,因为在死循环里,会不停打印。所以要关闭线程
										msg=recievedFromSocket.getInetAddress().getHostAddress()+"退出";
										//移除出集合,避免后续再向其发数据发不过去报异常。
										sockets.remove(recievedFromSocket);
										//向其他所有客户端通知某个已经退出
										sendToAll(msg, sockets);
										//关闭当前线程。
										return;
									}
								}
							}
						}.start();	
					} catch (Exception e) {
					}
				}
			}
		}.start();
	}
	
	//封装发送方法
	public static void sendToAll(String msg,LinkedList<Socket> sockets) {
		for(int j = 0; j< sockets.size();j++){
			try {
				OutputStream ops = sockets.get(j).getOutputStream();
				ops.write(msg.getBytes());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	//封装获取时间的方法
	public static String getTime() throws IOException{
		Date now = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
		return sdf.format(now);
	}

}

  • 客户端
package com.hw.study;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;


public class ClientA {
	public static void main(String[] args) throws Exception, IOException {
		System.out.println("客户端A开启");
		Socket client = new Socket("服务的IP",服务的端口号);
		//收取线程
		new Thread(){
			@Override
			public void run(){
				while(true){
					try {
						InputStream ips = client.getInputStream();
						int i=0;
						byte[] bs = new byte[1024];
						while((i=ips.read(bs))!=-1){
							System.out.println(new String(bs,0,i,"utf-8"));
						}
					} catch ( Exception e) {
						System.out.println("服务器关闭");
						return;
					}
				}
			}
		}.start();
		

		//发送线程
		new Thread(){
			//可以一直发
			public void run() {
				Scanner scanner = null;
				while(true){
					scanner = new Scanner(System.in);
					try {
						OutputStream  ops = client.getOutputStream();
						int len = 0;
						byte[] bs = new byte[1024];
						ops.write(scanner.next().getBytes());
					} catch (Exception e) {
						System.out.println("服务器关闭");
						return;
					}
				}
			}
		}.start();
		
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值