通过Socket类的getInputStream()方法获得输入流对象,并借助InputStreamReader类将输入流对象转换为BufferedReader对象读取接收到的信息,使用getOutputStream()方法获得输出流对象,并创建了PrintWriter对象发送信息。
主要代码如下:
//客户端程序
private void connect() { // 连接套接字方法
ta_info.append("尝试连接......\n"); // 文本域中信息信息
try { // 捕捉异常
socket = new Socket("192.168.1.193", 1978); // 实例化Socket对象
while (true) {
writer = new PrintWriter(socket.getOutputStream(), true);
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream())); // 实例化BufferedReader对象
ta_info.append("完成连接。\n"); // 文本域中提示信息
getClientInfo();
}
} catch (Exception e) {
e.printStackTrace(); // 输出异常信息
}
}
//获取信息
private void getClientInfo() {
try {
while (true) { // 如果套接字是连接状态
if (reader != null) {
String line = reader.readLine();
if (line != null)
ta_info.append("接收到服务器发送的信息:" + line + "\n"); // 获得客户端信息
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();// 关闭流
}
if (socket != null) {
socket.close(); // 关闭套接字
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//服务器程序
public void getserver() {
try {
server = new ServerSocket(1978); // 实例化Socket对象
ta_info.append("服务器套接字已经创建成功\n"); // 输出信息
while (true) { // 如果套接字是连接状态
ta_info.append("等待客户机的连接......\n"); // 输出信息
socket = server.accept(); // 实例化Socket对象
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream())); // 实例化BufferedReader对象
writer = new PrintWriter(socket.getOutputStream(), true);
getClientInfo(); // 调用getClientInfo()方法
}
} catch (Exception e) {
e.printStackTrace(); // 输出异常信息
}
}
//获取信息
private void getClientInfo() {
try {
while (true) { // 如果套接字是连接状态
String line = reader.readLine();
if (line != null)
ta_info.append("接收到客户机发送的信息:" + line + "\n"); // 获得客户端信息
}
} catch (Exception e) {
ta_info.append("客户端已退出。\n"); // 输出异常信息
} finally {
try {
if (reader != null) {
reader.close();// 关闭流
}
if (socket != null) {
socket.close(); // 关闭套接字
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
之所以会在传递中出现汉字乱码,是因为发送数据和接收数据所使用的编码不同,因此,要解决传递汉字乱码问题,只需要在创建输入流和输出流对象时,使用相同的编码,在使用OutputStreamWriter类和InputStreamReader类创建对象时,在构造方法中指定相同的编码。
主要代码如下:
reader=new BufferedReader(new InputStreamReader(socket
.getInputStream(),"UTF-8"));
out=new OutputStreamWriter(socket.getOutputStream(),"UTF-8");
注意:由于使用Socket通信是通过IO流实现的,因此为了避免传递汉字乱码,只需要记住一个原则,那就是通过什么字符集发送的数据,就使用什么字符集接收数据,这样就不会发生Socket传递汉字乱码的问题。
只有序列化的对象才能被Socket传递,在Java中实现Serializable接口的类所创建的对象就是序列化对象。
在实例314的基础上修改代码,主要代码如下:
out=new ObjectOutputStream(socket.getOutputStream());//创建输出流对象
in=new ObjectInputStream(socket.getInputStream());//创建输入流对象
//getClientInfo()方法用于接收客户端发送的信息,该方法的关键代码如下:
while(true){
Student stud=(Student)in.readObject();
if(stud!=null)
ta_info.append("接收到客户机发送的 编号为:"+stud.getId()+" 名称为:"+stud.getName()+"\n");//获得客户端信息
}
本实例通过使用DataInputStream类的read()方法,将图片文件读取到字节数组,然后使用DataOutputStream类从DataOutput类继承的write()方法输出字节数组,从而实现了使用Socket传输图片的功能。
主要代码如下:
//服务器接收客户端发送的图片的关键代码
long lengths=in.readLong();//读取图片文件的长度
byte[] bt=new byte[(int)lengths];//创建字节数组
for(int i=0;iin.readByte();//读取字节信息并存储到字节数组
}
receiveImg=new ImageIcon(bt).getImage();//创建图像对象
receiveImagePanel.repaint();//重新绘制图像
//客户端发送事件的代码如下:
DataInputStream inStream=null;//定义数据输入流对象
if(imgFile!=null){
lengths=imgFile.length();//获得选择图片的大小
inStream=new DataInputStream(new FileInputStream(imgFile));//创建输入流对象
}else{
JOptionPane.showMessageDialog(null,"还没有选择图片文件。");
return;
}
out.writeLong(lengths);//将文件的大小写入输出流
byte[] bt=new byte[(int)lengths];//创建字节数组
int len=-1;
while((len=inStream.read(bt))!=-1){//将图片文件读取到字节数组
out.write(bt);//将字节数组写入输出流
}
同实例316一样,使用DataInputStream类的read()方法和DataOutputStream类从DataOutput类继承的write()方法实现了对音频文件的读写操作。
使用保存对话框,将接收到的音频文件保存到接收方的主机上。
主要代码入下:
//接受客户端发送的音频文件
String name=in.readUTF();//读取文件名
long lengths=in.readLong();//读取文件的长度
byte[] bt=new byte[(int)lengths];//创建字节数组
for(int i=0;iin.readByte();//读取字节信息并存储到字节数组
}
FileDialog dialog=new FileDialog(ServerSocketFrame.this,"保存");//创建对话框
dialog.setMode(FileDialog.SAVE);//设置对话框为保存对话框
dialog.setFile(name);//设置保存对话框显示的文件名
dialog.setVisible(true);//显示保存对话框
String newFileName=dialog.getFile();//获得文件的保存路径
String newFileName=dialog.getFile();//获得保存的文件名
if(path==null||newFileName==null){
return;
}
String pathAndName=path+"\\"+newFileName;//文件的完整路径
FileOutputStream fOut=new FIleOutputStream(pathAndName);//创建输出流对象
fOut.write(bt);//向输出流写信息
fOut.flush();//更新缓冲区
fOut.close();关闭输出流对象
ta_info.append("文件接收完毕。\n");
//客户端中发送事件代码
DataInputStream inStream=null;//定义数据输入流对象
if(file!=null){
lengths=file.length();//获得所选择音频文件的大小
inStream=new DataInputStream(new FileInputStream(file));//创建输入流对象
}else{
JOptionPane.showMessageDialog(null,"还没有选择音频文件。");
return;
}
out.writeUTF(fileName);//写入音频文件名
out.writeLong(lengths);//将文件的大小写入输出流
byte[]bt=new byte[(int)lengths];//创建字节数组
int len=-1;//用于存储读取到的字节数
while(len=inStream.read(bt)!=-1){//将音频文件读取到字节数组
out.write(bt);
}
out.flush();//更新输出流对象
out.close();//关闭输出流对象
ta_info.append("文件发送完毕。\n");
本实例与实例317基本相同,只是在文件选择对话框中指定的用于发送的文件类型不同而已。
主要代码如下:
//为文件选择对话框指定音频类型的文件,代码如下:
JFileChooser fileChoose=new JFileChooser();//创建文件选择器
FileFilter filter=new FileNameExtensionFilter("音频文件(WAV/MIDI/MP3/AU)","WAV","MID","MP3","AU");//创建音频过滤器
fileChooser.setFileFilter(filter);//设置过滤器
int flag=flag=fileChooser.showOpenDialog(null);//显示打开对话框
服务器端程序:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.Font;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ServerSocketFrame extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private JTextArea textArea;
private ServerSocket server;
private Socket socket;//该类实现客户端套接字(也称为“套接字”)。 套接字是两台机器之间通讯的端点。
private BufferedReader reader;
private PrintWriter writer;
private JTextField textField;
/**
* 主程序
*/
public static void main(String[] args) {
ServerSocketFrame frame = new ServerSocketFrame();
frame.setVisible(true);
frame.getServer();
}
/**
* 创建窗体
*/
public ServerSocketFrame() {
super();
setTitle("服务器端程序");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 379, 260);
final JScrollPane scrollPane = new JScrollPane();//添加滚动窗体
getContentPane().add(scrollPane, BorderLayout.CENTER);
textArea= new JTextArea();
scrollPane.setViewportView(textArea);
final JPanel panel = new JPanel();
getContentPane().add(panel, BorderLayout.SOUTH);
final JLabel label = new JLabel();
label.setText("服务器发送的信息:");
panel.add(label);
textField = new JTextField();
textField.setPreferredSize(new Dimension(150, 25));
panel.add(textField);
final JButton button = new JButton();
button.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
writer.println(textField.getText()); // 将文本框中信息写入流
textArea.append("服务器发送的信息是:" + textField.getText() + "\n"); // 将文本框中信息显示在文本域中
textField.setText(""); // 将文本框清空
}
});
button.setText("发 送");
panel.add(button);
final JPanel panel_1 = new JPanel();
getContentPane().add(panel_1, BorderLayout.NORTH);
final JLabel label_1 = new JLabel();
label_1.setForeground(new Color(0, 0, 255));
label_1.setFont(new Font("", Font.BOLD, 22));
label_1.setText("一对一通信——服务器端程序");
panel_1.add(label_1);
}
/**
* 创建服务器端套接字
监听客户端程序
创建向客户端发送信息的输出流对象和用于接收客户端发送信息的输入流对象
*
*/
public void getServer(){
try {
//ServerSocket()创建未绑定的服务器套接字。
server = new ServerSocket(1978); // 实例化Socket对象
textArea.append("服务器套接字已经创建成功\n"); // 输出信息
while (true) { // 如果套接字是连接状态
textArea.append("等待客户机的连接......\n"); // 输出信息
socket = server.accept(); // 实例化Socket对象,侦听要连接到此套接字并接受它。 该方法将阻塞直到建立连接。
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream())); // 实例化BufferedReader对象,创建一个使用默认字符集的InputStreamReader。
writer = new PrintWriter(socket.getOutputStream(), true);//从现有的OutputStream创建一个新的PrintWriter。
getClientInfo(); // 调用getClientInfo()方法
}
} catch (Exception e) {
e.printStackTrace(); // 输出异常信息
}
}
/**
* 接收客户端发送的信息
*/
private void getClientInfo() {
// TODO Auto-generated method stub
try {
while(true){
//readLine()
//读一行文字。 一行被视为由换行符('\ n'),回车符('\ r')中的任何一个或随后的换行符终止。
String line=reader.readLine();
if(line!=null)
textArea.append("接收到客户机发送的信息:"+line+"\n");
}
} catch (Exception e) {
// TODO: handle exception
textArea.append("客户端已退出。\n");
}
finally {
try {
if (reader != null) {
reader.close();// 关闭流
}
if (socket != null) {
socket.close(); // 关闭套接字
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端程序:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.Font;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ClientSocketFrame extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private PrintWriter writer; // 声明PrintWriter类对象
private BufferedReader reader; // 声明BufferedReader对象
private Socket socket; // 声明Socket对象
private JTextArea textArea; // 创建JtextArea对象
private JTextField textField; // 创建JtextField对象
/**与服务器连接的套接字对象、输入流对象和输出流对象
* 以及在文本域中显示与服务器的连接信息和接收到服务器端发送的信息
*/
private void connect() { // 连接套接字方法
textArea.append("尝试连接......\n"); // 文本域中信息信息
try { // 捕捉异常
socket = new Socket("10.82.25.166", 1978); // 实例化Socket对象
while (true) {
writer = new PrintWriter(socket.getOutputStream(), true);
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream())); // 实例化BufferedReader对象
textArea.append("完成连接。\n"); // 文本域中提示信息
getServerInfo();
}
} catch (Exception e) {
e.printStackTrace(); // 输出异常信息
}
}
public static void main(String[] args) { // 主方法
ClientSocketFrame clien = new ClientSocketFrame(); // 创建本例对象
clien.setVisible(true); // 将窗体显示
clien.connect(); // 调用连接方法
}
/**
* 接收服务端发送的信息
*/
private void getServerInfo() {
try {
while (true) {
if (reader != null) {
String line = reader.readLine();// 读取服务器发送的信息
if (line != null)
textArea.append("接收到服务器发送的信息:" + line + "\n"); // 显示服务器端发送的信息
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();// 关闭流
}
if (socket != null) {
socket.close(); // 关闭套接字
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 创建客户端窗体
*/
public ClientSocketFrame() {
super();
setTitle("客户端程序");
setBounds(100, 100, 361, 257);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
getContentPane().add(panel, BorderLayout.NORTH);
final JLabel label = new JLabel();
label.setFont(new Font("", Font.BOLD, 22));
label.setText("一对一通信——客户端程序");
panel.add(label);
final JPanel panel_1 = new JPanel();
getContentPane().add(panel_1, BorderLayout.SOUTH);
final JLabel label_1 = new JLabel();
label_1.setText("客户端发送的信息:");
panel_1.add(label_1);
textField = new JTextField();
textField.setPreferredSize(new Dimension(140, 25));
panel_1.add(textField);
final JButton button = new JButton();
button.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
writer.println(textField.getText()); // 将文本框中信息写入流
textArea.append("客户端发送的信息是:" + textField.getText()
+ "\n"); // 将文本框中信息显示在文本域中
textField.setText(""); // 将文本框清空
}
});
button.setText("发 送");
panel_1.add(button);
final JScrollPane scrollPane = new JScrollPane();
getContentPane().add(scrollPane, BorderLayout.CENTER);
textArea = new JTextArea();
scrollPane.setViewportView(textArea);
//
}
}
实例320:一个服务器与多个客户端通信
本实例通过线程处理不同客户发送的信息,当多个客户连接到服务器时,服务器会为每一个客户建立一个线程来处理接收到的信息,而不会产生阻塞。
主要代码如下:
//在服务器端创建线程类ServerThread,用于接收客户端发送的信息以及处理客户端的退出信息,该线程类中run()方法的关键代码如下:
public void run(){
try{
if(socket!=null){
reder=new BufferedReader(new InputStreamReader(socket.getInputStream()));
int index=-1;
try{
while(true){
String line=reader.readLine();
try{
index=Integer.parseInt(line);
}catch(Exception ex){
index=-1;
}
if(line!=null){
ta_info.append("接收到客户机发送的信息:"+line+"\n");
}
}
}catch(Exception e){
if(line!=null){
vector.set(index,null);
ta_info.append("第"+(index+1)+"个客户端已经退出。\n");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
//客户端窗体的关闭事件,用于向服务器发送退出客户的索引值
writer.println(String.valueOf(index));
//创建并启动线程对象的关键代码
new ServerThread(socket).start();
本实例主要是在服务器端通过线程对客户端发送的信息进行监听,当有客户端发送信息时,就会将该信息发送给其他已经登录到服务器的客户端,但是不会向发送方发送该信息,在客户端也通过线程来监听服务器转发的信息。
关键代码如下:
//在服务器端创建线程类,用于接收客户端发送的信息,并转发,run()方法的关键代码:
while(true){
String info=in.readLine();
for(Socket s:vector){
if(s!=socket){
PrintWriter out=new PrintWriter(s.getOutputStream(),true);
out.println(info);
out.flush();
}
}
}
//在客户端创建线程类,用于接收服务器端发送的客户信息,并显示。run()方法的关键代码如下:
while(true){
String info=in.readLine();
ta_info.append(info+"\n");
if(info.equals("88")){
break;
}
}