必威体育Betway必威体育官网
当前位置:首页 > IT技术

Socket通信详解

时间:2019-10-29 05:44:27来源:IT技术作者:seo实验室小编阅读:58次「手机版」
 

socket通信

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。

建立一个完整的socket(套接字),需要调用java.net包中的Socket类和ServerSocket类(Socket和ServerSocket的工作都是通过SocketImpl类及其子类完成的),客户端的输出流是服务器端的输入流,服务器端的输出流是客户端的输入流。

首先用sqlyog创建一个数据库socket,需要记住用户名root密码1234,创建user、file表。

到时候使用获取mysql链接时方便进行数据库操作。

第一部分:服务器监听

创建Server类,服务器端必须先创建ServerSocket对象,并使用accept()方法监听等待客户端。

Java API为TCP协议提供的类分为Socket和ServerSocket,可以简单的理解为客户端Socket和服务端Socket,更具体的讲Socket对象是主动发起连接请求的,而ServerSocket对象是等待接收连接请求的。

代码

package com.socket;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

/*

* 服务器端主程序操作

*/

public class Server {

public static void main(String[] args) {

try {

    //1.创建服务器端的ServerSocket,指定伴随的端口

    ServerSocket serversocket=new ServerSocket(8881);

    System.out.println("****服务器端即将启动,等待客户端*****");// 提示服务器开启

    Socket socket=null;

    while(true){

        //2.调用accept()方法开始监听,等待客户端连接

        socket=serversocket.accept();

        //调用线程

        ServerThread st=new ServerThread(socket);

        st.start();

    }

} catch (IOException e) {

    e.printstacktrace();

}

}

}

等待客户端连接需要创建线程ServerThread,并开启线程。

代码:

package com.socket;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.net.Socket;

import java.sql.SQLException;

//这里是根据客户端的需求写的响应

import com.Server_To_Client_response.Add_File_Response;

import com.Server_To_Client_Response.Add_User_Response;

import com.Server_To_Client_Response.Select_User_Response;

public class ServerThread extends Thread {

//创建与本线程相关的socket

Socket socket=null;

ObjectInputStream ois=null;

//重写构造函数

public ServerThread(Socket socket){

this.socket=socket; 

}

//线程操作 获取输出流,响应客户端的请求

public void run(){

    try {

        //3.获取输入流,并读取客户端信息

        ois=new ObjectInputStream(socket.getInputStream());

             /*

              * 读取接收到的对象信息

              */

   //available() 方法返回从这个输入流中读取余下的字节

             while( ois.available()!=-1){

             //输入流读取的信息对象化

             ClientToServerInfo ctsi =(ClientToServerInfo)ois.readObject();

             System.out.println(ctsi.toString());//打印客户端传到服务器的数据(对象)

             /*

              * 需要客户端传入的对象数据进行分析,根据传入的sign标志值进行相应的操作

              * sign="1"完成注册会员操作,链接数据库,向数据库中user表添加记录

              * sign="2"完成会员登录操作,连接数据库,在数据库中user表查询记录

              * sign="3"完成文件上传操作,连接数据库,向数据库中file表添加记录

              */

             //System.out.println(ctsi.sign);

             if(ctsi.sign.equals("1")){

                 //调用 Add_User_Response类的构造方法对客户端传入的信息进行详细的响应

                 Add_User_Response aur=new Add_User_Response(socket, ctsi.name,ctsi.password_OR_path);

                //System.out.println("运行完Add_User_Response类");

                break;

             }

             if(ctsi.sign.equals("2")){

                 //调用 Select_User_Response类的构造方法对客户端传入的信息进行详细的响应

                 Select_User_Response sur=new Select_User_Response(socket, ctsi.name,ctsi.password_OR_path);

                 //System.out.println("运行完 Select_User_Response类");

                 break;

             }

             if(ctsi.sign.equals("3")){

                //调用  Add_File_Response类的构造方法对客户端传入的信息进行详细的响应

                 Add_File_Response afr=new Add_File_Response(socket, ctsi.name,ctsi.password_OR_path);

                // System.out.println("运行完 Add_File_Response类");

                 break;

             }

             }

            //5.关闭资源

             ois.close();

    } catch (ClassnotfoundException e) {

        e.printStackTrace();

    } catch (IOException e) {

        e.printStackTrace();

    } catch (SQLException e) {

        e.printStackTrace();

    }

}

}

/*

* 创建存储需要传输信息的对象,方便客户端向服务器端传送数据

*/

package com.socket;

import java.io.serializable;

public class ClientToServerInfo implements Serializable{

//当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化

String sign;

String name;

String password_OR_path;

//重写构造函数

public ClientToServerInfo(String sign,String name,String password_OR_path){

this.sign=sign;

this.name=name;

this.password_OR_path=password_OR_path;

}

  

public String getSign() {

return sign;

}

public void setSign(String sign) {

this.sign = sign;

}

public String getName() {

return name;

}

public void setName(String Name) {

this.name = name;

}

public String getPassword_OR_path() {

return password_OR_path;

}

public void setPassword_OR_path(String password_OR_path) {

this.password_OR_path = password_OR_path;

}

@Override

public String toString() {

return "客户端说: [sign=" + sign + ", name=" + name + ", password_OR_path=" +password_OR_path + "]";

}

}

服务器端对客户端的响应:

代码:

package com.Server_To_Client_Response;

/*

* 判断客户端传入到服务器端的上传文件数据,提交到数据库中,

* 是否在数据库中添加成功,并完成对客户端的响应,成功返回上传成功,

* 反之提示其重新上传

*/

import java.io.IOException;

import java.io.outputstream;

import java.io.printwriter;

import java.net.Socket;

import java.sql.SQLException;

import com.database.Database_Add_File;

public class Add_File_Response {

OutputStream os=null;

PrintWriter pw=null;

Socket socket=null;

boolean flag;

public Add_File_Response(Socket socket,String name,

    String password_OR_path ) throws SQLException, IOException{

//将已创建的socket对象加载进来

this.socket=socket;

os=socket.getOutputStream();

pw=new PrintWriter(os);

/*

 * 创建和数据库的链接,完成向file表格中添加记录操作

 *  添加成功返回false,否则返回true

 */

Database_Add_File dau=new Database_Add_File();

flag=dau.Database_Add_File_run(name, password_OR_path);

/*

 * 对插入数据后的返回值进行分析,并对客户端进行响应

 */

if(!flag){

    //获取输出流,响应客户端的请求

        pw.write("文件上传成功!");

        pw.flush();

}

else{

    pw.write("文件上传失败!请重新上传!");

    pw.flush();

}

//关闭输入输出流(关闭资源)

pw.close();

os.close();

}

}

package com.Server_To_Client_Response;

/*

* 判断客户端传入到服务器端的注册会员数据,提交到数据库中,

* 是否在数据库中添加成功,并完成对客户端的响应,成功返回注册成功,

* 反之提示其重新注册

*/

import java.io.IOException;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.net.Socket;

import java.sql.SQLException;

import com.database.Database_Add_User;

public class Add_User_Response {

Socket socket=null;

boolean flag;

public Add_User_Response(Socket socket,String name,

    String password_OR_path ) throws SQLException{

try {

    System.out.println("运行Add_User_Response类");

    //将已创建的socket对象加载进来

    

    this.socket=socket;

    OutputStream os=socket.getOutputStream();

    // 根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter

    PrintWriter pw=new PrintWriter(os);

    /*

     * 创建和数据库的链接,完成向user表格中添加记录操作

     * 添加成功返回false,否则返回true

     */

    Database_Add_User dau=new Database_Add_User();

    flag=dau.Database_Add_User_run(name, password_OR_path);

    //System.out.println("运行完Database_Add_User类");

    /*

     * 对插入数据后的返回值进行分析,并对客户端进行响应

     */

    if(!flag){

        //获取输出流,响应客户端的请求

            pw.write("您已经成功注册!");

            pw.flush();

    }

    else{

        pw.write("注册失败!");

        pw.flush();

    }

    //关闭输入输出流(关闭资源)

    pw.close();

    os.close();

    } catch (IOException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

}finally {

    try {

        socket.close();

    } catch (IOException e) {

        // TODO Auto-generated catch block

        e.printStackTrace();

    }

}

}

}

