网络编程之TCP

        TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接,所以也可以说TCP是一种可靠的网络协议,他在通信的两端各建立一个Socket,从而在通信两端形成虚拟的网络链路,
       java中对TCP协议的网络通信提供了良好的封装,java使用Socket对象来代表两端的通信端,并通过Socket获取IO流来进行网络通信,在TCP通信协议里,定义了两个网络设备,一个是服务器端,一个是客户端,单独看可以分别对应于UDP的发送端和接收端,但是因为TCP是有连接的协议,下面按照服务器和客户端分别介绍java实现方法;

      服务器端:
                ServerSocket ss=new ServerSocket(9999);    //建立服务器监听端,
		Socket socket=ss.accept();  //得到一个客户端连接请求并建立socket连接,注意accept()是一个阻塞式方法,
      客户端:
                Socket s=new Socket("127.0.0.1",9999);//建立socket端,并与指定的IP地址和端口号连接,
         
      建立了socket连接以后剩下的就是IO操作了,在TCP中,首先要通过socket对象来获取输入输出流,
             例如:BufferedReader bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));//将socket输入流封装为字符流,获取这个输入流的目的是为了读取网络中的数据。
                      PrintWriter pr=new PrintWriter(socket.getOutputStream(),true);//获取socket输出流,用于向网络中特定的连接对象发送数据,这里使用了PrintWriter打印装饰流,
          这里特别要提一下,在网络通信的流操作中经常有很多莫名的等待,多是因为阻塞方法造成的,例如在 Tcp 复制文本中,客户端读文件可以自己跳出循环,而服务器端基于 BufferedReader中的readline方法,这个方法是要读到换行符才返回数据,如果发送端没有发送换行符,则接收端会一直等待 读文件则因找不到结束标志,而一直等待,

    实现结束标记有多种方法,可以以时间戳来标记,通过DataInputstream实现,也或者通过判断读取的文件长度,是否为0

当然,最合理的方式,是通过socket自带的一个方法, void shutdownOutput()  禁用此套接字的输出流。 

         该方法用于向接收端写一个结束标志。例如,所以在写发送端的时候,需要在发送完成时加上socket的一个发送结束标志的方法,socket.shutdownOutput();



例子,通过tcp实现一个文本的复制;

 服务器端代码:

package com.io;

import java.net.*;

import java.io.*;

public class TCPCOPYS {

/**

* @param args

* @throws IOException 

*/

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

ServerSocket ss=new ServerSocket(9999);  //建立一个服务器端

Socket socket=ss.accept();   //获取一个socket连接对象

String ip=socket.getInetAddress().getHostAddress();  //得到连接对象的IP地址

System.out.println(ip+"is connet");

BufferedReader bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));//获得socket的输入流对象

BufferedWriter bfw=new BufferedWriter(new FileWriter("F:\\IOTest\\a\\source-copy320.txt"));//建立本地的输出流对象

PrintWriter pr=new PrintWriter(socket.getOutputStream(),true);//获得socket的输出流对象

String st=null;

while((st=bfr.readLine())!=null){

//System.out.println("client:"+st);

bfw.write(st);    //将获得的输入流数据写到本地输出流的缓冲区中

bfw.newLine();//写一个换行符

bfw.flush();//刷新输出流,将数据写到本地文件中;

//if(st.length()==0)break;

}

System.out.println("复制完了");

pr.println(" 复制成功");  //向客户端发送一个信息

socket.close();  关闭socket连接

}


}


客户端代码:

package com.io;

import java.net.*;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.FileReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;


public class TCPCOPYC {

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

 Socket s=new Socket("127.0.0.1",9999);    //创建一个socket连接,指定IP地址和端口号,


  BufferedReader bfr=new BufferedReader(new FileReader("F:\\IOTest\\a\\source.txt"));//创建一个读取流对象

