网络间的通信方式分为两种,一种是面向无连接(UDP),一种是面向连接(TCP)。而网络间的通信底层都遵循一种通信机制,也叫Socket,我把它理解为是一种端口,因为每个应用程序运行都有指定的端口进行数据的传输。所以在用前面的两种通信方式时都要指定相同的端口。UDP的特点:将数据及源和目的封装到数据包中,不需要建立连接;每个数据包的大小限制在64K内,如果数据包大于64K就会自动的将其分割成小的包;因为是无连接,所以是不可靠协议,但是传输速度相对较快。TCP的特点:需建立连接,形成传输数据的通道,并通过三次握手来确定通道的安全,因此传输效率稍低。
UDP传输的操作步骤:
发送端:
1,建立udp的socket服务,创建对象时如果没有明确端口,系统会自动分配一个未被使用的端口。
2,明确要发送的具体数据。
3,将数据封装成了数据包。
4,用socket服务的send方法将数据包发送出去。
5,关闭资源。
class UdpSend{
public static voidmain(String[] args)throws Exception {
1,建立udp的socket服务。
DatagramSocket ds = newDatagramSocket();//指定发送端口,不指定系统会随机分配。
2,明确要发送的具体数据。
//因为底层传输的是字节流,所以要把传送的内容变成字节数组再进行传输。
byte[] buf = "UDP wo lai le".getBytes();
3,将数据封装成了数据包。
//创建一个数据包对象,DatagramPacket(byte[] buf, int length, InetAddress address, int port)
//buf
- 包数据。length
- 包长度,address
- 目的地址。port
- 目的端口号。
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.100"),10000);
4,用socket服务的send方法将数据包发送出去。
ds.send(dp);
// 5,关闭资源。
ds.close();
}
}
udp的接收端:
1,创建udp的socket服务,必须要明确一个端口,作用在于,只有发送到这个端口的数据才是这个接收端可以处理的数据。
2,定义数据包,用于存储接收到数据。
3,通过socket服务的接收方法将收到的数据存储到数据包中。
4,通过数据包的方法获取数据包中的具体数据内容,比如ip、端口、数据等等。
5,关闭资源。
lass UdpRece {
public static voidmain(String[] args) throws Exception{
1,创建udp的socket服务。
DatagramSocket ds = newDatagramSocket(10000);
2,定义数据包,用于存储接收到数据。先定义字节数组,数据包会把数据存储到字节数组中。
byte[] buf = new byte[1024];
DatagramPacket dp = newDatagramPacket(buf,buf.length);
3,通过socket服务的接收方法将收到的数据存储到数据包中。
ds.receive(dp);//该方法是阻塞式方法。
4,通过数据包的方法获取数据包中的具体数据内容,比如ip,端口,数据等等。
String ip =dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = newString(dp.getData(),0,dp.getLength());//将字节数组中的有效部分转成字符串。
System.out.println(ip+":"+port+"--"+text);
5,关闭资源。
ds.close();
}
}
TCP传输的操作步骤:
客户端:
1,建立tcp的socket服务,最好明确具体的地址和端口。这个对象在创建时,就已经可以对指定ip和端口进行连接(三次握手)。
2,如果连接成功,就意味着通道建立了,socket流就已经产生了。只要获取到socket流中的读取流和写入流即可,只要通过getInputStream和getOutputStream就可以获取两个流对象。
3,关闭资源。
class TcpClient{
public static voidmain(String[] args) throws Exception{
Socket s = newSocket("192.168.1.100",10002);
OutputStream out = s.getOutputStream();//获取了socket流中的输出流对象。
out.write("tcp,哥们又来了!".getBytes());
s.close();
}
}
客户端:
1,创建服务端socket服务,并监听一个端口。
2,服务端为了给客户端提供服务,获取客户端的内容,可以通过accept方法获取连接过来的客户端对象。服务器可以连接多个客户端而能保证数据不会发错的原因就是,它用的是客户端的Socket流。
3,可以通过获取到的socket对象中的socket流和具体的客户端进行通讯。
4,如果通讯结束,关闭资源。注意:要先关客户端,再关服务端。
class TcpServer{
public static voidmain(String[] args) throws Exception{
ServerSocket ss = newServerSocket(10002);//建立服务端的socket服务
Socket s = ss.accept();//获取客户端对象
String ip =s.getInetAddress().getHostAddress();//获取连接进来的客户端的IP地址
System.out.println(ip+".....connected");
//可以通过获取到的socket对象中的socket流和具体的客户端进行通讯。
InputStream in = s.getInputStream();//读取客户端的数据,使用客户端对象的socket读取流
//将获取的数据存放到一个字节数组中,有利于提高效率
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
// 如果通讯结束,关闭资源。
s.close();
ss.close();
}
}
练习:服务端上传一张图片给服务端,服务端将其存入到一个文件中,存入成功给出一个反馈信息给客户端,表示上传成功。class PicSocket{
public static void main(String[] args) throws Exception
{
Socket s=new Socket("192.168.1.100",10014);
FileInputStream fs=new FileInputStream("D:\\Media\\ZUOYE\\day24\\1.jpg");
OutputStream fos=s.getOutputStream();
byte[] buf=new byte[1024];
int len=0;
while((len=fs.read(buf))!=-1)
{
fos.write(buf,0,len);
}
//此处很关键,因为是阻塞式传输,如果客户端传送完数据没有给出一个标记表示文件已经传输完的话,服务端会一直等,所以服务端其实已经存入成功,但是没有给出“上传成功”的字样,初次遇到这种问题会很纠结,找不出原因。因此,客户端在上传时,当要上传的文件上传完时,一定要给一个信息让服务端知道。
s.shutdownOutput();
InputStream fr=s.getInputStream();
byte[] buff=new byte[1024];
int leng=fr.read(buff);
System.out.println(new String(buff,0,leng));
fs.close();d
s.close();
}
}
class PicServer
{
public static void main(String[] args)throws Exception
{
ServerSocket ss=new ServerSocket(10014);
Socket s=ss.accept();
InputStream is=s.getInputStream();
FileOutputStream fos=new FileOutputStream("D:\\Media\\ZUOYE\\day24\\3.jpg");
byte[] buf=new byte[1024];
int len=0;
while((len=is.read(buf))!=-1)
{
fos.write(buf,0,len);
}
System.out.println(".......");
OutputStream os=s.getOutputStream();
os.write("上传成功".getBytes());
is.close();
s.close();
ss.close();
}
}
练习二,客户端从键盘录入中获取到的字符上传给服务端,服务端收到后再将其转换成大写反馈给客户端。
public class Tcp {
/**
* @param args
*/
//获取键盘录入,并将其传给服务端,服务接收到数据后再将其转换成大写返回给客户端。
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
Socket s=new Socket("192.168.1.100",10003);
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
BufferedReader bfr=new BufferedReader(new InputStreamReader(s.getInputStream()));
//byte[]buf=new byte[1024];
String line=null;
while((line=br.readLine())!=null)
{
if("over".equals(line))
break;
pw.println(line);
String str=bfr.readLine();
System.out.println(str);
}
br.close();
s.close();
}
}
public class TcpServer {
/**
* @param args
*/
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
ServerSocket ss=new ServerSocket(10003);
Socket s=ss.accept();
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
String line=null;
while((line=br.readLine())!=null)
{
String str=line.toUpperCase();
pw.println(str);
}
br.close();
ss.close();
}
}
总结:在网络编程中一定要注意它的步骤,要明确每一个流程是怎么走的。就以上面这个键盘录入为例分析:
客户端:1,建立连接。
2,键盘录入,再将获取到的数据通过输出流发送到服务端。
3,要获取服务端反馈过来的信息,所以要有一个输入流来接收服务端发来的数据。
4,如果输出流用到了缓冲技术一定要每输完一行就要换行一次,并且刷新一次。
5,当读到有结束标记的语句时就停止读写的操作
6,关闭资源
服务端:1,建立连接。
2,通过accept()的方法获取客户端的Socket。并通过它来获取客户端传过来的数据。
3,每获取一行的数据,将其转换成大写再反馈给客户端,直至客户端结束传入数据。
4,关闭资源。