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

Java之------socket系列(四)TCP

2016-05-10 12:44 627 查看
TCP传输

TCP Socket:IP地址和端口,套接字

Socket和ServerSocket

建立客户端和服务器端

建立连接后,通过Socket中的IO流进行数据的传输

关闭socket

同样,客户端与服务器端是两个独立的应用程序。



TCP传输编程

☆基本思路(客户端)

客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。

连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(), getOutputStream()获取即可。

与服务端通讯结束后,关闭Socket。

☆基本思路(服务器端)

服务端需要明确它要处理的数据是从哪个端口进入的。

当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。

当该客户端访问结束,关闭该客户端。

基于TCP的Socket通信流程



TCP传输编程

☆客户端

通过Socket建立对象并指定要连接的服务端主机以及端口。

Socket s = new Socket(“192.168.1.1”,9999);
OutputStream out = s.getOutputStream();
out.write(“hello”.getBytes());
s.close();
☆服务器端

建立服务端需要监听一个端口

ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();


具体实例:

客户端:

package cn.hncu.tcp;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class MyClientSocket {

public static void main(String[] args) {
try {
Socket s = new Socket("192.168.31.168",8888);//和服务器进行三次握手,若失败则出异常,否则返回和对方通讯的socket
//发送数据
OutputStream out = s.getOutputStream();
out.write( "Hello Server!".getBytes() );

//接收服务器的应答数据
InputStream in = s.getInputStream();
DataInputStream din = new DataInputStream(in);
System.out.println( din.readUTF() );
s.close();
din.close();
} catch (IOException e) {
e.printStackTrace();
}

}

}
服务器端:

package cn.hncu.tcp;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServerSocket {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(8888);
while(true){
System.out.println("准备接收一个连接....");
Socket s = server.accept();//阻塞式方法
System.out.println("收到一个连接....");

//读--从客户端读数据
InputStream in = s.getInputStream();
byte buf[] = new byte[20];
in.read(buf);
System.out.println("read info:"+ new String(buf) );

//写--应答客户端--向他写数据
OutputStream out = s.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
dout.writeUTF("Hello,"+s.getInetAddress()+"port#"+s.getPort()+"\nbye!");
dout.close();
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}

}

}


☆TCP传输最容易出现的问题

客户端连接上服务端,两端都在等待,没有任何数据传输。

通过例程分析:

因为read方法或者readLine方法是阻塞式。

解决办法:

自定义结束标记

使用shutdownInput,shutdownOutput方法。

编程练习

1、上传文本文件(本例只考虑只有一个客户端)

读取一个本地文本文件,将数据发送到服务端,服务器端对数据进行存储。 存储完毕后,给客户端一个提示。

客户端:

package cn.hncu.tcp.upload;

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

public class UploadTextClient {

public static void main(String[] args) {
try {
Socket s = new Socket("192.168.1.106",10005);
//思路:把本地文件的数据读取出来通过 s.getOutputStream()获得的out对象发送出去
BufferedReader br = new BufferedReader(new FileReader("tempfiles\\client.txt"));

OutputStream out = s.getOutputStream();//这里的输出流 对应的是服务器端的输入流
PrintWriter pw = new PrintWriter(out, true);//建议不要用BufferedWriter
String line = null;
while( (line=br.readLine())!=null){
pw.println(line);
}
//给服务器发送结束标记---上传结束,要加结束标记,否则服务器在数据接收完毕时再调用read()或readLine()时会出异常
//法1:pw.println("over#$@#@$");//不能出现文件中存在的结束关键字---搞特殊一点
//法2---建议采用该种方式---由socket内部来指定结束标记
s.shutdownOutput();
br.close();

//读取服务器上传成功的反馈消息
BufferedReader br2 = new BufferedReader(new InputStreamReader(s.getInputStream()));
String info = br2.readLine();
System.out.println("server应答:"+info);
s.close();
br2.close();

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

}

}
服务器端:

package cn.hncu.tcp.upload;

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

public class UploadTextServer {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(10005);
Socket s = server.accept();
System.out.println(s.getInetAddress().getHostAddress()+"...connected");

//读取客户端上传过来的文本文件
//源 ---socket(字节流)---额外:需要转换成字符流  ,缓存流
BufferedReader br = new BufferedReader( new InputStreamReader(s.getInputStream()));
//目的 ---硬盘字符流 FileWriter---额外:打印流
PrintWriter pw = new PrintWriter(new FileWriter("tempfiles\\server.txt"), true);
String line=null;
while ( (line=br.readLine())!=null ){
//				if("over#$@#@$".equals(line)){
//					break;
//				}
pw.println(line);
}
pw.close();
//上传成功,给客户端一个提示信息
PrintWriter pw2 = new PrintWriter(s.getOutputStream(),true);
pw2.println("上传成功!");
s.close();
server.close();
pw2.close();

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

}
}


2、上传图片文件(本例考虑有多个客户端)

客户端需求:把一个图片文件发送到服务端并读取回馈信息。要求判断文件是否存在及格式是否为jpg或gif并要求文件小于2M。

服务端需求:接收客户端发送过来的图片数据。进行存储后,回馈一个 上传成功字样。支持多用户的并发访问。
客户端:

package cn.hncu.tcp.upload;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class UploadPicClient {

public static void main(String[] args) {

//卫条件
if(args.length!=1){
System.out.println("请指定文件");
return;
}
File file = new File(args[0]);
if(!(file.exists() && file.isFile()) ){
System.out.println("上传的文件不存在");
return;
}
if( !(file.getName().endsWith(".jpg")||file.getName().endsWith(".gif"))  ){
System.out.println("文件扩展名必须是jpg或gif");
return;
}
if( file.length()>=1024*1024*2){
System.out.println("文件过大,不应超过2M,请重新上传!");
return;
}

//上传
try {
Socket s = new Socket("127.0.0.1",9999);
FileInputStream fin = new FileInputStream(file);
OutputStream out = s.getOutputStream();
byte buf[] = new byte[1024];
int len=0;
while( (len=fin.read(buf))!=-1){
out.write(buf, 0, len);
}
s.shutdownOutput();//告诉服务器,文件上传完毕

//读取服务器的回馈信息
InputStream in = s.getInputStream();
byte buf2[] = new byte[1024];
int len2 = in.read(buf2);
String info = new String(buf2,0,len2);
System.out.println(info);

//关流
fin.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器端:

package cn.hncu.tcp.upload;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class UploadPicServer {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(9999);
while(true){
Socket s = server.accept();//只负责和客户端进行握手
new Thread( new UploadThread(s)).start();
}
//server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

class UploadThread implements Runnable{
private Socket s;
public UploadThread(Socket s) {
this.s = s;
}

@Override
public void run() {
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");

try {
InputStream in = s.getInputStream();
File dir = new File("d:\\mypic");
if(!dir.exists()){
dir.mkdir();
}
int count=1;
File file = new File(dir,ip+".jpg");
while(file.exists()){
file = new File(dir,ip+"("+(count++) +")"+".jpg"); //带号的文件名
}
FileOutputStream fout = new FileOutputStream(file);

//从socket流中读取数据,存储到本地文件。相当于对拷
byte buf[] = new byte[1024];
int len=0;
while( (len=in.read(buf))!=-1){
fout.write(buf, 0, len);
}
//图片接收完毕

//向客户端发送回馈信息
OutputStream out = s.getOutputStream();
out.write( "上传成功".getBytes() );
fout.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: