Socket通信---网络通信学习笔记(一)

原文地址为: Socket通信---网络通信学习笔记(一)

两台计算机进行通信的基本前提:

(1)IP地址:
每台计算机都有自己独一无二的IP地址,根据IP地址判断与哪台计算机进行通信。
(2)端口号:
每个应用程序都有自己专属的端口,根据端口号判断与计算机中的哪个应用程序进行通信。
说明:
<1>用于区分不同应用程序
<2>端口号的范围:0-65535,其中0-1023是为系统保留的端口号
<3>常用的协议的端口号:
http:80
ftp:21
telnet:23
<4>IP地址+端口号=Socket,Socket是TCP和UDP通信的基础
(3)协议:
好比是两个计算机通信的共同的语言,只用同时用这种协议才能够听懂对方说的话。
TCP/IP协议:是目前世界上应用最为广泛的协议,TCP/IP协议是传输层的协议,HTTP超文本传输协议,FTP文件传输协议,SMTP简单邮件传送协议,Telnet远程登录服务是应用层协议。
TCP:传输控制协议
IP:互联网协议

Java中提供的网络功能有四大类:

(1)InetAddress:用于标识网络上的硬件资源(就是IP地址的相关信息)
(2)URL:同一资源定位符,通过URL可以直接读取和写入网络上的数据
(3)Socket:使用TCP协议实现网络通信的Socket相关的类
(4)Datagram:使用UDP协议,将数据保存到数据报中,通过网络进行通信
注意:
TCP协议:是面向连接的,可靠的,有序的,以字节流的方式发送数据
UDP协议:是无连接的,不可靠的,无序的,速度比较快,以数据报作为数据传输的载体,进行数据传输时,首先要将传输的数据定义成数据报(Datagram),在数据报中指明数据所要到达的Socket(主机地址和端口号),然后再将数据报发送出去。

基于TCP的Socket通信:

(1)服务器端的实现步骤:
<1>创建ServerSocket,指定端口号
<2>调用ServerSocket的accept()方法(该方法为阻塞方法,直到有客户端连接进来才会执行该方法之后的代码),等待客户端的连接,当有客户端连接后就会返回一个Socket实例
<3>调用Scoket实例的getInputStream()方法,读取客户端发送过来的信息
<4>调用Socket实例的getOutputStream()方法,向客户端写入服务器发送给该客户端的信息
<5>关闭所有资源

(2)客户端实现步骤:
<1>创建Socket对象,并指明服务器的IP地址(或主机名)和端口号
<2>调用Socket实例的getOutputStream()方法,向服务器发送内容
<3>调用Socket实例的getInputStream()方法,接收服务器发送来的信息
<4>关闭所有资源

注意:
在使用socket进行TCP通信时,对于同一个Socket,如果关闭了输出流,
则与该输出流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可。

下面一个多客户端(多线程)与服务器通信的例子来说明一下基于TCP协议的Socket通信使用方法:
服务器端代码:

package com.socket.serversocket;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Serversocket {

public static void main(String[] args) {

ServerSocket serversocket;
int count = 0;
try {
serversocket = new ServerSocket(8080);
System.out.println("服务器已经启动,等待客户端的连接。。。。");
while(true){
Socket socket = serversocket.accept();
new ServerThread(socket).start();
count++;
System.out.println("客户端的IP:"+socket.getInetAddress().getHostAddress());
System.out.println("连接的客户端的总数:"+count);
}


} catch (IOException e) {
e.printStackTrace();
}


}




}
package com.socket.serversocket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class ServerThread extends Thread{
Socket socket = null;
public ServerThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while((line = br.readLine())!=null){
System.out.println("我是服务器,客户端发来的信息是:"+line);
}
socket.shutdownInput();//关闭socket的输入流

PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.write("欢迎访问服务器");
socket.shutdownOutput();//关闭socket的输出流

//关闭资源
br.close();
pw.close();
socket.close();//注意:在使用socket进行TCP通信时,对于同一个Socket,如果关闭了输出流,
//则与该输出流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可

} catch (IOException e) {
e.printStackTrace();
}

}
}

客户端代码:

package com.socket.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost",8080);
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
String s = "用户名:admin;密码:123";
pw.write(s);
pw.flush();//因为PrintWriter是字符流,所以在写入完成后一定要调用字符流的flush()方法,冲刷缓冲区保证所有内容已经从流里写出去了
socket.shutdownOutput();

InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null;
while((line = br.readLine())!=null){
System.out.println("我是客户端,接收到服务器发来的信息为:"+line);
}
socket.shutdownInput();
//关闭资源
br.close();
is.close();
pw.close();
os.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

}

程序运行结果:
这里写图片描述

基于UDP的Socket通信

(1)服务器端实现步骤:
<1>创建DatagramSocket,指定端口号
<2>创建DatagramPacket
<3>接收客户端发送的数据信息
<4>读取数据

