java网络编程
原创
©著作权归作者所有:来自51CTO博客作者959_1x的原创作品,请联系作者获取转载授权,否则将追究法律责任
Socket概述
Java的网络编程主要涉及到的内容是Socket编程。Socket,套接字,就是两台主机之间逻辑连接的端点。TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。Socket是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远程主机的IP地址、远程进程的协议端口。
应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
Socket,实际上是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。实际上,Socket跟TCP/IP协议没有必然的关系,Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以说,Socket的出现,只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、listen、accept、send、read和write等等。网络有一段关于socket和TCP/IP协议关系的说法比较容易理解:
“TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。这个就像操作系统会提供标准的编程接口,比如win32编程接口一样,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。”
实际上,传输层的TCP是基于网络层的IP协议的,而应用层的HTTP协议又是基于传输层的TCP协议的,而Socket本身不算是协议,就像上面所说,它只是提供了一个针对TCP或者UDP编程的接口。socket是对端口通信开发的工具,它要更底层一些。
Socket整体流程
Socket编程主要涉及到客户端和服务端两个方面,首先是在服务器端创建一个服务器套接字(ServerSocket),并把它附加到一个端口上,服务器从这个端口监听连接。端口号的范围是0到65536,但是0到1024是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。
客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。

单线程的网络编程
服务器端
public class SocketServer {
public static void main(String[] args) throws IOException {
// 端口号
int port = 7000;
// 在端口上创建一个服务器套接字
ServerSocket serverSocket = new ServerSocket(port);
// 监听来自客户端的连接
Socket socket = serverSocket.accept();
DataInputStream dis = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(socket.getOutputStream()));
do {
double length = dis.readDouble();
System.out.println("服务器端收到的边长数据为:" + length);
double result = length * length;
dos.writeDouble(result);
dos.flush();
} while (dis.readInt() != 0);
socket.close();
serverSocket.close();
}
}
客户端
public class SocketClient {
public static void main(String[] args) throws UnknownHostException, IOException {
int port = 7000;
String host = "localhost";
// 创建一个套接字并将其连接到指定端口号
Socket socket = new Socket(host, port);
DataInputStream dis = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(socket.getOutputStream()));
Scanner sc = new Scanner(System.in);
boolean flag = false;
while (!flag) {
System.out.println("请输入正方形的边长:");
double length = sc.nextDouble();
dos.writeDouble(length);
dos.flush();
double area = dis.readDouble();
System.out.println("服务器返回的计算面积为:" + area);
while (true) {
System.out.println("继续计算?(Y/N)");
String str = sc.next();
if (str.equalsIgnoreCase("N")) {
dos.writeInt(0);
dos.flush();
flag = true;
break;
} else if (str.equalsIgnoreCase("Y")) {
dos.writeInt(1);
dos.flush();
break;
}
}
}
socket.close();
}
}
实现把客户端发送的文本打印出来的操作
服务器
public void server(){
//先Server再Client,然后输出在Server上
//输入
ServerSocket ss = null;
Socket accept = null;
InputStream is = null;
try {
//创建ServerSocket对象,指明端口号
ss = new ServerSocket(8899);
//调用accept()方法接受来自客户端的socket
accept = ss.accept();
is = accept.getInputStream();
//读取数据
byte[] buffer = new byte[10];
int len;
if((len = is.read(buffer)) != -1){
String str = new String(buffer,0,len);
System.out.println(str);
System.out.println(accept.getInetAddress().getHostAddress() + "---" + accept.getInetAddress().getHostName());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(is !=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(accept != null){
try {
accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(ss !=null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端
public void client() {
Socket socket = null;
OutputStream os = null;
try {
//创建Socket对象,指明IP和端口号
InetAddress inetAddress = InetAddress.getByName("192.168.5.87");
socket = new Socket(inetAddress,8899);
//获取输出流
os = socket.getOutputStream();
//写出数据
os.write("你好".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if(os!=null){
try {
//资源关闭
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实现客户端上传到服务器的图片或者视频
public class TCPTest2 {
@Test
public void client() throws IOException {
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9000);
OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream(new File("ruiwen5.jpg"));
byte[] buffer = new byte[20];
int len;
while ((len = fis.read(buffer)) != -1){
os.write(buffer,0,len);
}
fis.close();
os.close();
socket.close();
}
@Test
public void server() throws IOException {
ServerSocket ss = new ServerSocket(9000);
Socket socket = ss.accept();
InputStream inputStream = socket.getInputStream();
FileOutputStream fos = new FileOutputStream(new File("ruiwen6.jpg"));
byte[] buffer = new byte[20];
int len;
while ((len = inputStream.read(buffer)) != -1){
fos.write(buffer,0,len);
}
fos.close();
inputStream.close();
socket.close();
ss.close();
}
}
多线程的网络编程
多线程机制,1:N,修改的服务器端的代码
public class SocketServerM {
public static void main(String[] args) throws IOException {
int port = 7000;
int clientNo = 1;
ServerSocket serverSocket = new ServerSocket(port);
// 创建线程池
ExecutorService exec = Executors.newCachedThreadPool();
try {
while (true) {
Socket socket = serverSocket.accept();
exec.execute(new SingleServer(socket, clientNo));
clientNo++;
}
} finally {
serverSocket.close();
}
}
}
class SingleServer implements Runnable {
private Socket socket;
private int clientNo;
public SingleServer(Socket socket, int clientNo) {
this.socket = socket;
this.clientNo = clientNo;
}
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(socket.getOutputStream()));
do {
double length = dis.readDouble();
System.out.println("从客户端" + clientNo + "接收到的边长数据为:" + length);
double result = length * length;
dos.writeDouble(result);
dos.flush();
} while (dis.readInt() != 0);
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("与客户端" + clientNo + "通信结束");
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
关键的地方在于多线程机制的运用,同时利用线程池可以改善服务器程序的性能。
UDP:
public class UDPTest {
@Test
public void sender() throws IOException {
DatagramSocket ds = new DatagramSocket();
String str = "123";
byte[] data = str.getBytes();
InetAddress byName = InetAddress.getByName("127.0.0.1");
DatagramPacket datagramPacket = new DatagramPacket(data,0,data.length,byName,9000);
ds.send(datagramPacket);
ds.close();
}
@Test
public void receiver() throws IOException {
DatagramSocket ds = new DatagramSocket(9000);
byte[] buffer = new byte[100];
DatagramPacket datagramSocket = new DatagramPacket(buffer,0,buffer.length);
ds.receive(datagramSocket);
System.out.println(new String(datagramSocket.getData(),0,datagramSocket.getLength()));
ds.close();
}
}