本篇只讲述和TCP有关的概念,没有UDP网络编程内容
Socket
首先了解一个概念,什么是socket。
简单来说Socket是两台计算机之间通信的端点 , 是网络驱动提供给应用程序编程的一种接口,一套标准, 一种机制。 它用于定位ip和传输信息。
Server
Server端常用的方法是ServerSocket,用于创建服务器。创建完毕后, 会绑定一个端口号, 然后此服务器可以等待客户端连接。每连接一个客户端 , 服务器就会得到一个对应于该客户端的新的Socket对象, 只用于跟该客户端进行通信。
常用构造方法
ServerSocket(int port);
创建一个基于TCP/IP协议的服务器 , 并绑定指定的端口号。
注意: 参数port的范围是: 0-65535 (建议1025-65535)
常用方法
Socket accept();
等待客户端连接。此方法会导致线程的阻塞!
直到一个新的客户端连接成功, return Socket对象后, 线程才会继续执行。
void close();
释放占用的端口号 , 关闭服务器.
- OutputStream getOutputStream();
返回的是 , 指向通信的另一端点的输出流
- InputStream getInputStream();
返回的是 , 指向通信的另一端点的输入流
-BufferedReader();
-InputStreamReader();
把字节流转换成字符流,可以参考之前那篇IO
Client
常用构造方法
Socket(String ip,int port);
创建一个套接字, 并连接指定ip和端口号的 服务器.
参数1. 服务器的ip地址 参数2. 服务器软件的端口号
常用方法
OutputStream getOutputStream(); 返回的是 , 指向通信的另一端点的输出流
InputStream getInputStream(); 返回的是 , 指向通信的另一端点的输入流
void close(); 关闭套接字
注意:在网络编程时, 获取输入输出流的操作 ,对于客户端与服务器来说是相对的
客户端的输入流, 输入的是服务器的输出流输出的内容.
客户端的输出流, 输出到了服务器的输入流中.
所以 在使用时, 需要注意以下一点规则: 客户端与服务器获取流的顺序必须是相反的
例如:客户端先得到了输入流 , 那服务器必须先获取输出流。
这是因为如果不相反两边都在等待就会进入死锁。 #面试题:如何写死锁
简单的两边输入输出的Demo
客户端:
public static void main(String[] args) throws Exception {
//1. 连接服务器
Socket socket = new Socket("192.168.102.228";,8888);
//2. 得到输出流
//2.1 得到输出流
OutputStream os = socket.getOutputStream();
//2.2 将输出流, 转换为打印流
//打印流会将这个os输出到服务器端
PrintStream ps = new PrintStream(os);
//3. 得到输入流
//3.1 得到输入流
InputStream is = socket.getInputStream();
//3.2 将字节输入流, 转换为字符输入流 , 并转换为逐行读取流
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
//4. 循环接收用户输入
Scanner input = new Scanner(System.in);
while(true) {
System.out.println("请输入要发送给服务器的内容:");
String text1 = input.nextLine();
//5. 将用户输入的内容, 发送给服务器
ps.println(text1);
//6. 接收服务器回复的消息
String text2 = br.readLine();
System.out.println(text2);
if("886".equals(text1)) { break; }
}
//7.关闭
socket.close();
}
服务器:
public static void main(String[] args) throws Exception {
//1. 启动服务器, 并侦听8888端口号
ServerSocket server = new ServerSocket(8888);
//2. 打印提示
System.out.println("服务器已启动 , 等待客户端连接中...");
//3. 等待客户端连接
Socket socket = server.accept();
System.out.println("一个客户端连接成功:" + socket.getInetAddress().toString());
//4. 获取输入流
//4.1 获取输入流
InputStream is = socket.getInputStream();
//4.2 将输入的字节流 ,转换为字符流
InputStreamReader isr = new InputStreamReader(is);
//4.3 将字符流, 转换为逐行读取流
BufferedReader br = new BufferedReader(isr);
//5. 获取输出流
//5.1 获取输出流
OutputStream os = socket.getOutputStream();
//5.2 将字节输出流, 转换为打印流
//打印流会将os输出到客户端,而不是打印出来的意思
PrintStream ps = new PrintStream(os);
while(true) {
//6. 循环读取一行行的数据 ,读取操作会导致线程的阻塞, 直到客户端真的发送了数据,
//服务器才能接到, 顺序继续执行下面的代码
String text = br.readLine();
//7. 将这个文字, 再打印给客户端
ps.println("服务器:"+text);
if("886".equals(text)) { break; }
}
//8.关闭
server.close();
}
简单多线程服务器
让每一个进来的客户端都分配一个线程跑,加大效率
多线程服务器:
public static void main(String[] args) throws Exception {
//1. 启动服务器, 并侦听8888端口号
ServerSocket server = new ServerSocket(8888);
//2. 打印提示
System.out.println("服务器已启动 , 等待客户端连接中...");
//3.一直监听是否有新客户端加入
while(true){
//4. 等待客户端连接
Socket socket = server.accept();
//5.创建新线程执行,直接用new Thread(){}.start()
//因为只针对单客户端运行,这样写简洁很多
new Thread(){
@override
public void run(){
//6.获取输入输出流
//这里注意!如果服务器先输入再输出
//客户端要先输出再输入!不然会死锁!
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
}
}.start();
}
//跳出循环和close懒得写了
}
InetAddress
这个类的对象, 用于描述IP,根据字符串可以得到InetAddress对象
InetAddress ip = InetAddress.getByName(“192.168.102.228”);
在UDP协议中. 通过数据包DatagramPacket的getAddress方法, 可以得到数据包来自哪个ip
在TCP协议中, 通过套接字Socket的getInetAddress方法, 可以得到套接字连接的ip地址.
- 常用方法:
1.String getHostAddress() ip地址字符串
2.String getHostName() 计算机名称, 当名称无法获取时, 获取的为ip地址.