package com.Server_To_Client_Response;

/*

* 判断客户端传入到服务器端的会员数据,提交到数据库中,

* 在数据库中查找,是否注册,密码是否正确,若账号名和密码都正确,登陆成功

* 并完成对客户端的响应。

*/

import java.io.IOException;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.net.Socket;

import java.sql.SQLException;

import com.database.Database_Select_User;

public class Select_User_Response {

//输出流的初始化,为服务器对客户端的响应做准备

OutputStream os=null;

PrintWriter pw=null;

Socket socket=null;

int flag;

public Select_User_Response(Socket socket,String name,

    String password_OR_path) throws IOException, SQLException{

this.socket=socket;

os=socket.getOutputStream();

pw=new PrintWriter(os);

/*

 * 创建和数据库的链接,完成对user表格的查询操作(完成登陆)

 * 若登录名不存在file表中(即未注册)返回2

 * 若登录名存在file表中,密码输入正确返回1

 * 若登录名存在file表中,密码输入错误返回3

 */

Database_Select_User dsu=new Database_Select_User();

flag=dsu.Database_Select_User_run(name,password_OR_path);

/*

 * 对查找返回的数据进行分析,并对客户端进行响应

 */

if(flag==1){

       pw.write("欢迎您,你已经完成登陆!请输入3");

       pw.flush();

}

else if(flag==2){

     pw.write("您还没进行注册,请先注册!");

       pw.flush();

}

else{

     pw.write("密码错误!请重新登陆!请输入2");

       pw.flush();             

}

//关闭输出输入流(关闭资源)

pw.close();

os.close();

}

}

创建数据库连接对象

代码:

package com.database;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;

public class DBUtil {

private static final String URL="jdbc:mysql://127.0.0.1:3306/socket?useunicode=true&characterEncoding=utf-8";

private static final String USER="root";

private static final String PASSWORD="1234";

  

private static Connection conn=null;

  

static {

try {

    //1.加载数据库厂商提供的驱动程序,com.myql.jdbc.Driver在mysql-connector-java的jar包中

    class.forname("com.mysql.jdbc.Driver");

    //2.获得数据库的连接

    conn=DriverManager.getConnection(URL, USER, PASSWORD);

} catch (ClassNotFoundException e) {

    e.printStackTrace();

} catch (SQLException e) {

    e.printStackTrace();

}

}

//将获得的数据库与java的链接返回(返回的类型为Connection)

public static Connection getConnection(){

return conn;

}

}

package com.database;

/*

* 服务器端链接数据库,并向数据库表file添加数据

* 添加成功返回false,否则返回true

*/

import java.io.File;

import java.io.fileinputstream;

import java.io.filenotfoundException;

import java.io.InputStream;

import java.sql.Connection;

import java.sql.Preparedstatement;

import java.sql.SQLException;

public class Database_Add_File {

boolean flag=false;

public boolean Database_Add_File_run(String name,String password_OR_path) throws SQLException, FileNotFoundException{

//获取mysql链接

File files = new File(password_OR_path);

Connection conn=DBUtil.getConnection();

String sql=""+"insert into file"+"(user_name,path)"

            +"values("+"?,?)";

//加载sql语句到执行程序中(并不进行执行)

PreparedStatement ptmt=conn.preparestatement(sql);

ptmt.setString(1, name);

//ptmt.setString(2, password_OR_path);

FileInputStream fis = new FileInputStream(files);

ptmt.setbinaryStream(2, (InputStream) fis, (int) (files.length()));

flag=ptmt.execute();

return flag;

}

}

