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

从简单到复杂实现TCP客户端服务器通信

2015-12-30 19:09 615 查看
**


首先我们要知道服务器客户端的概念。

客户端-服务器(Client/Server)结构,简称C/S结构,是一种软件系统体系结构,它把客户端 (Client) 与服务器 (Server) 区分开来,每一个客户端软件的实例都可以向一个服务器或应用程序服务器发出请求。由美国Borland公司最早研发,目前最流行的两大主流软件体系结构之一,另一个是美国微软公司研发的浏览器-服务器(Browser/Server,B/S)结构。服务器端一般使用高性能的计算机,并配合Oracle、Sybase等大型数据库;客户端需要安装专门的软件。

socket通信模型



客户端套接字是Socket,绑定了服务器的IP地址和端口号。

服务器套接字是ServerSocket,绑定了端口号。

然后通过TCP或者UDP协议通过io流进行通信。

几个名词解释

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。所以可以把客户端/服务器看作一种特殊的文件,客户端/服务器从流中读、写数据可以看成是从文件中“读、写”数据。

ip地址,每一个网络、每一台主机都具有唯一的ip地址,可以把它看成是电话通信中的电话号码,电话号码可以唯一指定是那台电话。

端口:可以唯一指定是哪个应用,范围0-65535,1024前一般为系统应用所使用。

TCP协议,一种传输协议,与UDP区别是它必须建立连接才可通信,可从accept方法看出,以数据流的方式传输数据,深层原理不太清楚。

UDP协议,传输协议,不必建立连接,以数据包的形式传输数据,数据容易丢失。





TCP协议通信-代码流程

客户端:

1、创建套接字(指定ip和端口)

2、创建输出流OutputStream对象,发送数据

3、创建输入流InputStream对象,接收服务器发来的数据

4、释放资源

服务器:

1、创建套接字(指定端口)

2、创建输入流InputStream对象,接收客户端发送的信息

3、创建输出流OutputStream对象,发送数据到客户端

4、释放资源

到此,我们就可以写一个简单的客户端和服务器通信的简单代码了。

简单程序1

客户端

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class Client{
public static void main(String[] args) throws IOException {
// 创建Socket套接字对象
Socket s = new Socket("localhost", 8888);
// 获取输出流对象,写数据
OutputStream os = s.getOutputStream();
os.write("hello,世界".getBytes());
// 释放资源
s.close();
}
}


服务器

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

public class Server{
public static void main(String[] args) throws IOException {
// 创建接收端的套接字对象
ServerSocket ss = new ServerSocket(8888);
// 监听客户端连接,返回相应的Socket对象
Socket s = ss.accept();
// 获取输入流对象,从输入流中获取数据
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);
String str = new String(bys, 0, len);
System.out.println("From Client" + "(" + s.getInetAddress().getHostAddress() + "):" + str);
// 释放资源
s.close();
}
}


这个程序就可以看到当客户端连接到服务器时,服务器就可以接收到客户端发送来的数据。



但是这个程序只能看到客户端发送的一句话,并不能从客户端输入什么服务器就接收什么。要实现这个效果就必须假如多线程,一个线程负责发送数据,另一个线程负责接收数据。

客户端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class Client {
public static void main(String[] args) throws IOException {
Socket s = new Socket("localhost", 8080);
// 标准读入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("请输入用户名:");
String name = br.readLine();
// 启动线程,监听读入流
ClientRunable cr = new ClientRunable(s);
new Thread(cr).start();
// 发送消息到服务器
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String str = null;
bw.write(name);
bw.newLine();
bw.flush();
while (true) {
str = br.readLine();
bw.write(name + ":" + str);
bw.newLine();
bw.flush();
}
}
}

class ClientRunable implements Runnable {
private Socket s;

private BufferedReader br;

public ClientRunable(Socket s) throws IOException {
this.s = s;
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
}

public void run() {
while (true) {
try {
String s = br.readLine();
System.out.println(s);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}


服务器

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8080);
System.out.println("等待客户端连接...");
while (true) {
// 等待客户端连接
Socket s = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
System.out.println(br.readLine() + "已连接");
// 启动线程,处理输入、输出流对象
ServerRunnable sr = new ServerRunnable(s);
Thread t1 = new Thread(sr);
t1.start();
}
}
}

class ServerRunnable implements Runnable {
Socket s;

public ServerRunnable(Socket s) {
this.s = s;
}

@Override
public void run() {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream(), "GBK"));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while (true) {
String s;
try {
s = br.readLine();
System.out.println(s);
bw.write(s);
bw.newLine();
bw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}




这样的话就可以实现客户端输入什么服务器就显示什么了,但是当多个客户端连接进来时,并不能实现多个客户端同时通信。所以为了实现多个客户端通信,我们可以有一下思路:在服务器新建一个队列,当有客户端连接进来时就把他加到该队列中,然后服务器发送数据时就遍历这个队列,向这个队列的每个Socket的输出流都写入数据。

客户端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class Client {
public static void main(String[] args) throws IOException {
Socket s = new Socket("localhost", 8080);
// 标准读入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in, "GBK"));
System.out.print("请输入用户名:");
String name = br.readLine();
// 启动线程,监听读入流
ClientRunable cr = new ClientRunable(s);
new Thread(cr).start();
// 发送消息到服务器
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream(), "GBK"));
String str = null;
bw.write(name);
bw.newLine();
bw.flush();
while (true) {
str = br.readLine();
bw.write(name + ":" + str);
bw.newLine();
bw.flush();
}
}
}

class ClientRunable implements Runnable {
private Socket s;

private BufferedReader br;

public ClientRunable(Socket s) throws IOException {
this.s = s;
br = new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
}

public void run() {
while (true) {
try {
String s = br.readLine();
System.out.println(s);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}


服务器

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class Server {
private static ArrayList<Socket> arraySocket = new ArrayList<Socket>();
static ArrayList<ServerRunnableReader> arrayThread = new ArrayList<ServerRunnableReader>();
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8080);
System.out.println("等待客户端连接...");
while (true) {
// 等待客户端连接
Socket s = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
System.out.println(br.readLine() + "已连接");
// 将连接的客户端加入到队列
arraySocket.add(s);
Start(new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK")));
}
}

private static void Start(BufferedReader br) {
BufferedWriter[] bw = new BufferedWriter[arraySocket.size()];
int i = 0;
try {
for (Socket as : arraySocket) {
bw[i] = new BufferedWriter(new OutputStreamWriter(as.getOutputStream(), "GBK"));
i++;
}
} catch (Exception e) {
e.printStackTrace();
}
ServerRunnableReader srr = new ServerRunnableReader(br);
Thread t1 = new Thread(srr);
t1.start();
arrayThread.add(srr);
for (ServerRunnableReader thread : arrayThread) {
thread.setBw(bw);
}
}
}

class ServerRunnableReader implements Runnable {
BufferedReader br = null;
BufferedWriter[] bw = null;

public ServerRunnableReader(BufferedReader br) {
this.br = br;
}

public void setBw(BufferedWriter[] bw) {
this.bw = new BufferedWriter[bw.length];
for (int j = 0; j < bw.length; j++) {
this.bw[j] = bw[j];
}
}

@Override
public void run() {
while (true) {
try {
String s = br.readLine();
System.out.println(s);
for (BufferedWriter bufferedWriter : bw) {
bufferedWriter.write(s);
bufferedWriter.newLine();
bufferedWriter.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: