基于Socket的Java网络编程

------- java学习型技术博客、期待与您交流 ----------

Java基础总结之基于Socket的Java网络编程

 

一、网络基础知识

       在学习网络编程之前,需要了解一些网络知识,比如说网络模型,IP地址,通讯端口,通讯协议,其中要重点熟悉TCPUDP通讯协议。

1,网络参考模型

2IP地址

      所谓IP地址就是给每个连接在Internet上的主机分配的一个32bit地址,它是网络中设备的标识。但由于数字不易记忆,可用主机名。

3,端口

      端口(port)可以认为是计算机与外界通讯交流的出口。用于标识进程的逻辑地址,不同进程的标识。

4,传输协议UDP TCP(必须熟悉)

TCP(传输控制协议)

     TCPTranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。其特点如下:

◆  建立连接,形成传输数据的通道。

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

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

◆  因为必须建立连接,效率会稍低

UDP(用户数据报协议)

     UDPUser Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。其特点如下:

◆   将数据及源和目的封装成数据包中,不需要建立连接

◆   每个数据报的大小在限制在64k

◆   因没有建立连接,是不可靠协议

◆  也是因为不需要建立连接,所以速度较快

两者应用分析:

      1TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。

      2UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。

 

二、基于SocketJava网络编程

1,什么是Socket

       网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket通过网络进行通信的两端都有Socket,网络通信其实就是Socket之间的通信,也就是数据在两个Socket之间通过IO传输。

       由于传输协议的不同,每个传输协议都有自己建立端点的方式,分别对UDP传输和TCP传输进行演示。

UDP传输

UDP传输需要以下几个步骤:

---->建立发送端,接收端。

---->建立数据包。

----> 调用Socket的发送接收方法(send(); receive();)。

----> 关闭Socket

其中,发送端和接受端是两个独立的应用程序,在发送端,要在数据包对象中明确目的地IP及端口。在接收端,要指定监听的端口

UDP传输示例:

需求:编写一个发送端程序,实现通过udp传输方式,将一段文字数据发送出去。用另一个接收端程序接受UDP协议传输的数据。

发送端:

class  UdpSend
{
	public static void main(String[] args) throws Exception
	{
		//1,通过DatagramSocket对象创建udp服务,并指定端口。
		DatagramSocket ds = new DatagramSocket(8888);

		//2,确定数据,并封装成数据包。
		byte[] buf = "udp ge men lai le ".getBytes();
		DatagramPacket dp = 
	    new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),10000);

		//3,通过socket服务,将已有的数据包发送出去。通过send方法。
		ds.send(dp);

		//4,关闭资源。
		ds.close();
	}
}

 

接收端:

class  UdpRece
{
	public static void main(String[] args) throws Exception
	{
		//1,创建udp socket,建立端点。
		DatagramSocket ds = new DatagramSocket(10000);
		
		//2,定义数据包。用于存储数据。
		byte[] buf = new byte[1024];
		DatagramPacket dp = new DatagramPacket(buf,buf.length);

		//3,通过服务的receive方法将收到数据存入数据包中。
		ds.receive(dp);//阻塞式方法。
		
		//获取IP地址
		String ip = dp.getAddress().getHostAddress();
		
  //4,通过数据包的方法获取其中的数据。
		String data = new String(dp.getData(),0,dp.getLength());
		//获取端口
		int port = dp.getPort();
		
  //打印数据信息
		System.out.println(ip+"::"+data+"::"+port);
		
  //5,关闭资源
		ds.close();
	}
}

 

UDP练习:

需求:编写一个聊天程序。分为接收端和发送端,两端需要同时执行,所以需要运用多线程。一个线程负责发送数据,一个线程负责接收数据。由于收发的动作不一致,所以要定义两个run方法,且这两个方法要封装到不同的类中。代码如下:

import java.io.*;
import java.net.*;
//定义一个发送端Send类实现Runnable接口
class Send implements Runnable
{	
	//为了DatagramSocket作用于整个类中,创建一个静态成员变量
	private DatagramSocket ds;
	//以DatagramSocket对象为参数的构造函数
	public Send(DatagramSocket ds)
	{
		this.ds = ds;
	}
	//覆盖Runnable接口的run方法
	public void run()
	{
		try
		{
			//由系统标准输入设备构造BufferedReader对象,读取键盘录入
			BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

			String line = null;
			while((line=bufr.readLine())!=null)
			{
				//将读取到的数据存入字节数组中
				byte[] buf = line.getBytes();
				//将数据中的数据封装成数据报包
				DatagramPacket dp = 
					new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.104"),10002);
				//通过DatagramSocket对象的send方法将读取到的数据发送出去。
				ds.send(dp);
				//如果输入886则结束
				if("886".equals(line))
					break;
			}
		}
		catch (Exception e)
		{
			throw new RuntimeException("发送端失败");
		}
	}
}
//定义一个接受端Send类实现Runnable接口
class Rece implements Runnable
{
	private DatagramSocket ds;
	public Rece(DatagramSocket ds)
	{
		this.ds = ds;
	}
	public void run()
	{
		try
		{
			//由于接受端需要接受多个客户端发来的数据,所以将其置于循环中
			while(true)
			{
				//创建一个缓冲区
				byte[] buf = new byte[1024];
				//定义一个数据报包存储接受到的数据
				DatagramPacket dp = new DatagramPacket(buf,buf.length);
				//将接受的数据存入到数据包中
				ds.receive(dp);
				//获取发送端IP地址
				String ip = dp.getAddress().getHostAddress();
				//获取发送端发送的数据
				String data = new String(dp.getData(),0,dp.getLength());
				//如果接受到的数据为"886"表面客户端离开,打印提示
				if("886".equals(data))
				{
					System.out.println(ip+"....离开聊天室");
					break;
				}
				//打印发送端Ip和数据
				System.out.println(ip+":"+data);
			}
		}
		catch (Exception e)
		{
			throw new RuntimeException("接收端失败");
		}
	}
}
class  ChatDemo
{
	public static void main(String[] args) throws Exception
	{
		//创建发送端和接受端的Socket对象,(接收端要指定监听的端口)
		DatagramSocket sendSocket = new DatagramSocket();
		DatagramSocket receSocket = new DatagramSocket(10002);
		//创建线程对象并开启
		new Thread(new Send(sendSocket)).start();
		new Thread(new Rece(receSocket)).start();
	}
}

 