package com.database;

/*

* 服务器端链接数据库,并向数据库表user添加数据

* 添加成功返回true,否则返回false

*/

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.SQLException;

public class Database_Add_User {

boolean flag=false;

//

public boolean Database_Add_User_run(String name,String password_OR_path) throws SQLException{

//获取mysql链接

System.out.println("开始运行Database_Add_User类");

Connection conn=DBUtil.getConnection();

String sql=""+"insert into user"+"(user_name,user_password)"

            +"values("+"?,?)";

//加载sql语句到执行程序中(并不进行执行)

//PreparedStatement是预编译的,对于批量处理可以大大提高效率. 也叫JDBC存储过程

PreparedStatement ptmt=conn.prepareStatement(sql);

ptmt.setString(1, name);

ptmt.setString(2, password_OR_path);        

flag=ptmt.execute();

return flag;

}

}

package com.database;

/*

* 用于用户登录时,将账号和密码传递到数据库,

* 若登录名不存在file表中(即未注册)返回2

* 若登录名存在file表中,密码输入正确返回1

* 若登录名存在file表中,密码输入错误返回3

*/

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

public class Database_Select_User {

  

  

public int Database_Select_User_run(String name,String password_OR_path) throws SQLException{

//获取mysql链接

Connection conn=DBUtil.getConnection();

String sql1=""+"select * from user where user_name like ?";

PreparedStatement ptmt=conn.prepareStatement(sql1.toString());

ptmt.setString(1, "%"+name+"%");

//executequery查询得到的结果集

ResultSet rs=ptmt.executeQuery();

while(rs.next()){

    //判断密码是否正确,正确返回1

    if(rs.getString("user_password").equals(password_OR_path)){

        return 1;

    }

    //判断密码错误返回2

    else

        return 3;

}

//用户名不存在返回2

return 2;

}

}

第二部分:客户端请求

第三部分:连接确认,服务器端监听到客户端的请求,然后响应客户端的套接字请求。

代码:

package com.socket;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.inputstreamreader;

import java.io.ObjectOutputStream;

import java.io.OutputStream;

import java.net.Socket;

import java.net.UnknownHostException;

import java.util.scanner;

public class Client {

public static void main(String[] args) throws UnknownHostException, IOException {

String []data=new String[3];

int sum=0;//用来记录客户端第几次传值到服务器端

/*

 * 客户端界面的提示语

 */

ClientTips ctps=new ClientTips();

Scanner sc=new Scanner(System.in);

int n;

/*

 * 客户端实现与服务器端交互

 */

try {

while(true){//使得客户端始终处于程序运行当中

    //1.创建客户端socket,指定服务器和端口号

    Socket socket=new Socket("localhost", 8881);

    OutputStream os=null;

    InputStream is=null;

    InputStreamReader isr=null;

    BufferedReader br=null;

    ClientToServerInfo ctsi=null;

    ObjectOutputStream oos=null;

    while(true){//通过循环让客户端完成注册、登录    和上传信息的客户界面操作

        n=sc.nextint();

        /*

         * data用于接收tipchoose返回的字符串数组

         * 如果传入的值为1时返回的为三个非空字符串,分别为“1”和用户名和密码

         * 如果传入的值为2是返回的为两个非空字符串,分别是“2”和用户名和密码

         * 如果传入的值为3是返回的两个字符串,分别是“3”和data[0]=null,data[1]=文件路径

         */

    data=ctps.tipChoose(n);

    if(data[0]!=null&&data[1]!=null){

        //如果界面输入值合理,则跳出一层循环

        break;

      }

    }

        //2.获取输出流,向服务器端发送信息

        os=socket.getOutputStream();//字节输出流

        

        /*

         *这里要判断是不是第一次写文件,若是则写入头部,否则不写入。 

         */

        //if(sum==0){

           oos=new ObjectOutputStream(os);

        //}

        //将需要传送的信息封装成对象

        ctsi=new ClientToServerInfo(data[0],data[1],data[2]);

        oos.writeObject(ctsi);

        oos.writeObject(null);//objectoutputstream写入结束的标志

        oos.flush();

        sum++;

        //socket.shutdownOutput();//关闭输出流

        //3.读取服务器端发送的信息

        is=socket.getInputStream();

        isr=new InputStreamReader(is);

        br=new BufferedReader(isr);

         String info=null;

        if((info=br.readLine())!=null){

            System.out.println("我是客户端,服务器说:"+info);

        }

        //用来区分不同次的客户端发送和服务器响应的命令

        System.out.println("**************************");

        //关闭输入输出流(关闭资源)

        br.close();

        isr.close();

        is.close();

        oos.close();

        os.close();

        socket.close();

}

} catch (IOException e) {

    e.printStackTrace();

}

}        

}    