(2)客户端实现步骤:
<1>定义发送信息
<2>创建DatagramPacket,包含将要发送的信息
<3>创建DatagramSocket
<4>发送数据

下面是一个客户端(单线程)与服务器之间的信息交互例子,可以看出基于UDP的Socket通信的使用步骤:
服务器端代码:

package com.udpServer;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Server {
public static void main(String[] args) {
try {
//1.创建服务器端DatagramSocket,指定端口
DatagramSocket socket = new DatagramSocket(8080);
//2.创建数据报,用于接收客户端发送的数据
byte[] data = new byte[1024];//创建字节数组,指定接收的数据包的大小
DatagramPacket packet = new DatagramPacket(data, data.length);
//3.接收客户端发送的数据
System.out.println("服务器端已经启动,等待客户端连接。。。");
socket.receive(packet);//此方法在接收到数据之前一直阻塞
//4.读取数据
String info = new String(data,0,packet.getLength());
System.out.println("我是服务器,客户端说:"+info);

/*
* 服务器端响应客户端
*
*/

//1.定义客户端的地址,端口号,数据
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "欢迎访问服务器!".getBytes();
//2.创建数据报,包含响应的数据信息
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
//3.响应客户端
socket.send(packet2);
//4.关闭资源
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}

}

客户端代码:

package com.udpClient;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
* 客户端
* 2015年8月28日 下午2:19:51
* @author 张耀晖
*
*/

public class Client {
public static void main(String[] args) {

try {
/*
* 向服务器端发送数据
*/

//1.定义服务器的地址,端口号,数据
InetAddress address = InetAddress.getByName("localhost");
int port = 8080;
byte[] data = "用户名:admin;密码:123".getBytes();
//2.创建数据报,包含发送的数据信息
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
//3.创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket();
//4.向服务器端发送数据报
socket.send(packet);

/*
* 接收服务器端相应的数据
*
*/

//1.创建数据报,用于接收服务器相应的数据
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
//2.接收服务器端响应的数据信息
socket.receive(packet2);
//3.读取数据
String reply = new String(data2,0,packet2.getLength());
System.out.println("我是客户端,服务器说:"+reply);
//4.关闭资源
socket.close();
} catch (Exception e) {
e.printStackTrace();
}

}
}

运行结果演示:
这里写图片描述

下面是多个客户端(多线程)与服务器之间的信息交互的代码。
服务器端代码:

package com.udpServer;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Server1 {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(8080);
System.out.println("服务器已经启动,等待客户端发送数据。。。");
int count = 0;
while(true){
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
socket.receive(packet);
count++;
System.out.println("连接进来的客户端的数量为:"+count);
new ServerThread(data,packet,socket).start();
}




}

}
package com.udpServer;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class ServerThread extends Thread{
byte[] data = null;
DatagramPacket packet = null;
DatagramSocket socket = null;

public ServerThread(byte[] data,DatagramPacket packet,DatagramSocket socket){
this.data = data;
this.packet = packet;
this.socket = socket;
}

@Override
public void run() {
String info = new String(data,0,packet.getLength());
System.out.println("我是服务器,客户端说:"+info);
//服务器响应客户端信息
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "欢迎访问服务器!".getBytes();
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
try {
socket.send(packet2);
} catch (IOException e) {
e.printStackTrace();
}
}

}

客户端代码:

package com.udpClient;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Client1 {
public static void main(String[] args) throws IOException {
InetAddress address = InetAddress.getByName("localhost");
int port = 8080;
byte[] data = "用户名:admin;密码:123".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
DatagramSocket socket = new DatagramSocket();
socket.send(packet);

//接收服务器的相应信息
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
socket.receive(packet2);
String relpy = new String(data2, 0, packet2.getLength());
System.out.println("我是客户端,服务器说:"+relpy);
socket.close();

}

}

运行结果演示:
这里写图片描述

下面是一个文件上传(将整张图片的内容保存到mysql数据库中,因为图片很小所以直接就将图片内容直接存储到了数据库中了,如果图片很大就应该将图片上传,然后将图片的地址保存到数据库中就可以了)的代码,该代码中使用到了基于TCP的Socket的通信,数据库的基本查询和插入,基本的IO字节流的读写操作。
程序目录结构:
这里写图片描述
服务器端的Server类:

package com.test.socket;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
public static void main(String[] args) {
try {
ServerSocket serversocket = new ServerSocket(8888);
System.out.println("服务器已启动,等待客户端的连接。。。");
while(true){
Socket socket = serversocket.accept();
new ServerThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
}

}

}

服务器端的线程类(ServerThread类):

package com.test.socket;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import com.test.entity.FileInfo;
import com.test.entity.User;
import com.test.service.FileService;
import com.test.service.UserService;
import com.test.util.CommandTransfer;