TCP传输

       UDP 传输分为发送端和接受端,而TCP传输则分为客户端和服务端,分别对应两个对象,SocketServerSocket

◆   客户端:

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

◆ 服务端:

    Server端监听某个端口是否有连接请求,Client端向Server 端发出连接请求,Server端向Client端发回接受消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。

TCP传输示例

需求:编写一个客户端程序,实现通过tcp传输方式,将一段文字数据发送出去。用另一个服务端程序接受tcp协议传输的数据。

客户端步骤:

     1,创建Socket服务。并指定要连接的主机和端口。

     2,由Socket对象得到输出流,并通过输出流的write方法写出数据

     3,关闭客户端

import java.io.*;
import java.net.*;
class  TcpClient
{
	public static void main(String[] args) throws Exception 
	{
		//创建客户端的socket服务。指定目的主机和端口
		Socket s = new Socket("192.168.1.254",10003);
		
		//由Socket对象得到输出流,并通过输出流的write方法写出数据
		OutputStream out = s.getOutputStream();
		out.write("This Message from tcpClient".getBytes());

		s.close();
	}
}

 

服务端步骤:

     1,建立服务端的socket服务。ServerSocket();并监听一个端口。

     2,通过ServerSokcet的 accept方法,获取连接过来的客户端对象。

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

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

class  TcpServer
{
	public static void main(String[] args) throws Exception
	{
		//建立服务端socket服务。并监听一个端口。
		ServerSocket ss = new ServerSocket(10003);

		//通过accept方法获取连接过来的客户端对象。(此方法为阻塞式,没有连接就会等)
		while(true)
		{
		Socket s = ss.accept();
		//获取客户端的IP地址
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+".....connected");

		//获取客户端发送过来的数据,由Socket对象得到输入流。
		InputStream in = s.getInputStream();
		
		//定义数组缓冲读取到的数据
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		//打印数组中的数据
		System.out.println(new String(buf,0,len));

		s.close();//关闭客户端.
		}
		//ss.close();
	}
}

 

TCP练习:

需求:模拟一个用户登录,客户端通过键盘录入用户名,服务端对这个用户名进行校验。

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

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

           最多登录三次。

import java.io.*;
import java.net.*;
//客户端
class  LoginClient
{
	public static void main(String[] args) throws Exception
	{	//创建Socket服务。并指定要连接的主机和端口。
		Socket s = new Socket("192.168.1.254",10008);
		//构造BufferedReader对象获取键盘录入
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));
		//由Socket对象得到输出流,并构造PrintWriter对象
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		//由Socket对象得到输入流,并构造相应的BufferedReader对象
		BufferedReader bufIn =
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		//通过for循环限定次数
		for(int x=0; x<3; x++)
		{
			//读取键盘录入信息
			String line = bufr.readLine();
			//如果客户端录入为空,例如“ctrl+c”退出。则结束循环
			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 UserThread implements Runnable
{
	private Socket s;
	UserThread(Socket s)
	{
		this.s = s;
	}
	public void run()
	{
		//获取客户端IP并打印
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"....connected");
		try
		{
			//通过for循环控制次数
			for(int x=0; x<3; x++)
			{
				//由Socket对象得到输入流,并构造相应的BufferedReader对象
				BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
				//读取客户端提交的数据
				String name = bufIn.readLine();
				//如果数据已经为空,则结束循环,(否则将以null为读取的到的数据进行无意义的判断)
				if(name==null)
					break;
				//构造BufferedReader对象并关联存储用户名的文件对象。
				BufferedReader bufr = new BufferedReader(new FileReader("user.txt"));

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

				String line = null;
				//定义一个标记记录校验结果,默认为false
				boolean flag = false;
				while((line=bufr.readLine())!=null)
				{
					//如果匹配成功则结束循环并将标记置为true
					if(line.equals(name))
					{
						flag = true;
						break;
					}				
				}
				//根据标记在服务端输出相应提示,并向客户端写出相应提示。
				if(flag)
				{
					System.out.println(name+",已登录");
					out.println(name+",欢迎光临");
					//标记为true则结束三次校验循环
					break;
				}
				else
				{
					System.out.println(name+",尝试登录");
					out.println(name+",用户名不存在");
				}
			}
			//关闭客户端
			s.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException(ip+"校验失败");
		}
	}
}
//服务端
class  LoginServer
{
	public static void main(String[] args) throws Exception
	{
		//创建服务端SercerSocket对象,并监听端口
		ServerSocket ss = new ServerSocket(10008);
		//由于可能存在多个客户端,使用while循环
		while(true)
		{
			//通过accept方法获取连接过来的客户端对象
			Socket s = ss.accept();
			//创建用户线程并开启
			new Thread(new UserThread(s)).start();
		}
	}
}

 

------- java学习型技术博客、期待与您交流 ----------

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值