基本任务
- 掌握常用的网络命令
- 对网络通信有基本理解
- 能编写简单的网络通信服务器和客户端程序
- 能自定义通信协议,实现复杂的通信机制
- 完成一个完整的网络聊天系统
作为学习计算机网络,想要开发通信项目的人,我想你应该知道每台机器有一个IP地址,相当于人的名字方便识别,在计算机中如果机器A想和B通信时,不仅需要知道B的IP地址,还需要知道B在哪个端口等待,就好比A如果想到B家里做客,它不知道B的家在哪里,B要去路口接A,这样A只知道B的名字是不够的,需要知道B在哪接他,这里的路口就相当于端口。每台机器有0-65535个端口,每个端口可供一个应用,通常我们要避免使用“知名端口”(0-1024),他是规定给一些常用应用使用的,例如打开一个网页,连接的是80端口,DNS(域名解析)使用的是53端口。
一. 常用的网络命令
1.现在我们利用命令ping netjava.cn查看网络是否通畅(ping命令使用ICMP报文工作在TCP层,它只能查询网络是否顺畅并不能证明主机是否开放某个端口)
我们可以看到网络连接通畅,测试的主机连接了netjava.cn这个网络,开放默认端口为80,当然我们也可以查询其他,如:
2.利用telnet+ip地址+端口号查看端口是否连接。按下回车,如果出现如下窗口则表示连接成功,这时,输入字符观察现象
我们可以看到,服务器与web使用的是HTTP协议,我们输入的字符不满足协议要求无法解析,所以失去了与主机的连接
3.利用命令netstat命令查看测试主机与哪些网络通信
从上图可以看到测试主机与其他服务器建立了许多的TCP和UDP连接
二、简单的服务器创建
首先来了解一下基本的网络通信:我们用打电话来举例说明。我们要实现一个电话的连接首先需要一台手机并且需要一个电话卡,我们将手机进入开机等待模式,等待别人cll你,这里手机和卡号就相当于服务器对象和设定的端口号,当电话连接时,我们会接到对方的语音流,自己也会说话回应,这里的语音流就是输入输出流
所以实现一个简单的服务器将客户机发来的字符串显示出来并送回到客户机分为如下几步:
1.在指定端口创建服务器对象Socket
2.让服务器进入等待状态,等待客户机的连接
3,从Socket对象调用方法获取输入输出流
package com.lm.helloServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HelloServer {
public void setUpServer(int port) {
try {
// 1.在指定端口创建服务器对象
ServerSocket server = new ServerSocket(port);
System.out.println("服务器创建成功!"+port);
// 2.等待客户机连接
Socket client = server.accept();
// 3.从连接对象获取输入输出流
OutputStream out = client.getOutputStream();
InputStream in = client.getInputStream();
String s = "欢迎欢迎";
byte[] data = s.getBytes();
out.write(data);//用输出对象发送数据
out.flush();//强制输出
client.close();//关闭连接
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
HelloServer hs = new HelloServer();
hs.setUpServer(9090);
}
}
运行:
查看主机:
在这个程序中,我们只是创建了服务器,利用的是telnet命令使用的客户机,可以看出客户端连接了我们创建的服务器并收到了消息。不论我们发送的是文本还是图片或是字符串,在传输过程中都是以字节的方式进行的,一个字节是由一组二进制组成的。
如何让服务器读取客户机发送的信息,显然是可以的。我们让服务器循环接收读取客户机信息直到收到bye接收通信。
/**
* 单线程服务器通信
*/
package com.lm.helloServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HelloServer {
public void setUpServer(int port) {
try {
// 1.在指定端口创建服务器对象
ServerSocket server = new ServerSocket(port);
System.out.println("服务器创建成功!"+port);
// 2.等待客户机连接
Socket client = server.accept();
System.out.println("client's socketAddress"+client.getRemoteSocketAddress());
processChat(client);//"调用处理连接对象的方法处理连接对象"
// 3.从连接对象获取输入输出流
OutputStream out = client.getOutputStream();
InputStream in = client.getInputStream();
String s = "欢迎欢迎";
byte[] data = s.getBytes();
out.write(data);//用输出对象发送数据
out.flush();//强制输出
client.close();//关闭连接
} catch (IOException e) {
e.printStackTrace();
}
}
public void processChat(Socket client) {
try {
OutputStream out = client.getOutputStream();
InputStream in = client.getInputStream();
String s = "欢迎来到服务器!\r\n";
byte[] data = s.getBytes();
out.write(data);
out.flush();
String inputS = readString(in);
while(!inputS.equals("bye")) {
System.out.println("客户机说:"+inputS);
s="服务器收到:"+inputS +"\r\n";
data = s.getBytes();
out.write(data);
out.flush();
inputS = readString(in);//读取客户机下一次输入
}
s = "欢迎下次连接!\r\n";
data = s.getBytes();
out.write(data);
out.flush();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public String readString(InputStream in) {
StringBuffer sb = new StringBuffer();
char c = 0;
while(c!=13) {
int i;
try {
i = in.read();//读取客户机发来的字节
c=(char)i;//将输入的字节转化为Char
sb.append(c);
} catch (IOException e) {
e.printStackTrace();
}
}
String inputS = sb.toString().trim();
return inputS;
}
public static void main(String[] args) {
HelloServer hs = new HelloServer();
hs.setUpServer(9090);
}
}
当我们打另一个客户机的时候发现,服务器只能与一个客户机通信,如何改善???这就需要使用操作系统里的多线程了!
三、多线程服务器的创建
单线程服务器通信图:
多线程服务器通信图:
如果还有对多线程不太了解的朋友可以去查阅一下相关资料,这里就不多做介绍了!
我们每与一个客户机连接就创建一个线程来处理,所以要将处理客户端连接对象封装成线程类
详细代码参考:Chats.rar文件(https://github.com/zhenglimei/chatServer)