第二节 TCP编程
2.1 URL统一资源定位符
URL(Uniform Resource Locator)
统一资源定位符,由4部分组成:协议 、存放资源的主机域名****、端口号和资源文件名。
URL是指向互联网“资源”的指针。
资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
【示例3】URL类的使用
public class TestURL {
public static void main(String[] args) throws IOException {
//URL url = new
//URL("https://www.bjsxt.com:90/news/11068.html?name=zhsan&pwd=lisi");
URL url = new URL("https://www.bjsxt.com:90/news/11068.html#cloths");
System.out.println(url.getProtocol());
System.out.println(url.getHost());
System.out.println(url.getPort());
System.out.println(url.getDefaultPort());
System.out.println(url.getPath());
System.out.println(url.getQuery());
System.out.println(url.getRef());
System.out.println(url.getAuthority());
System.out.println(url.getContent());
System.out.println(url.getFile());
}
}
2.2 Socket套接字
套接字Socket
我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接字来进行分离。
套接字就像传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或接收远程发来数据;而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心它如何传输,这属于网络其它层次的工作。
Socket实际是传输层供给应用层的编程接口。传输层则在网络层的基础上提供进程到进程问的逻辑通道,而应用层的进程则利用传输层向另一台主机的某一进程通信。Socket就是应用层与传输层之间的桥梁
使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。
生活案例1:
如果你想写封邮件发给远方的朋友,如何写信、将信打包,属于应用层。信怎么写,怎么打包完全由我们做主;
而当我们将信投入邮筒时,邮筒的那个口就是套接字,在进入套接字之后,就是传输层、网络层等(邮局、公路交管或者航线等)其它层次的工作了。我们从来不会去关心信是如何从西安发往北京的,我们只知道写好了投入邮筒就OK了。
生活案例2:
如果你想发货给国外,你只要把货物放入集装箱,然后交给码头就可以了。发送什么货物,货物如何打包,完全有你做主。
码头就是套接字,剩下的事情就交给港口和货运公司处理就行了,具体细节我们无需了解。
2.3 TCP编程-一次单向通信
功能:实现类似QQ、微信、邮箱、商城的网络登录功能,可以多个用户同时登录。为了便于理解,进行功能分解和迭代,分为一次单向通信、一次双向通信、传输对象、引入多线程来实现。
【示例4】网络登录-一次单向通信
public class LoginServer {
public static void main(String[] args) throws IOException {
//1.创建一个ServerSocket,负责监听客户端请求
ServerSocket serverSocket = new ServerSocket(8800);
//2.使用ServerSocket进行监听
Socket socket = serverSocket.accept();//请求未到,在此阻塞
//3.处理用户的请求
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
String result = dis.readUTF();
System.out.println("这里是服务器端:"+result);
//4.关闭资源
dis.close();
// is.close();
// socket.close(); serverSocket.close();
}
}
public class LoginClient {
public static void main(String[] args) throws IOException {
//1.创建一个Socket,指明服务器端的IP地址和端口号
Socket socket = new
Socket(InetAddress.getByName("192.168.153.253"),8800);
//2.向服务器端发起一个新的请求
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("username=bjsxt&password=bjsxt");
//3.关闭资源
dos.close();
// os.close();
// socket.close();
}
}
2.4 TCP编程-一次双向通信
【示例5】网络登录-一次双向通信
public class LoginServer {
public static void main(String[] args) throws IOException {
//1.创建一个ServerSocket,负责监听客户端请求
ServerSocket serverSocket = new ServerSocket(8800);
//2.使用ServerSocket进行监听
Socket socket = serverSocket.accept();//请求未到,在此阻塞
//3.处理用户的请求
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
String result = dis.readUTF();
System.out.println("这里是服务器端:"+result);
//4.给出客户端响应
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("您好,登录成功,欢迎您");
//5.关闭资源
dis.close();
dos.close();
}
}
public class LoginClient {
public static void main(String[] args) throws IOException {
//1.创建一个Socket,指明服务器端的IP地址和端口号
Socket socket = new
Socket(InetAddress.getByName("192.168.153.253"),8800);
//2.向服务器端发起一个新的请求
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("username=bjsxt&password=bjsxt");
//3.接收来自服务器端的响应
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
String result = dis.readUTF();
System.out.println("这里是客户端:"+result);
//3.关闭资源
dis.close();
dos.close();
}
}
第三节 TCP编程
3.1TCP编程-传输对象
在一次双向通信基础上进步优化,在客户端输入用户名和密码,并封装到User对象中。如何在TCP编程中传输对象呢?
【示例6】网络登录-传输对象
public class User implements Serializable {
private String userId;
private String password;
}
public class LoginServer {
public static void main(String[] args)
throws IOException, ClassNotFoundException {
//1.创建一个ServerSocket,负责监听客户端请求
ServerSocket serverSocket = new ServerSocket(8800);
//2.使用ServerSocket进行监听
Socket socket = serverSocket.accept();//请求未到,在此阻塞
//3.处理用户的请求
InputStream is = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
User user = (User)ois.readObject();
System.out.println("这里是服务器端:"+user);
//4.给出客户端响应
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
if(user.getUserId().indexOf("sxt")>=0){
dos.writeUTF("您好,登录成功,欢迎您");
}else{
dos.writeUTF("您好,登录失败,请重新输入");
}
//4.关闭资源
ois.close();
dos.close();
}
}
public class LoginClient {
public static void main(String[] args) throws IOException {
//1.创建一个Socket,指明服务器端的IP地址和端口号
Socket socket =
new Socket(InetAddress.getByName("192.168.153.253"),8800);
//从键盘输入用户名和密码,封装到User对象中
Scanner input = new Scanner(System.in);
System.out.println("请输入用户名:");
String userId = input.next();
System.out.println("请输入密码:");
String password = input.next();
User user = new User(userId,password);
//2.向服务器端发起一个新的请求
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(user);
//3.接收来自服务器端的响应
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
String result = dis.readUTF();
System.out.println("这里是客户端:"+result);
//3.关闭资源
dis.close();
oos.close();
}
}
使用TCP实现网络登录功能总结
服务器创建ServerSocket,在指定端口监听并并处理请求;
ServletSocket通过accept() 接收用户请求并返回对应的Socket,否则一种处于监听等待状态,线程也被阻塞
客户端创建Socket,需要指定服务器的ip和端口号,向服务器发送和接收响应
客户端发送数据需输出流(写),客户端获取反馈数据需输入流(读)
服务端反馈数据需输出流(写),服务端获取请求数据需输入流(读)
一旦使用ServerSocket和Socket建立了网络连接后,网络通信和普通IO流操作并没有太大区别
网络通信输出流建议使用DataOutputStream和ObjectOutputStream,与平台无关,输入流相应使用DataIntputStream和ObjectInputStream
如果是字符串通信也可以使用BufferedReader和PrintWriter,简单方便
3.2 TCP编程-引入多线程
实现应用中,服务器端一周处于运行状态,并且会出现多个用户同时登录的情况,需要服务器端进行处理。
引入多线程,服务器端收到请求后,开辟一个新的线程,实现对用户请求的处理。
【示例7】网络登录-引入多线程
public class LoginThread extends Thread {
private Socket socket;
public LoginThread(Socket socket) {
this.socket = socket;
}
public void run() {
ObjectInputStream ois = null;
DataOutputStream dos = null;
try{
ois = new ObjectInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
//3.处理用户的请求
User user = (User)ois.readObject();
//4.给出客户端响应
if(user.getUserId().indexOf("sxt")>=0){
dos.writeUTF("您好,登录成功,欢迎您");
}else{
dos.writeUTF("您好,登录失败,请重新输入");
}
}catch(IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if(ois != null){
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(dos != null){
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class LoginServer {
public static void main(String[] args)
throws IOException, ClassNotFoundException {
//1.创建一个ServerSocket,负责监听客户端请求
ServerSocket serverSocket = new ServerSocket(8800);
int i = 0;
while(true){
//2.使用ServerSocket进行监听
Socket socket = serverSocket.accept();//请求未到,在此阻塞
//3.创建一个新的线程来处理请求
Thread thread = new LoginThread(socket);
thread.start();
//4.输出请求的次数,客户端的ip地址
String ip = socket.getInetAddress().getHostAddress();
System.out.println("这是第"+(++i)+"次访问,对方的IP地址是:"+ip);
}
}
}