   BufferedReader bfrs=new BufferedReader(new InputStreamReader(s.getInputStream()));//创建一个输入流对象,并获得socket的输入流;

//   BufferedWriter bfw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

   PrintWriter pr=new PrintWriter(s.getOutputStream(),true);//创建个输出打印流对象,并获得socket的输出流对象,

  String st=null;   

 

           while((st=bfr.readLine())!=null){

          pr.println(st);  //将本地输入流获取的数据发送至服务器端

  }   

            s.shutdownOutput(); //发送完毕后发送一个停止输出流的标志

              System.out.println("发送完了");

             System.out.println("sever:"+bfrs.readLine()); //显示服务器返回的信息。

             s.close();


  }


}



例2:通过多线程技术,实现一个服务器端,并且通过多个客户端想服务器端发送图片,服务器将这些图片以客户端的IP地址命名并保存到本地硬盘。

                                   服务器端  :                       

                                                                       package com.io;

import java.io.*;

import java.net.ServerSocket;

import java.net.Socket


public class TCPCOPYPICS {

/**

* @param args

* @throws IOException 

*/

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

ServerSocket  ss=new ServerSocket(9009); //创建一个服务器端

        while(true){   

        Socket s=ss.accept();          //获取一个socket连接

        new Thread(new picTread(s) ).start(); //为socket分配一个线程

        } 

}

}


class picTread implements Runnable{   //线程类,定义线程方法

public Socket s;

public picTread(Socket s){  //传递socket对象

this.s=s;

}

public void run() {

String ip=s.getInetAddress().getHostAddress();//获取socket连接对象的IP

int i=0;  //定义一个文件计数器

try {

       System.out.println(ip);

       BufferedInputStream bfr=new BufferedInputStream(s.getInputStream());

       PrintWriter prouts=new PrintWriter(s.getOutputStream(),true);

       File file=new File("F:\\IOTest\\p\\"+ip+"("+i+")"+"p-321.jpg");   //创建一个文件对象

       while(file.exists()){file=new File("F:\\IOTest\\p\\"+ip+"("+i+++")"+"p-321.jpg");}//当目录下有file存在是,重新创建一个新的file对象,命名方式通过文件计数器来区分,

       BufferedOutputStream bfw=new BufferedOutputStream(new FileOutputStream(file));

       byte[]buf=new byte[1024];

       int len=0;

       while((len=bfr.read(buf))!=-1){

      bfw.write(buf, 0, len);  //向指定文件中写数据

       }

       prouts.println("复制成功");

} catch (Exception e) {

throw new RuntimeException(ip+"上传失败");

}

}

}


客户端:

 package com.io;

import java.net.*;

import java.io.*;

public class TCPCOPYPICC {

/**

* @param args

* @throws IOException 

* @throws UnknownHostException 

*/

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

// TODO Auto-generated method stub

Socket s=new Socket("127.0.0.1",9009);  //创建一个socket连接,

        BufferedInputStream bfr=new BufferedInputStream(new FileInputStream("F:\\IOTest\\p\\TI.jpg"));

BufferedOutputStream bfo=new BufferedOutputStream(s.getOutputStream());

BufferedReader bfrs=new BufferedReader(new InputStreamReader(s.getInputStream()));

byte []buf=new byte[1024];

int len=0;

while((len=bfr.read(buf))!=-1){

            bfo.write(buf,0,len);

}

   System.out.println("发送完了");

   s.shutdownOutput();  //向服务器端发送结束标志

   System.out.println("sever:"+bfrs.readLine());

}

}


             小结:因为TCP通信是基于连接的,在应用的时候,首先应该先建立服务器,让服务器侦听一个指定端口,然后通过客户端去连接服务器,在涉及到外网的时候,我们填写的服务器端IP地址就是服务器的外网IP,在服务器所在局域网内,还需要通过路由器做端口映射;当非正常关闭客户端的时候,服务器端会因为socket异常而退出,另外多线程是网络服务器的基本思路,应该重点掌握。

                        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值