Socket编程基础--基于TCP协议的网络编程

时间:2022-01-22 00:44:23

一、概述

在学习TCP协议之前,需要了解网络编程中的几个基本概念。

  • IP协议:是Internet Protocol的外语缩写,为计算机网络相互连接进行通信而设计的协议。在因特网中,它是能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。任何厂家生产的计算机系统,只要遵守IP协议就可以与因特网互连互通。IP地址具有唯一性。
  • TCP协议:TCP协议被称作是一种端对端协议。它提供IP环境下的数据可靠传输,提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。通俗说,它是事先为所发送的数据开辟出连接好的通道,然后再进行数据发送
  • 端口号Port:主要是计算机用来来区分连接不同应用程序。注意:0-1023不可以使用,系统已经占用。

二、Java的基本网络支持

  • 使用InetAddress
  • URLDecoder和URLEncoder
  • URL、URLConnetction(这个后面说)

    InetAddress的简单使用

 public static void main(String[] args) throws Exception {
InetAddress ia = InetAddress.getLocalHost() ;
System.out.println("ip:" + ia.getHostAddress());
System.out.println("主机名:" + ia.getHostName());
}

URLDecoder和URLEncoder

URLDecoder和URLEncoder是完成字符串和application/x-www-form-urlencode MIME字符串之间的相互转化

  • URLDecoder的decode()方法可以将一个看上去是乱码的转换成不是乱码的字符串
  • URLEncoder的encode()方法可以将一个看上去不是乱码的转换成是application/x-www-form-urlencode MIME的字符串
public static void main(String[] args) throws Exception {
String keyword = URLEncoder.encode("李文");
System.out.println(keyword);
keyword = URLDecoder.decode(keyword) ;
System.out.println(keyword);
}

三、通信流程

服务端

计算机能够接受通信实体请求的类是ServerSocket,ServerSocket用来监听客户端对应的socket,一般我们在while(true)里使用ServerSocket不断的获取socket,如果没用连接它会处于等待状态。

常用api:

  • accept():获取一个客户端socket的连接请求,这个方法会返回一个与客户端对应的socket,否则会处于等待状态,线程阻塞。
  • ServerSocket(int port):用指定端口号创建一个ServerSocket该值可以在0-65535之间。上面已经提过,0-1023不要用。
  • ServerSocket(int port,int backlog):增加一个改变连接队列长度的参数。
 //1.创建socketserver,监听3000端口
ServerSocket ss = new ServerSocket(30000) ;
while(true){
Socket s = ss.accept();
//2.将输出流包装成PrintStream
PrintStream ps = new PrintStream(s.getOutputStream()) ;
//3.进行IO操作
ps.println("hello socket,i am from SocketServer");
//4.关流
ps.close();
s.close() ;
}

客户端

客户端通常使用Socket的构造器连接指定服务器
构造函数如下:
public Socket(String host, int port)
使用如下:

//1.创建socket
Socket s = new Socket("127.0.0.1",30000) ;
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())) ;
String line = br.readLine();
System.out.println("来自服务端:" + line );
s.close() ;

上面的127.0.0.1是连接本地IP的。大家可以到C:\Windows\System32\drivers\etc这个目录下的host文件中查看到。
上面程序运首先要运行服务端,然后在运行客户端就可以进行通信了。
当服务端、客户端连接到了对应的socket之后,程序就不用再区分服务端、客户端了,两者是通过各自的socket通信。socket通过如下两个api来获取输出流、输入流。

 public InputStream getInputStream()
public OutputStream getOutputStream()

四、加入多线程

上面的client、server只是简单的通信,下面实现一个包含多线程的socket通信。我们在服务端创建多线程线程,每一个socket对应一个线程,这个线程负责读取socket输入流中的内容,也就是从客户端发送过来的数据,然后再返回给客户端信息表示收到了。

服务端代码:

    public static ArrayList<Socket> socketList = new ArrayList<Socket>() ;

public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(30001) ;
while(true){
Socket s = ss.accept() ;
//每次获取一个socket,添加到集合中
socketList.add(s) ;
//开启线程执行任务
new Thread(new ServerThread(s)).start();
}
}

服务端创建一个集合用来存放来自客户端的Socket,每当获取一个socket,就开始启动一个线程,该线程用来处理通信业务。

class ServerThread implements Runnable{
//处理的socket
Socket s = null ;
//处理socket对应的流
BufferedReader br = null ;
public ServerThread(Socket s ) throws IOException{
this.s = s ;
br = new BufferedReader(new InputStreamReader(s.getInputStream())) ;
}

public void run() {
String context ;
while((context=readFromClient())!= null ){
for (Socket s : MyServer.socketList) {
System.out.println("服务端收到数据了:" + context );
PrintStream ps;
try {
ps = new PrintStream(s.getOutputStream());
ps.println(context + "---来自服务端");
} catch (IOException e) {
e.printStackTrace();
}

}
}
}

public String readFromClient(){
try {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
//删除socket
MyServer.socketList.remove(s);
}
return null ;
}
}

客户端代码:

public static void main(String[] args) {
//1.创建socket
try {
Socket s = new Socket("127.0.0.1",30001) ;
//2.获取来自服务端的数据
new Thread(new ClientThread(s)).start() ;
//3.获取socket对应的输出流
PrintStream ps = new PrintStream(s.getOutputStream()) ;
String line = null ;
//4.键盘输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
while((line = br.readLine())!= null ){
//5.将键盘输入写到socket输出流中
ps.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}

重要的地方已经注释,代码不同,相信能看懂。这样就完成了多线程的通信了,还是和上面一样,先运行server,再运行client,通过运行可以知道每个客户端可以看到其它客户端的信息。

Ok,这篇已经结束了,这篇只是简单介绍了Socket的基础,后面会继续详细分析还有介绍基于UDP网络编程。

源码下载