一、简述:
在上一篇文章提到了网络编程的基础知识,今天将继续为大家介绍网络编程的基础知识,并提供相应的Demo。
二、TCP协议与UDP协议:
通过上一篇文章大家可以知道网络层主要协议是IP协议。而今天主要讲的是TCP和UDP协议以及简单的Demo。
TCP(Transfer Control Protocol)
一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport Layer)通信协议。
特点:
面向连接
点到点的通信
高可靠性
占用系统资源多、效率低
生活案例:
打电话
应用案例:
HTTP、FTP、TELNET、SMTP 等
UDP(User Datagram Protocol)
一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务
特点:
非面向连接,传输不可靠,可能丢失
发送方不管接收方是否准备好,是否收到也不确认
可以广播发送
非常简单的协议,开销小
生活案例:
电视与电视塔的关系
应用案例:
DNS、SNMP
三、常用的知识
InetAddress类:
封装计算机的IP地址,没有端口
//使用getLocalHost方法创建InetAddress对象
InetAddress addr = InetAddress.getLocalHost();
System.out.println(addr.getHostAddress()); //返回:192.168.1.110
System.out.println(addr.getHostName()); //输出计算机名
//根据域名得到InetAddress对象
addr = InetAddress.getByName(“www.163.com”);
System.out.println(addr.getHostAddress()); //返回 163服务器的ip:61.135.253.15
System.out.println(addr.getHostName()); //输出:www.163.com
//根据ip得到InetAddress对象
addr = InetAddress.getByName(“61.135.253.15”);
System.out.println(addr.getHostAddress()); //返回 163服务器的ip:61.135.253.15
System.out.println(addr.getHostName()); //输出ip而不是域名。如果这个IP地 址不存在或DNS服务器不允许进行IP地址和域名的映射,getHostName方法就直接返回这个IP地址。
InetSocketAddress:
包含端口,用于socket通信的
//包含端口
InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8080);
InetSocketAddress socketAddress2 = new InetSocketAddress(“localhost”,9000);
System.out.println(socketAddress.getHostName());
System.out.println(socketAddress2.getAddress());
URL
统一资源定位符,由4部分组成:* 协议、存放资源的主机域名、端口号和资源文件名 *
URL是指向互联网“资源”的指针
资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
URL u = new URL("http://www.baidu.con:80/index.html#aa?cansu=bjsxt");
System.out.println("获取与此url关联的协议的默认端口:"+u.getDefaultPort());
System.out.println(“getFile:”+u.getFile()); //端口号后面的内容
System.out.println("主机名:"+u.getHost()); //www.google.cn
System.out.println(“路径:”+u.getPath()); //端口号后,参数前的内容
System.out.println(“端口:”+u.getPort()); //存在返回80.否则返回-1
System.out.println("协议:"+u.getProtocol());
System.out.println("参数部分:"+u.getQuery());
System.out.println("锚点:"+u.getRef());
URL u = new URL("http://www.abc.com/aa/");
URL u2 = new URL(u,“2.html”); //相对路径构建url对象
System.out.println(u2.toString()); //http://www.abc.com/aa/2.html
四、套接字Socket
我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接字来进行分离。
套接字就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据;而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心它如何传输,这属于网络其它层次的工作。
Socket实际是网络传输层供给应用层的编程接口。传输层则在网络层的基础上提供进程到进程问的逻辑通道,而应用层的进程则利用传输层向另一台主机的某一进程通信。Socket就是应用层与传输层之间的桥梁。
使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。
五、TCP编程
需求:完成网络登录功能:
用户输入用户名密码,服务器给出登录成功或失败的提示
分析
- 1 使用基于TCP协议的Socket网络编程实现
- 2 TCP协议基于请求-响应模式
- 3 在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序
- 4 第一次通讯中等待连接的程序被称作服务器端(Server)程序
- 5 利用IO流实现数据的传输
TCP通信原理(详细步骤)
- 1 服务器创建ServerSocket,在指定端口监听并并处理请求。
- 2 客户端创建Socket,向服务器发送请求。
网络登录功能分解:
- 1 单向:客户端向服务器端发送字符串,服务器获取字符串并输出
- 2 双向:服务器端给出客户端反馈,客户端得到反馈并输出
- 3 对象:客户端向服务器端发送User对象,服务器端获取对象并输出
- 4 多线程:服务器接收多个客户端的请求,并给出反馈每个客户请求开启一个线程
代码实例:
在这里我只给出功能分解的第四步的源码了,大家一个根据第四步,一步一步向前练习。
实体类:
import java.io.Serializable;
public class User implements Serializable{
private static final long serialVersionUID = 1809727215200397637L;
private String user;
private String password;
public User() {
super();
}
public User(String user, String password) {
super();
this.user = user;
this.password = password;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [user=" + user + ", password=" + password + "]";
}
}
服务端代码:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TestServer {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建端口
ServerSocket serversocket = new ServerSocket(8888);
int i = 1;
while (true) {
// 使用serversocket进行监听
Socket socket = serversocket.accept();
new LoginThread(socket).start();
System.out.print("第"+i+"个用户访问,IP地址是:" + socket.getInetAddress().getHostAddress());
i++;
}
//socket.close();
}
}
客户端代码:
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import com.fddqq.socket.Entity.User;
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
Scanner input = new Scanner(System.in);
System.out.print("请输入用户名:");
String name = input.next();
System.out.print("请输入密码:");
String password = input.next();
User user = new User(name, password);
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(user);
DataInputStream dis = new DataInputStream(socket.getInputStream());
String result = dis.readUTF();
System.out.println("客户端输出:" + result);
input.close();
dis.close();
oos.close();
socket.close();
}
}
模拟数据库验证密码:
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
import com.fddqq.socket.Entity.User;
public class LoginThread extends Thread {
private Socket socket;
public LoginThread() {
super();
}
public LoginThread(Socket socket) {
super();
this.socket = socket;
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
ObjectInputStream ois = null;
DataOutputStream dos = null;
try {
InputStream is;
is = socket.getInputStream();
ois = new ObjectInputStream(is);
User user = (User) ois.readObject();
System.out.println(",服务器端输出:" + user);
dos = new DataOutputStream(socket.getOutputStream());
if ("fddqq".equals(user.getUser()) && "123456".equals(user.getPassword())) {
String str = "登陆成功";
dos.writeUTF(str);
} else {
String str = "登陆失败";
dos.writeUTF(str);
}
} catch (IOException |
ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果
先启动服务器端代码,然后在启动客户端代码:
登陆失败:
客户端:
服务器端:
登陆成功:
客户端:
服务器端:
总结:
服务器创建ServerSocket,在指定端口监听并并处理请求;客户端创建Socket,向服务器发送请求
ServletSocket通过accept() 接收用户请求并返回对应的Socket,否则一种处于监听等待状态,线程也被阻塞
客户端发送数据需要输出流(写),客户端获取反馈数据需要输入流(读)
服务端反馈数据需要输出流(写),服务端获取请求数据需要输入流(读)
一旦使用ServerSocket和Socket建立了网络连接后,网络通信和普通IO流操作并没有太大区别
网络通信输出流建议使用DataOutputStream和ObjectOutputStream,与平台无关,输入流相应使用DataIntputStream和ObjectInputStream
如果是字符串通信也可以使用BufferedReader和PrintWriter,简单方便
六、UDP编程
需求:完成在线咨询功能:
客户和咨询师在线交流
分析
使用基于UDP协议的Socket网络编程实现
不需要利用IO流实现数据的传输
每个数据发送单元被统一封装成数据包的方式,发送方将数据包发送到网络中,数据包在网络中去寻找他的目的地。
UDP基本概念
DatagramSocket:用于发送或接收数据包
DatagramPacket:数据包
代码实例
客户端:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import com.fddqq.socket.Entity.User;
public class AskClient {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(8888);
int num = 9;
double sum = 8.8;
String str = "奋斗的蛐蛐!";
User user = new User("奋斗的蛐蛐", "fddqq");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeInt(num);
oos.writeDouble(sum);
oos.writeUTF(str);
oos.writeObject(user);
byte [] buf = baos.toByteArray();
DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), 9999) ;
ds.send(packet);
ds.close();
}
}
服务端:
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class AskServer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
DatagramSocket ds = new DatagramSocket(9999);
byte [] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
ds.receive(packet);
byte [] buf2 = packet.getData();
InputStream bais = new ByteArrayInputStream(buf2, 0, packet.getLength());
ObjectInputStream ois =new ObjectInputStream(bais);
System.out.println(ois.readInt());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
System.out.println(ois.readObject());
// System.out.println(new String(packet.getData(),0,packet.getLength())); //获取数据
// System.out.println(packet.getAddress());//获取对方IP
// System.out.println(packet.getPort()); //获取对方端口
// System.out.println(packet.getLength()); //获取数据长度
ds.close();
}
}
运行结果
先运行服务器端,在运行客户端,然后会在服务器端输出下面的场景:
七、结束语
今天就写到这里,说一千道一万,看的再多,不如自己手敲一遍。如果大家感觉有收获,不妨作作下面的习题,如果又需要下面习题的源码的可以留言给我,我会私信给大家源码连接地址。
喜欢的话可以点个喜欢与关注,感谢大家支持,也欢迎有不同意见的童鞋指教。谢谢大家!
使用基于UDP的Java Socket编程,完成在线咨询功能:
- 1 客户向咨询人员咨询。
- 2 咨询人员给出回答。
- 3 客户和咨询人员可以一直沟通,直到客户发送bye给咨询人员。