public class ServerThread extends Thread{
private Socket socket = null;
private ObjectInputStream ois = null;
private ObjectOutputStream oos = null;
private UserService us = new UserService();//用户业务对象
private FileService fs = new FileService();//文件业务对象

public ServerThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
ois = new ObjectInputStream(socket.getInputStream());
oos = new ObjectOutputStream(socket.getOutputStream());
CommandTransfer transfer = (CommandTransfer)ois.readObject();
transfer = execute(transfer);//执行客户端发送到服务器的指令操作
oos.writeObject(transfer);

} catch (Exception e) {
e.printStackTrace();
}
}
private CommandTransfer execute(CommandTransfer transfer) {
String cmd = transfer.getCmd();//获取当前操作的指令
if(cmd.equals("login")){
User user = (User)transfer.getData();
boolean flag = us.findLogin(user);
transfer.setFlag(flag);
if(flag){//判断登录是否成功
transfer.setResult("登录成功!");
}else{
transfer.setResult("用户名或者密码不正确,请重新登录!");
}
}else if(cmd.equals("register")){
User user = (User)transfer.getData();
boolean flag = us.registerLogin(user);
transfer.setFlag(flag);
if(flag){
transfer.setResult("注册成功!");
}else{
transfer.setResult("注册失败!");
}
}else if(cmd.equals("uploadFile")){
FileInfo fileInfo = (FileInfo)transfer.getData();
boolean flag = fs.uploadFile(fileInfo);
transfer.setFlag(flag);
if(flag){
transfer.setResult("文件上传成功!");
}else{
transfer.setResult("文件上传失败!");
}
}
return transfer;
}

}

客户端代码(Client类)

package com.test.socket;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

import com.test.entity.FileInfo;
import com.test.entity.User;
import com.test.util.CommandTransfer;

public class Client {
private static Scanner input = new Scanner(System.in);
private static Socket socket = null;

public static void main(String[] args) {
showMainMenu();
}

private static void showMainMenu() {
System.out.println("*****欢迎使用文件上传器******");
System.out.println("1.登录\n2.注册\n3.退出");
System.out.println("*************************");
System.out.println("请选择:");
int choice = input.nextInt();
switch (choice) {
case 1:
showLogin();
break;
case 2:
showRegister();
break;
case 3:
System.out.println("再见,感谢您对本系统的支持!");
System.exit(0);
break;

default:
System.out.println("输入有误!");
System.exit(0);
break;
}

}

/**
* 注册操作
*/

private static void showRegister(){
CommandTransfer transfer = new CommandTransfer();
while(true){
System.out.print("请输入用户名:");
String username = input.next();
System.out.print("请输入密码:");
String password = input.next();
System.out.print("请再次输入密码:");
String password2 = input.next();
if(!password.equals(password2)){
System.out.println("两次输入的密码不一致!");
System.out.println("**********************");
continue;
}
User user = new User(username,password);
transfer.setCmd("register");
transfer.setData(user);

try {
socket = new Socket("localhost",8888);
sendData(transfer);//将数据发送到服务器
transfer = getData();//获取服务器返回的数据
System.out.println(transfer.getResult());//输出显示结果
System.out.println("*****************");
if(transfer.isFlag()){
break;//如果注册成功,则不再重复执行注册操作
}

} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}

}
showLogin();//注册成功后显示登录
}

/**
* 登录操作
*/

private static void showLogin() {
CommandTransfer transfer = new CommandTransfer();
int count = 0;
while (true) {
count++;
if (count > 3) {
System.out.println("您已经连续三次登录失败,程序退出!");
System.exit(0);
}
System.out.print("请输入用户名:");
String username = input.next();
System.out.print("请输入密码:");
String password = input.next();
User user = new User(username, password);
transfer.setCmd("login");
transfer.setData(user);

try {
socket = new Socket("localhost",8888);
sendData(transfer);//将数据发送到服务器
transfer = getData();//获取服务器返回的数据
System.out.println(transfer.getResult());//输出显示结果
System.out.println("************************");
if(transfer.isFlag()){
break;//如果登录成功,则不再重复执行登录
}

} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}


}
showUploadFile();//登录成功后进行文件上传

}

/**
* 文件上传操作
*/

private static void showUploadFile() {
CommandTransfer transfer = new CommandTransfer();
System.out.println("请输入上传文件的绝对路径(如:E:/photo.jpg");
String path = input.next();
FileInfo fileInfo = null;
FileInputStream fis = null;
BufferedInputStream bis = null;
String filename = path.substring(path.lastIndexOf("/")+1);
try {
fis = new FileInputStream(path);
byte[] content = new byte[fis.available()];
bis = new BufferedInputStream(fis);
bis.read(content);
fileInfo = new FileInfo(filename, content);
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
transfer.setCmd("uploadFile");
transfer.setData(fileInfo);
try {
socket = new Socket("localhost",8888);
sendData(transfer);
transfer = getData();
System.out.println(transfer.getResult());
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}


}

private static CommandTransfer getData() {
CommandTransfer transfer = new CommandTransfer();
try {
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
transfer = (CommandTransfer)ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}

return transfer;
}

private static void sendData(CommandTransfer transfer) {
try {
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(transfer);
} catch (IOException e) {
e.printStackTrace();
}

}

}

