网络编程之TCP

1 TCP 的特点

1)建立连接,形成传输的数据通道

2)在连接中进行大数据量传输

3)通过三次握手完成连接,是可靠的协议

4)必须连接,效率稍低

2TCP分客户端和服务端

1)客户端对应的对象:Socket

2)服务端对应的对象:ServerSocket

2.1、客户端

  通过查阅Socket对象,发现在该对象建立时,就可以去连接指定主机,因为TCP时面向连接的,所以在建立Socket服务时,就要有服务端存在,并连接成功,形成通路后,在该通道进行数据传输。

思路:

1)创建Socket服务,并制定要连接的主机和端口号

一旦Socket对象创建,就形成Socket流,包含输入流和输出流,Socket有以下方法:

InputStream getInputStream():返回次套接字的输入流

OutputStream getOutputStream():返回套接字的输出流

2)为了发送数据,应获取Socket流中俄输出流

3)关闭资源

例:

package com.heima.net;

import java.io.OutputStream;

import java.net.InetAddress;

import java.net.Socket;

public class TCPDemo1 {

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

//创建Socket服务

Socket s=new Socket(InetAddress.getByName("localhost"),10001);

//发送数据,应获取Socket流中输出流

        OutputStream out=s.getOutputStream();

        //写数据

        out.write("hello heima".getBytes());

        //关闭资源

        s.close();

}

}

2.2、服务端ServerSocket

思路

1)建立服务端Socket服务,ServerSocket,并监听一个端口

2)获取连接过来的客户端对象,通过ServerSocketaccept方法,没有连接就会等,是阻塞式的

3)客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据

4)关闭流

代码如下:

package com.heima.net;

import java.io.InputStream;

import java.net.ServerSocket;

import java.net.Socket;

public class TCPServer {

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

//创建ServerSocket服务

ServerSocket ss=new ServerSocket(10001);

//通过accept方法获取连接过来的客户端对象

Socket s=ss.accept();

//获取ip

String ip=s.getInetAddress().getHostAddress();

//获取客户端发过来的数据

InputStream in=s.getInputStream();

//读取数据到控制台

byte[]buf=new byte[1024];

int len=0;

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

System.out.println(new String(buf,0,len));

}

//关闭资源

        s.close();

}

}

应用一:建立一个文本转换服务器

  客户端给服务器发送文本,服务端会将文本转换成大写在返回该客户端,而且客户端能够不断的进行文本转换。

package com.heima.net;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;

public class TCPDemo1 {

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

}

}

//客户端

class Send{

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

//创建一个Socket服务

Socket s=new Socket(InetAddress.getByName("localhost"),10009);

//创建一个键盘输入流

BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));

//创建一个Socket输出流

BufferedWriter bufOut=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

//创建一个Socket输入流

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

//创建一个字符串

String line=null;

while((line=bufr.readLine())!=null){

//把从键盘读取的数据写到Socket输出流中

bufOut.write(line);

//换行

bufOut.newLine();

//刷新

bufOut.flush();

//同时需要接受服务端反馈回来的信息

String str=bufIn.readLine();

//打印出来

System.out.println("server说:"+str);

}

//关闭资源

bufr.close();

s.close();

}

}

//服务端

class Receive{

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

//创建一个ServerSocket服务

ServerSocket ss=new ServerSocket(10009);

//获取客户端的Socket服务

Socket s=ss.accept();

//创建一个Socket接受流

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

//创建一个Socket输出流

BufferedWriter bufOut=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

//定义一个字符串

String line=null;

while((line=bufIn.readLine())!=null){

//把读取的数据转换成大些写出去

bufOut.write(line.toUpperCase());

//换行

bufOut.newLine();

//刷新

bufOut.flush();

}

//关闭资源

s.close();

}

}

注:如果不进行换行和刷新,客户端会停留等待,因为readLine(),只有回车符时才结束,二newLine表示一行的结束。

应用二:复制文件

package com.heima.net;

import java.io.BufferedReader;

import java.io.File;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;

public class TCPDemo2 {

}

//客户端

class Send1{

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

//创建Socket服务

Socket s=new Socket(InetAddress.getByName("localhost"),10007);

//创建输入流

BufferedReader bufr=new BufferedReader(

 new FileReader(new File("d:"+File.separator+"clint.txt")));

//创建Socket输出流

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

//创建一个字符串

String line=null;

while((line=bufr.readLine())!=null){

//把从文件中读取的数据写到Socket输出流中

out.println(line);

}

//关闭客户端的输出流,相当于给流种加入一个结束标记

s.shutdownOutput();

//读取服务端反馈回来的数据

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

//读取服务端反馈回来的数据

String str=in.readLine();

System.out.println("服务器说:"+str);

//关闭资源

bufr.close();

s.close();

}

}

//服务端

class Receive1{

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

//创建ServerSocket服务

ServerSocket ss=new ServerSocket(10007);

//创建一个客户端的Socket服务

Socket s=ss.accept();

//读取Socket中数据

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

//把读取的数据写到文件中

PrintWriter pw=new PrintWriter(new FileWriter(new File("d:"+File.separator+"server.txt")),true);

//定义一个字符串

String line=null;

while((line=bufIn.readLine())!=null){

//把数据读取到指定的文件中

pw.println(line);

}

//向客户端反馈一些信息

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

out.println("上传成功");

//关闭资源

s.close();

pw.close();

}

}

应用三:TCP客户端并发上传图片

  问题:怎样让多个客户端同时并发访问服务端?

  最好就是将每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求。

如何定义线程呢?

  只要明确了每个客户端要在服务端执行的代码即可,将该代码存入run方法中。