提示性语句:

package com.socket;

import java.util.Scanner;

public class ClientTips {

static boolean flag=false;

static String a;

public ClientTips(){

//打印客户端界面

    System.out.println("*********************************");

    System.out.println("注册信息:1");

    System.out.println("登陆账号:2");

    System.out.println("上传文件(只有在登陆之后才能传文件!):3");

    System.out.println("*********************************");

}

public String[] tipChoose(int n){

String[] userinfo=new String[3];

String b,c,e;

Scanner sc=new Scanner(System.in);

/*

* 注册时的提示操作

*/

if(n==1){

System.out.print("请输入要注册的账号名:");

a=sc.nextLine();

System.out.print("请输入密码:");

b=sc.nextLine();

System.out.print("请确认密码:");

c=sc.nextLine();

if(!c.equals(b)){//这里不能用c!=b判断,c!=b不仅对比了字符串,还对比了存储地址

    System.out.println("密码不一致!注册失败,需要重新注册请输入:1");

    //如果注册失败,userinfo返回的为空数组

    userinfo[0]=null;

    userinfo[1]=null;

    userinfo[2]=null;

}

else{

    userinfo[0]="1";

    userinfo[1]=a;

    userinfo[2]=b;

}

}

/*

* 登陆时提示信息操作

*/

if(n==2){

System.out.print("请输入要登陆的账号名:");

a=sc.nextLine();

System.out.print("请输入密码:");

b=sc.nextLine();

userinfo[0]="2";

userinfo[1]=a;

userinfo[2]=b;

flag=true;

}

/*

* 传送文件相关文件

*/

if(n==3){

if(flag!=true){

    System.out.println("请先登录账号!请输入2");

    //如果没有登陆操作,输入3时先返回空数组

    userinfo[0]=null;

    userinfo[1]=null;

    userinfo[2]=null;

}

else{

    System.out.println("请输入文件的路径:");

    e=sc.nextLine();

    userinfo[0]="3";

    userinfo[1]=a;

    userinfo[2]=e;

}

}

return userinfo;    

}

}

文章最后发布于: 2018-06-30 11:12:03

相关阅读

Socket编程(C语言实现)——基于TCP协议,基于UDP协议(多线

Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该

socket技术详解(看清socket编程)

socket编程是网络常用的编程,我们通过在网络中创建socket关键字来实现网络间的通信,通过收集大量的资料,通过这一章节,充分的了解sock

Socket网络编程——(一)

什么是Socket 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。端点由IP地址和端口号共同

详解Java Socket的工作机制

Socket的来龙去脉 下面的分析主要是参阅了计算机网络(谢希仁第7版)进行总结的,从系统调用—>应用编程接口API—>套接字接口来分析So

Socket编程之TCP实例(附C/C++代码详解)

说明: 主要分步骤给出Windows平台下socket编程的一个TCP实例;使用WINDOWS下网络编程规范Winsock完成网络通信; 对程序各部分细节进行

分享到:

栏目导航

推荐阅读

热门阅读