您的位置:首页 > 理论基础 > 计算机网络

java基础--网络编程之TCP

2012-11-22 21:00 330 查看
Tcp传输应用

应用一、实现TCP传输的客户端和服务端的简单互访

需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息。

客户端:
1,建立socket服务。指定要连接主机和端口。

2,获取socket流中的输出流。将数据写到该流中。通过网络发送给服务端。

3,获取socket流中的输入流,将服务端反馈的数据获取到,并打印。

4,关闭客户端资源。

关键在于通过getOutputStream()和getInputStream()获取读写流

package cn.xushuai.Test;
import java.io.*;
import java.net.*;

class TcpClient2 {
public static void main(String[] args)throws Exception {
Socket s = new Socket("127.0.0.1",10007);

OutputStream out = s.getOutputStream();
out.write("服务端,你好".getBytes());

InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));

s.close();
}
}


服务端:

1,建立服务端的socket服务。ServerSocket();

并监听一个端口。

2,获取连接过来的客户端对象。

通过ServerSokcet的 accept方法。没有连接就会等,所以这个方法阻塞式的。

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

并打印在控制台。

4,关闭服务端。(可选)

class TcpServer2{
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10007);
Socket s = ss.accept();

String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");
InputStream in = s.getInputStream();

byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
OutputStream out = s.getOutputStream();

//Thread.sleep(10000);
out.write("哥们收到,你也好".getBytes());
s.close();
ss.close();
}
}


应用二、编写一个文本转换服务器

分析:

既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。

都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。

客户端给服务端发送文本,服务单会将文本转成大写在返回给客户端。

而且客户度可以不断的进行文本转换。当客户端输入over时,转换结束。

该例子出现的问题:

现象:客户端和服务端都在莫名的等待。

原因:客户端和服务端都有阻塞式方法readLine(),这些方法没有读到结束标记,那么就一直等,而导致两端,都在等待。

为了书写简化,可以使用打印流,自动刷新与换行。

客户端:

源:键盘录入。
目的:网络设备,网络输出流。

而且操作的是文本数据,可以选择字符流。

步骤

1,建立服务。

2,获取键盘录入。

3,将数据发给服务端。

4,获取服务端返回的大写数据。

5,结束,关资源。

import java.io.*;
import java.net.*;

class  TransClient{
public static void main(String[] args) throws Exception{
Socket s = new Socket("127.0.0.1",10010);

//定义读取键盘数据的流对象。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));

//定义目的,将数据写入到socket输出流。发给服务端。
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);

//定义一个socket读取流,读取服务端返回的大写信息。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));

String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;

out.println(line);
//bufOut.write(line);		//将键盘录入写给服务端
//bufOut.newLine();		//写入换行符,让服务端readLine识别,否则服务端一直阻塞
//bufOut.flush();

String str =bufIn.readLine();	//读取服务端反馈的信息
System.out.println("server:"+str);
}
bufr.close();
s.close();	//会在socket流中加入结束标记,服务端会识别,所以在客户端结束//后,服务端也就结束了
}
}


服务端:
源:socket读取流。目的:socket输出流。

都是文本,装饰。

class  TransServer{
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10010);
Socket s = ss.accept();

String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");

//读取socket读取流中的数据。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));

//目的:socket输出流,将大写数据写入到socket输出流,并发送给客户端。
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

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

String line = null;
while((line=bufIn.readLine())!=null) {//只有读到换行符才进行下次读取,否则一直阻塞

System.out.println(line);
out.println(line.toUpperCase());
// bufOut.write(line.toUpperCase());
// bufOut.newLine();	     //写入换行符,让客户端的readLine识别
//	bufOut.flush();		     //必须刷新缓冲区
}
s.close();
ss.close();

}
}


网络编程需注意的问题:

1、 读写流是否能读取到结束标记

读写流中的阻塞式方法read和write要刷新缓冲区和加入结束标记,readLine要进行换行操作,以便加入结束标记。

可以使用printReaader 和printWriter 来替代,有自动刷新和换行,这样简化了书写。

2、服务端是否能够接收到客户端结束标记

客户端上传完文件之后,需要向服务端提供一个结束标记。否则虽然客户端的循环结束,还会向下执行,又开始读取服务端反馈的信息,

但是,服务端还没有反馈时,客户端就向下执行了,以至于两端都阻塞。

3、自定义标签可能出现在文本中,所以导致文本文件还读取完就结束,所以使用通用的解决方案:shutDownOutput( )

服务端的服务线程:使用多线程实现并发访问,只需将共享的代码放在run方法中即可。

应用三、TCP上传文件

客户端:上传文本,并等待服务端的反馈信息

import java.io.*;
import java.net.*;