定义的线程部分

//定义一个线程

class PicThread implements Runnable{

    //此线程需要接受一个客户端

private Socket s;

public PicThread(Socket s){

this.s=s;

}

public void run() {

//获取IP

String ip=s.getInetAddress().getHostAddress();

FileOutputStream fis=null;

try{

System.out.println(ip+"连接成功");

//Socket服务中读取客户端传过来的数据,因为时图片,所有用字节流

InputStream in=s.getInputStream();

//创建一个文件对象

File file=new File(System.currentTimeMillis()+".jpg");

//把读取到得图片数据写到指定的地方

fis=new FileOutputStream(file);

//定义一个字节数组

byte[]buf=new byte[1024];

//定义一个整型

int len=0;

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

//把读取的数据写到指定的文件中

fis.write(buf,0,len);

}

//当上传成功后给客户端反馈一些信息

OutputStream out=s.getOutputStream();

//写一些数据

out.write("上传成功".getBytes());

}catch(Exception e){

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

}finally{

try{

if(fis!=null)

fis.close();

if(s!=null)

s.close();

}catch(Exception e){

e.printStackTrace();

}

}

}

}

客户端:

//定义一个客户端

class Client{

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

//接收一个参数

if(args.length!=1){

System.out.println("请选择一个jpg格式的图片");

return;

}

//创建一个文件

File file=new File(args[0]);

//判断是此文件是否存在且为文件

if(!(file.exists()&&file.isFile())){

System.out.println("此文件不存在或者不是一个文件");

return;

}

//判断是否为jpg格式的

if(!file.getName().endsWith("jpg")){

System.out.println("只能上传格式为jpg的文件");

return;

}

//判断文件的大小

if(file.length()>1024*1024*3){

System.out.println("文件超出了最大的上传大小");

return;

}

//如果上面的步骤都通过了,可以上传文件了,首先建立Socket服务

Socket s=new Socket(InetAddress.getByName("localhost"),10009);

//创建一个读取流

FileInputStream fis=new FileInputStream(file);

//创建一个Socket输出流,把读取到得数据输出到服务端

OutputStream out=s.getOutputStream();

//定义一个字节数组

byte[]buf=new byte[1024];

int len=0;

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

//把读取到的数据写入到流中

out.write(buf, 0, len);

}

//数据写完后,显示一个标记

s.shutdownOutput();

//接受服务端反馈的信息

InputStream in=s.getInputStream();

//创建一个字节数组,把接受的数据先存放到数组中

byte[]bufIn=new byte[1024];

int num=in.read(bufIn);

System.out.println(new String(bufIn,0,num));

//关闭资源

fis.close();

s.close();

}

}

服务端:

//定义一个服务端

class Server{

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

//创建一个ServerSocket服务

ServerSocket ss=new ServerSocket(10009);

//不断的接受客户端

while(true){

//接受Socket客户端

Socket s=ss.accept();

//创建一个线程

new Thread(new PicThread(s)).start();

}

}

}

应用四:客户端的并发登录

因为同时可能有很多人登录,所以用多线程

//定义一个线程

class UserThread implements Runnable{

//接收一个Socket服务

private Socket s=null;

public UserThread(Socket s){

this.s=s;

}

@Override

public void run() {

//获取ip地址

String ip=s.getInetAddress().getHostAddress();

try {

for(int i=0;i<3;i++){

//读取从客户端读取的数据

BufferedReader bufrIn=new BufferedReader(

                                       new InputStreamReader(s.getInputStream()));

 //创建一个字符串用于存储读取的数据

 String name=bufrIn.readLine();

 //从文件中获取数据

 BufferedReader bufr=new BufferedReader(new FileReader(

                                        new File("d:"+File.separator+"user.txt")));

 //定义一个字符串

 String line=null;

 //创建一个输出流,向客户端反馈信息

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

 //创建一个标记

 boolean flag=false;

 while((line=bufr.readLine())!=null){

 //判断从文件中读取的数据是否包含客户端传过来的

 if(line.equals(name)){//包含则把标记改为true

 flag=true;

 break;

 }

 }

 if(flag){

 System.out.println(name+"登陆成功");

 //向客户端反馈信息

 out.println(name+"欢迎登录");

 break;

 }else{

 System.out.println(name+"尝试登录");

 out.println(name+"登录失败");

 }

 }

s.close();   

catch (Exception e) {

e.printStackTrace();

throw new RuntimeException(ip+"校验失败");

}

}

}

客户端:登录三次,当三次失败是退出程序

//定义一个客户端

class LoginClient{

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

//创建一个Socket服务

Socket s=new Socket(InetAddress.getByName("localhost"),10008);

//从键盘输入数据

BufferedReader bufr=new BufferedReader(

                                       new InputStreamReader(System.in));

//把从键盘接收的数据输出到服务端

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

//接收服务端反馈回来的信息

BufferedReader in=new BufferedReader(

                                      new InputStreamReader(s.getInputStream()));

//定义一个字符串

for(int i=0;i<3;i++){

String line=bufr.readLine();

if(line==null)

break;

out.println(line);

String str=in.readLine();

//打印接收的服务端反馈

System.out.println(str);

if(str.contains("欢迎"))

break;

}

//关闭资源

bufr.close();

s.close();

}

}

服务端:

//定义一个服务端

class LoginServer{

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

//创建ServerSocket服务

ServerSocket ss=new ServerSocket(10008);

//循环接收客户端

while(true){

//接收一个客户端Socket服务

Socket s=ss.accept();

//开启一个线程

new Thread(new UserThread(s)).start();

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值