文件储存到数据库中的操作类(FileService):

package com.test.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import com.test.entity.FileInfo;
import com.test.util.DBUtil;

public class FileService {
private Connection conn = null;
private PreparedStatement pstmt = null;
private DBUtil dbutil= null;

public FileService(){
dbutil = new DBUtil();
this.conn = dbutil.getConnection();

}

public boolean uploadFile(FileInfo fileInfo){
boolean flag = false;
String sql = "insert into fileinfo(filename,content) values (?,?)";
try {
this.pstmt = this.conn.prepareStatement(sql);
this.pstmt.setString(1,fileInfo.getFilename() );
this.pstmt.setBytes(2, fileInfo.getContent());
if(this.pstmt.executeUpdate()==1){
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
dbutil.DBCclose();
}
return flag;
}

}

用户信息储存到数据库中的操作类(UserService):

package com.test.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.test.entity.User;
import com.test.util.DBUtil;

public class UserService {
private Connection conn = null;
private PreparedStatement pstmt = null;
private DBUtil dbutil = null;

public UserService(){
dbutil = new DBUtil();
this.conn = dbutil.getConnection();
}

public boolean findLogin(User user){
boolean flag = false;
String sql = "select username from user where username = ?and password =?";
try {
this.pstmt = this.conn.prepareStatement(sql);
this.pstmt.setString(1, user.getUsername());
this.pstmt.setString(2, user.getPassword());
ResultSet result = this.pstmt.executeQuery();
if(result.next()){
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
dbutil.DBCclose();
}

return flag;
}

public boolean registerLogin(User user){
boolean flag = false;
String sql = "insert into user(username,password) values (?,?)";
try {
this.pstmt = this.conn.prepareStatement(sql);
this.pstmt.setString(1, user.getUsername());
this.pstmt.setString(2, user.getPassword());
if(this.pstmt.executeUpdate()==1){
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
dbutil.DBCclose();
}
return flag;
}


}

数据库工具类(DBUtil类):

package com.test.util;

import java.sql.Connection;
import java.sql.DriverManager;


public class DBUtil {
private static final String DBDRIVER = "com.mysql.jdbc.Driver";
private static final String DBUSER = "root";
private static final String DBPWD = "123";
private static final String DBURL = "jdbc:mysql://localhost:3306/fileupdown";
private Connection conn = null;

/**
* 构造方法中进行数据库的连接
*/

public DBUtil() {
try {
Class.forName(DBDRIVER).newInstance();// 加载数据库的驱动类
conn = DriverManager.getConnection(DBURL, DBUSER, DBPWD);// 连接数据库
System.out.println("连接数据库成功!!!");
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 关闭数据库
*/

public void DBCclose() {
if (this.conn != null) {
try {
this.conn.close();//数据库关闭操作
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 取的数据库连接对象
*/

public Connection getConnection(){
return this.conn;
}

}

客户端和服务器之间传输的指令数据工具类(CommandTransfer):

package com.test.util;

import java.io.Serializable;

/**
* 此类表示客户端和服务器之间传输的指令数据
* 2015年8月28日 下午5:26:07
* @author 张耀晖
*
*/

public class CommandTransfer implements Serializable{

private static final long serialVersionUID = 1L;
private String cmd;//当前操作的命令
private Object data;//发送的数据
private boolean flag;//操作是否成功
private String result;//返回的结果
public String getCmd() {
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}

}

文件信息实体类(FileInfo类):

package com.test.entity;

import java.io.Serializable;

public class FileInfo implements Serializable{

private static final long serialVersionUID = 1L;
private int id;//文件编号
private String filename;//文件名
private byte[] content;//文件内容
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
public FileInfo(String filename, byte[] content) {
this.filename = filename;
this.content = content;
}



}

用户信息实体类(User类):

package com.test.entity;

import java.io.Serializable;

public class User implements Serializable{

private static final long serialVersionUID = 1L;
private int id;//编号
private String username;//用户名
private String password;//密码
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User( String username, String password) {
this.username = username;
this.password = password;
}



}

运行结果演示:
这里写图片描述
这里写图片描述

该程序代码下载地址:

点击下载代码


转载请注明本文地址: Socket通信---网络通信学习笔记(一)

猜你喜欢

转载自blog.csdn.net/zhengxiuchen86/article/details/80874804
今日推荐