//客户端上传文本,并等待服务端的反馈信息
class  TextClient{
public static void main(String[] args) throws Exception{
Socket s = new Socket("192.168.1.254",10006);

//生成一个缓冲读取流,同时关联一个文件
BufferedReader bufr =
new BufferedReader(new FileReader("IPDemo.java"));

//使用打印流,实现自动刷新和换行
PrintWriter out = new PrintWriter(s.getOutputStream(),true);

//循环向服务端发送数据
String line = null;
while((line=bufr.readLine())!=null){
out.println(line);
}

//自定义结束标记
//缺陷:文件中可能出现自定义标记符
//out.println("over");

s.shutdownOutput();//关闭客户端的输出流,相当于,给流中加入一个结束标记-1.
//服务端readLine()到-1时就结束读取,接着执行下面的代码

//得到一个缓冲读取流,用于获取服务端的反馈信息
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));

String str = bufIn.readLine();
System.out.println(str);

bufr.close();
s.close();
}
}


服务端:收到上传的文件,保存到本地server.txt中,并反馈给客户端上传成功的信息

class  TextServer{
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10006);

Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");

//得到一个缓冲读取流对象,用于读取客户端传过来的数据
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));

//得到一个打印流,用于写入读取的文件
PrintWriter out  = new PrintWriter(new FileWriter("server.txt"),true);

String line = null;

//读取数据,直到读到结束标记
while((line=bufIn.readLine())!=null){
//if("over".equals(line))
//break;
out.println(line);
}

//获取一个写入流,向客户端反馈上传结果信息
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println("上传成功");

out.close();
s.close();
ss.close();
}
}


应用四、客户端并发上传图片

客户端

1,服务端点。

2,读取客户端已有的图片数据。

3,通过socket 输出流将数据发给服务端。

4,读取服务端反馈信息。

5,关闭。

import java.io.*;
import java.net.*;
class  PicClient{
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("图片格式错误,请重新选择");
return ;
}

//限定文件的大小
if(file.length()>1024*1024*5){
System.out.println("文件过大,没安好心");
return ;
}

Socket s = new Socket("192.168.1.254",10007);

FileInputStream fis = new FileInputStream(file);
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 PicThread implements Runnable{

private Socket s;
PicThread(Socket s){
this.s = s;
}
public void run(){

int count = 1;//定义为局部变量,让每个客户都拥有一个变量,而不是共享(成员变量)

String ip  = s.getInetAddress().getHostAddress();
try{
System.out.println(ip+"....connected");
InputStream in = s.getInputStream();

File dir =  new File("d:\\pic");
File file = new File(dir,ip+"("+(count)+")"+".jpg");

//循环判断文件是否已经存在
while(file.exists())
file = new File(dir,ip+"("+(count++)+")"+".jpg");

FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len=in.read(buf))!=-1){
fos.write(buf,0,len);
}

OutputStream out = s.getOutputStream();
out.write("上传成功".getBytes());

fos.close();
s.close();
}
catch (Exception e){
throw new RuntimeException(ip+"上传失败");
}
}


服务端:为每个要上传文件的客户new一个线程提供服务
class  PicServer{
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10007);

while(true){
Socket s = ss.accept();
new Thread(new PicThread(s)).start();
}
//ss.close();
}
}


应用五、登陆服务

[b]需求:客户端向服务端发送用户请求登陆,服务端通过验证,返回”欢迎光临“,未通过”用户不存在“[/b]

客户端通过键盘录入用户名。

服务端对这个用户名进行校验。

如果该用户存在,在服务端显示xxx,已登陆,并在客户端显示 xxx,欢迎光临。

如果该用户存在,在服务端显示xxx,尝试登陆,并在客户端显示 xxx,该用户不存在。

最多就登录三次。

客户端登陆代码:
import java.io.*;
import java.net.*;

class  LoginClient{
public static void main(String[] args) throws Exception{
Socket s = new Socket("192.168.1.254",10008);

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

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

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

for(int x=0; x<3; x++){
String line = bufr.readLine();
if(line==null)
break;
out.println(line);

String info = bufIn.readLine();
System.out.println("info:"+info);
if(info.contains("欢迎"))
break;
}
bufr.close();
s.close();
}
}



服务端提供登陆服务的线程

class UserThreadimplements Runnable{
private Socket s;
UserThread(Socket s){
this.s = s;
}
public void run(){
String ip =s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");
try{
for(int x=0; x<3;x++){
BufferedReaderbufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
String name= bufIn.readLine();
//避免按Ctrl+C 结束时,readLine读取为null,不满足,而进行3次循环的判断,所以这里对null进行判断
if(name==null)
break;

BufferedReaderbufr = new BufferedReader(new FileReader("user.txt"));
PrintWriterout = new PrintWriter(s.getOutputStream(),true);
String line= null;
booleanflag = false;          //定义标记,判断是否获取到用户
while((line=bufr.readLine())!=null)     {//循环读取判断,用户是否存在
if(line.equals(name)){
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){
throw newRuntimeException(ip+"校验失败");
}
}
}


服务端:不断循环以产生新的线程为客户端提供服务
class  LoginServer{
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10008);

//通过循环,不断提供服务
while(true){
Socket s = ss.accept();
new Thread(new UserThread(s)).start();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