文章目录
前提紧要
使用流需要抛出异常,IEDA按Alt + Enter
自动补全抛出异常,也就是在方法处添加throws IOException
socket通信的基础模式
- 指定服务端与端口
- 创建socket并连接服务端
- 创建流,通过流写入/获取数据
- 关闭流,关闭socket
客户端
1.定义服务器信息(ip&port)
ip用字符串存,端口用整数存,端口最好写大点(5000以上)
String server_ip = "127.0.0.1";
int server_port = 8557;
2.创建socket并连接服务端
创建socket时可以使用构造函数指定服务端ip和端口,如果不指定ip则默认为本地回环
Socket socket = new Socket(server_ip,server_port);
3.连接成功后创建输出流
因为程序是客户端,需要输出数据到流中,所以用的是输出流。
这里可大有文章,首先是要看发送的是什么类型的数据,如果是字符,就用字符流,其他文件就用字节流。
输出流也可以使用Buffered类装饰,提高读写效率。
os.write()
将数据写入到流中,数据的类型是byte
String message = "to Server: Im client!";//发送的数据
OutputStream os = socket.getOutputStream();//输出流
os.write(message.getBytes(StandardCharsets.UTF_8));//将数据转换成byte类型
将os.write(message.getBytes(StandardCharsets.UTF_8))
拆开分析
- .getBytes():将数据按指定字符集转换成byte--既然你写入要的是byte那我就转换一下咯
- StandardCharsets.UTF_8:就是表示UTF-8字符集的意思
注意客户端和服务端的字符集要统一,如果写入用的utf-8那读取也应该用utf-8
4.各种关闭
os.close();//关闭输出流
socket.close();//关闭客户端socket
服务端
1.创建监听端口
就是指定要监听哪个端口
int port = 8557;
2.创建ServerSocket并监听
ServerSocket:因为服务端socket需要的功能和客户端有些出入,比如阻塞监听等,ServerSocket就是用来提供这些服务端所需功能的类,ServerSocket都是配合Socket使用的
ServerSocket server = new ServerSocket(port);//服务端监听的端口
3.监听连接
accept()
阻塞端口,直到有客户端向该端口发起连接,返回一个socket。由此可见,服务端一开始并没有socket,而是使用serversocket去监听连接,直到收到客户端的连接后才创建socket,进行信息传输。
Socket socket = server.accept();//阻塞并等待连接请求,accept()返回一个socket
4.创建输入流,接受并处理数据
看码说话
由于数据在流中,所以需要创建一个输入流,获取流中的数据。就像前面客户端写入流时需要进行数据处理一样,服务端从流中读取数据一样需要进行数据处理(而且更麻烦…)需要用到StringBuilder类
StringBuilder
用于将其他类型数据拼接成字符串类型。
new String(byte[],offset,length,charset)
将byte数组拼接成字符串
append()
将其他类型数据拼接成String,能大部分基本的数据类型,如String、char[],包括StringBuilder
- offset:从哪个位置开始拼接
- length:每次拼接几个byte
- charset:按照哪个字符集编码
read()
从bytes[]中读取1byte数据,当数组中没有数据时,就会返回-1
代码:
InputStream is = socket.getInputStream();//创建输入流,从客户端socket的流中获取输入
int len;
byte[] bytes = new byte[1024];//存储读取到的字节
StringBuilder sb = new StringBuilder();
while ((len = is.read(bytes)) != -1){
//拼接byte组成字符串
sb.append(new String(bytes,0,len, StandardCharsets.UTF_8));//bytes--字节数组;offset--组装开始位置;length--每次拼接的byte个数,最后一个是字符集
}
这个循环条件的意思是向byte[]中读取数据,而当里面没有数据可以读的时候会返回-1,结束循环(貌似流中的数据读一个就少一个???)
5.各种关闭
is.close();
server.close();
socket.close();
完整代码
服务端
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class Server {
//socket服务端
public static void main(String[] args) throws IOException {
int port = 8557;
/*
* ServerSocket何许人也?ServerSocket是专门为服务端所用的Socket类,有很多服务端用的函数,比如.accept()用于阻塞监听等
* */
ServerSocket server = new ServerSocket(port);//服务端监听的端口
//服务端开始监听,等待客户端发起连接请求
System.out.println("waiting for connection....");
//实际上通讯还是靠Socket类来完成,ServerSocket提供了很多方便服务端去做的事而已,现在这个阻塞就是
Socket socket = server.accept();//阻塞并等待连接请求,accept()返回一个socket
//建立连接后
System.out.println("连接成功!来自...");
InputStream is = socket.getInputStream();//创建输入流,从客户端socket的流中获取输入
//is.read()--从流(is)中获取数据下一byte数据,当byte读取完毕后,返回-1。如果有byte数组作为参数,则将读取到的数据存储到byte中
int len;
byte[] bytes = new byte[1024];//存储读取到的字节
StringBuilder sb = new StringBuilder();
//len = is .read(bytes)这个条件写的可真牛逼啊,如果读取到byte了,会存入到bytes中,如果读完了,就把-1赋给len,循环结束
while ((len = is.read(bytes)) != -1){
//拼接byte组成字符串
sb.append(new String(bytes,0,len, StandardCharsets.UTF_8));//bytes--字节数组;offset--组装开始位置;length--每次拼接的byte个数,最后一个是字符集
}
System.out.println("来自客户端的消息:"+sb);
is.close();
server.close();
socket.close();
}
}
客户端
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) throws IOException {
//发起连接,Socket类有不同参数的构造函数,只指定端口时,默认服务器ip为本地回环
String server_ip = "127.0.0.1";
int server_port = 8557;
System.out.println("正在向服务端发起连接...");
Socket socket = new Socket(server_ip,server_port);
//连接成功后
System.out.println("连接成功,服务端...");
//创建输出流
OutputStream os = socket.getOutputStream();//程序对外的输出流,通过socket获取就是对server的输出流,可以使用Bufferd提高效率
String message = "to Server: Im client!";//发往server的数据,这里可大有文章,发什么东西就用什么流,字符用字符流,图片啥的用字节流
//将消息写入输出流里(啥叫水流一般传输数据)
//getBytes(String charsetName)--将变量按指定字符集编码为bytes序列,并将结果返回到一个新的byte数组中在,可以看到os.write这个方法操作的正是byte数组
os.write(message.getBytes(StandardCharsets.UTF_8));
os.close();//关闭输出流
socket.close();//关闭客户端socket
//这时,数据就顺着socket的输出流流到了服务端
}
}
Summary
怪只怪自己Java没学多深,又要花时间去学一下IO流,前后花了整整两天才弄明白这个java的socket通信的基础模式。感觉就是数据的处理上有些麻烦、晦涩。其实大一的时候就用python写过socket,不过那时候是照猫画虎,不求甚解。在其他的语言中socket的用法应该都是大同小异的吧…对了,这些代码并不是我自己原创的,也是跟着网上的帖子写的只是在边写边学的过程中加了一些自己的见解,希望我发博客的时候记得注明出处。
出处:Java Socket编程基础,原文写的很好,还有优化模式
刚开始写的时候不知道从哪里开始,因为这个东西嘛,是两个端的交互,有点拎不清,后面有时间要去学一下优化。