文章目录
- 一、 网络编程
- 1.1 Socket(套接字)
- 1.2 网络通信的3要素
- 1.3 端口号
- 1.4 协议
- UDP协议:
- TCP协议:
- 二、InetAddress
- 三、UDP
- 3.1 发送步骤
- 3.2 方法
- 3.3 UDP协议手法数据的注意事项
- 四、TCP
- 4.1 发送步骤
- 4.2 方法
- 4.3 接收步骤
- 4.4 方法
- 4.5 案例一
- 4.6 案例二
一、 网络编程
1.1 Socket(套接字)
-
Socket
套接字:用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供了集中服务。每种服务都打开一个Socket
,并绑定到一个端口上,不同的端口对应于不同的服务。 -
Socket
就是为网络编程提供的一种机制;通信的两端都有Socket
;网络通信其实就是Socket
间的通信,数据在2个Socket
间通过IO传输。
1.2 网络通信的3要素
- IP地址:网络中的设备标识,不易记忆,可用主机名。
- 端口号:用于标识进程的逻辑地址,不同进程有不同的标识。
- 传输协议:通讯的规则,常见协议:TCP,UDP。
1.3 端口号
- 物理端口:网卡口。
- 逻辑端口:每个网络程序都至少有一个逻辑端口,用于标识程序的逻辑地址,不同进程的标识。有效端口:0~65535,其中0 ~1024系统使用或保留端口(可用360)查看。
1.4 协议
UDP协议:
- 将数据源和目的地封装在数据包中,不需要建立连接。
- 每个数据包的大小限制在64k。
- 因无连接,是不可靠的协议。
- 不需要建立连接,速度更快。
TCP协议:
- 建立连接,形成传输数据的通道。
- 在连接中进行大数量的传输。
- 通过3次握手完成连接,是可靠的连接。(1. 客户端问:你还活着吗?2. 服务器回:我还活着!3. 连接)
- 必须建立连接,效率会稍低。
二、InetAddress
此类表示互联网协议(IP)地址。
-
static InetAddress getByName(String host)
:在给定主机名的情况下确定主机的IP地址。
代码示例
InetAddress address = InetAddress.getByName("小男孩的PC"); //小男孩的PC/169.234.37.34
/*InetAddress address = InetAddress.getByName("169.254.37.34");*/ //169.234.37.34
System.out.print(address);
String hostAddress = address.getHostAddress(); //返回IP地址
String hostName = address.getHostName(); //返回主机名
System.out.println(hostAddress);
System.out.println(hostName);
三、UDP
3.1 发送步骤
- 创建发送端
Socket
对象 - 创建数据并打包
- 发送数据
- 释放资源
3.2 方法
-
DatagramSocket
:此类表示用来发送和接收数据,基于UDP协议。 -
DatagramSocket()
:创建Socket
对象,并随机分配端口号。 -
DatagramSocket(int port)
:创建Socket
对象,并指定端口号。 -
DatagramPacket
:表示数据包。 -
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
:对数据进行打包,贴上物流单,单上信息有数据,长度,地址,端口。
发送端代码示例
1)创建发送端Socket对象
DatagramSocket ds = new DatagramSocket();
2)创建数据并打包
String s = "hello, UDP, im coming";
byte[] bys = s.getBytes();
int length = bys.length;
InetAddress address = InetAddress.getByName("小男孩的PC");
int port = 8888;
3)打包
DatagramPacket dp = new DatagramPacket(bys, length, address, port);
4)发送数据
ds.send(dp);
5)释放资源
ds.close();
接收端代码示例
1)创建接收端Socket对象
DatagramServer ds = new DatagramServer(8888); //必须与发送的端口一致
2)创建包对象(容器)
byte[] bys = new byte[1024]; //容器
DatagramPacket dp = new DataPacket(bys, bys.length);
ds.receive(dp); //阻塞
3)解析数据
InetAddress address = dp.getAddress();
byte[] data = dp.getData();
int length = dp.getLength();
4)输出数据
System.out.println("sender --->" + address.getHostAddress());
System.out.println(data, 0, length);
5)释放资源
ds.close();
3.3 UDP协议手法数据的注意事项
- 端口号错误,数据可以正常发出,不会出现异常,但是收不到数据。
-
BindException
:端口已经被占用了。
四、TCP
4.1 发送步骤
- 创建发送端
Socket
对象(创建连接)。 - 获取输出流对象。
- 发送数据。
- 释放资源
4.2 方法
-
Socket(InetAddress address, int port)
:创建一个套接字,并将其连接到指定IP地址的指定端口号。
发送端代码示例
1)创建发送端Socket对象(创建连接)
Socket s = new Socket(InetAddress.getByName("小男孩的PC"), 10086);
2)获得输出流对象
OutputStream os = s.getOutputStream();
3)发送数据
String str = "hello TSP, im coming";
os.write(str.getBytes());
4)释放资源
os.close();
4.3 接收步骤
- 创建接收端
Socket
对象 - 监听(阻塞)
- 获取输入流对象
- 获取数据
- 输出数据
- 释放资源
4.4 方法
-
ServerSocket
:接收端,服务端Socket
,没有读写数据的方法,需要通过获取Socket
来进行读写,监听就是获取Socket
对象来进行读写。 -
ServerSocket(int port)
:创建绑定到特定端口的服务器套接字。
接收端代码示例
1)创建服务端Socket对象
ServerSocket ss = new ServerSocket(10086);
2)监听(阻塞)
Socket s = ss.accept();
3)获取输入流对象
InputStream is = s.getInputStream();
4)获取数据
byte[] bys = new byte[1024];
int len;
len = is.read(bys);
InetAddress address = s.getInetAddress();
5)输出数据
System.out.println(address);
System.out.println(bys, 0, len);
6)释放资源
s.close();
//ss.close();//服务端可不关闭
4.5 案例一
需求:使用TCP协议发送数据,并将接收导的数据转换成大写返回。
客户端代码示例
//发出数据
Socket s = new Socket(InetAddress.getByName("小男孩的PC", 10010));
OutputStream os = s.getOutputStream();
os.write("tcpdemo".getBytes());
----------------------
//接收数据
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len;
len = is.read(bys);
System.out.println(new String(bys, 0, len));
s.close();
接收端代码示例
//接收数据
ServerSocket ss = new ServerSocket(10010);
Socket s = ss.accept();
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len;
len = is.read(bys);
String str = new String(bys, 0, len);
String upperStr = str.toUpperCase();
--------------------
//发送数据
OutputStream os = s.getOutputStream();
os.write(upperStr.getBytes());
s.close();
4.6 案例二
需求:模拟用户登陆
用户类
public class User{
private String username;
private int age;
public User(String username, int age){
super();
this.username = username;
this.age = age;
}
//getter and setter
...
}
用户数据库类
public class User{
public static list<User> users = new ArrayList<User>();
static{
users.add(new User("张三", 18));
...
}
public static List<User> getUsers(){
return users;
}
}
客户端
//发出数据
Socket s = new Socket("小男孩的PC", 8888);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入用户名:");
String username = br.readLine();
System.out.println("请输入密码:");
String password = br.readLine();
/*BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));*/
PrintWriter out = new PrintWriter(s.getOutputStream(), true); //true表示可以自动换行
out.println(username);
out.println(password);
------------------------------
//接收数据
BufferedReader serverBr = new BufferedReader(new InputStreamWriter(s.getInputStream()));
String result = serverBr.readLine();
System.out.println(result);
s.close();
服务端
ServerSocket ss = new ServerSocket(8888);
Socket s = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String username = br.readLine();
String password = br.readLine();
boolean flag = false;
List<User> users = UserDB.getUsers();
User user = new User(username, password);
if(users.contains(user)){
flag = true;
}
PrintWriter out = new PrintWriter(s.getOutputStream());
if(flag){
out.println("登陆失败");
}else{
out.println("登陆成功");
}
s.close();