黑马程序员技术交流社区
标题: 网络编程--java实现 [打印本页]
作者: demo_jean 时间: 2017-12-2 19:59
标题: 网络编程--java实现
1. UDP协议基础编程注意:UDP只有发送端和接收端,不区分客户端和服务端. 可以计算机之间随意的发送数据.
1) 接收端
// 创建接收端套接字,接收端口号8888的数据
DatagramSocket ds = new DatagramSocket(8888);
byte[] bytes = new byte[1024];
// 打包数据
DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length);
//接收数据
ds.receive(dp); //阻塞
// 解析数据
System.out.println("data from : " + dp.getAddress()); // 数据来源的ip地址
System.out.println(new String(dp.getData(), 0, dp.getLength())); // 打印数据
// 释放资源
ds.close();
2) 发送端
[Java] 纯文本查看 复制代码
// 创建发送端套接字
DatagramSocket ds = new DatagramSocket();
//打包数据
byte[] bytes = "hello,udp~~".getBytes();
// 发送给当前设备 (127.0.0.1), 指定端口号8888
DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("127.0.0.1"), 8888);
// 发送数据
ds.send(dp);
// 释放资源
ds.close();
2. TCP协议基础编程注意:TCP协议与UDP不同的是:UDP只有发送端和接收端,不区分客户端和服务端. 可以计算机之间随意的发送数据. 而TCP,严格区分客户端和服务端,首先启动服务端,必须由客户端先去连接服务器,服务器予以响应才能实现通信。
1) 客户端
// 创建socket对象,指定端口号,与访问主机ip,向服务器发送请求
Socket s = new Socket(InetAddress.getByName("127.0.0.1"), 8888);
// 获取socket的字节输出流
OutputStream os = s.getOutputStream();
// 数据
String str = "hello, TCP, my friend!";
// 写出数据
os.write(str.getBytes());
// 释放资源
s.close();
2) 服务端
// 创建服务端ServerSocket对象,指定端口号,接收该端口发送的数据
ServerSocket ss = new ServerSocket(8888);
// 接收数据,获得socket对象,客户端发送请求前处于阻塞状态
Socket accept = ss.accept();
// 获得socket的输入流对象
InputStream is = accept.getInputStream();
byte[] bytes = new byte[1024]; //接收数据
int len; //数据长度
len = is.read(bytes); // 读取数据(写入到程序)
// 打印数据
System.out.println("data from: " + accept.getInetAddress().getHostAddress());
System.out.println(new String(bytes, 0, len));
// 释放资源
ss.close();
5. TCP模拟用户登录(面向对象)1) 数据封装类
public class UserData implements Serializable {
private static final long serialVersionUID = 1L;
private String name;//姓名
private String passworld;//密码
public UserData(String name, String passworld) { ...}
public UserData() { }
@Override
public int hashCode() {...}
@Override
public boolean equals(Object obj) {....}
}
注意:方法里的内容省略了,注意的是使用了对象输入输出流,所以类必须实现Serializable接口,可序列化.equals(Object obj)必须重写,因为集合中的contains()方法,底层是equal()方法实现的(我们认为用户名和密码匹配上,就是可以登陆的).
2) 用户数据存储
public class UserDB {
private static ArrayList<UserData> users = new ArrayList<>();
static { //静态代码块
users.add(new UserData("jean", "123456"));
users.add(new UserData("admin", "password"));
users.add(new UserData("anni", "qwerqwer"));
users.add(new UserData("jack", "034235"));
}
public static ArrayList<UserData> getUsers() {
return users;
}
}
注意:静态代码块是随着系统加载而加载的,虚拟机运行,只执行一次,并且优先于构造代码块和空参构造执行.随着虚拟机的终止而消失.
3) 模拟服务器
// 创建服务端套接字,指定端口号:8888
ServerSocket ss = new ServerSocket(8888);
// 监听
Socket socket = ss.accept();
// 获取套接字输出流,并用对象输入流包装
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object object = ois.readObject();
UserData use = (UserData) object;
// 打印流包装,套接字输出流
PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
// 判断信息是否存在
if(UserDB.getUsers().contains(use)) {
pw.println("登录成功!!"); //反馈信息
}else {
pw.println("登录失败"); //反馈信息
}
// 释放资源
socket.close();
//ss.close();
4) 模拟客户端
// 创建客户端套接字 指定访问主机ip,和端口号
Socket s = new Socket(InetAddress.getByName("127.0.0.1"), 8888);
// 键盘录入信息
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入用户名: ");
String name = br.readLine();
System.out.println("请输入密码: ");
String password = br.readLine();
// 包装数据
UserData use = new UserData(name, password);
// 获取客户端套接字输出流,并用对象输出流包装
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
// 发送数据
oos.writeObject(use);
// 接收反馈
BufferedReader br1 = new BufferedReader(new InputStreamReader(s.getInputStream()));
// 读取反馈
String line = br1.readLine();
//打印反馈信息
System.out.println(line);
// 释放资源
s.close();
6. 多线程实现服务器与多客户端之间通信以上从2-5的TCP服务器设计都是单个线程的,仅仅能够处理一个请求,当接收第二个请求时,服务器没有办法处理,但实际万万不能这样使用.正常的服务器,能够处理很多客户端发送的请求,实现这一点就要用到多线程方面的知识. 简单描述一下就是服务器开启处于等待请求的阻塞状态,当有客户端发送请求,创建一个新的线程来处理客户的需求,再来请求,再创建一个线程处理.我们仅仅需要对5设计修改一下.
我们依然使用设计5的客户端,UserDB和UserData类,
1) 服务器修改
public class Demo5_ThreadServer {
public static int count = 0; // 请求次数
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建服务器套接字指定端口号
ServerSocket ss = new ServerSocket(8888);
Socket socket = null;
System.out.println("--------服务器启动,等待客户端发送请求--------");
while (true) {
// 获取客户端套接字
socket = ss.accept(); // 阻塞
// 创建ThreadServer新线程,处理当前请求
new Thread(new ThreadServer(socket)).start();
}
}
}
当客户端请求时,我们创建一个新的线程来为客户需求服务,同时保证其他需求进来,一样可以处理.ThreadServer类为Runnable实现类,实现如下:
2) ThreadServer类的实现
class ThreadServer implements Runnable {
// 创建当前线程socket套接字对象
private Socket socket = null;
// 有参构造,传入socket套接字赋值给当前线程socket套接字
public ThreadServer(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
InputStreamReader isr = null;
InputStream is = null;
PrintWriter pw = null;
OutputStream os = null;
try {
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String name = br.readLine(); // 读取姓名
String password = br.readLine(); // 读取密码
// br.close();
// 封装信息
UserData use = new UserData(name, password);
// 获取scoket输出流,用打印流进行包装
os = socket.getOutputStream();
pw = new PrintWriter(os, true);
System.out.println("----第" + (++Demo5_ThreadServer.count) + "请求来源ip : "
+ socket.getInetAddress().getHostAddress() + "----");
if (UserDB.getUsers().contains(use)) {
System.out.println(name + "请求成功!");
pw.println("登录成功!");
} else {
System.out.println(name + "请求失败!");
pw.println("登录失败!");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
作者: wheat 时间: 2017-12-2 23:33
很好总结
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |