在Java中使用Socket模拟客户端和服务端(多线程)
1:Socket与ServerSocket的交互

2.Socket和ServerSocket介绍
Socket
构造函数
Socket()
Socket(InetAddress address, int port)throws UnknownHostException, IOException
Socket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException
Socket(String host, int port)throws UnknownHostException, IOException(最简单的连接方式)
Socket(String host, int port, InetAddress localAddress, int localPort)throws IOException
除去第一种不带参数的之外,其它构造函数会尝试建立与服务器的连接。如果失败会抛出IOException错误。如果成功,则返回Socket对象。
InetAddress是一个用于记录主机的类,其静态getHostByName(String msg)可以返回一个实例,其静态方法getLocalHost()也可以获得当前主机的IP地址,并返回一个实例。
Socket(String host, int port, InetAddress localAddress, int localPort)构造函数的参数分别为目标IP、目标端口、绑定本地IP、绑定本地端口。
Socket方法
getInetAddress(); 远程服务端的IP地址
getPort(); 远程服务端的端口
getLocalAddress() 本地客户端的IP地址
getLocalPort() 本地客户端的端口
getInputStream(); 获得输入流
getOutStream(); 获得输出流
值得注意的是,在这些方法里面,最重要的就是getInputStream()和getOutputStream()了。
Socket状态
isClosed(); //连接是否已关闭,若关闭,返回true;否则返回false
isConnect(); //如果曾经连接过,返回true;否则返回false
isBound(); //如果Socket已经与本地一个端口绑定,返回true;否则返回false
ServerSocket
构造函数
ServerSocket()throws IOException
ServerSocket(int port)throws IOException
ServerSocket(int port, int backlog)throws IOException
ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException
注意点:
1. port服务端要监听的端口;backlog客户端连接请求的队列长度;bindAddr服务端绑定IP
2. 如果端口被占用或者没有权限使用某些端口会抛出BindException错误。譬如1~1023的端口需要管理员才拥有权限绑定。
3. 如果设置端口为0,则系统会自动为其分配一个端口;
4. bindAddr用于绑定服务器IP,为什么会有这样的设置呢,譬如有些机器有多个网卡。
5. ServerSocket一旦绑定了监听端口,就无法更改。ServerSocket()可以实现在绑定端口前设置其他的参数。
3.实例前说明
Socket网络编程主要用于两台机器之间的数据传输,大致过程为:建立连接→信息传递→关闭连接。我们可以理解为服务器(ServerSocket)和客户端(Socket),服务器提供连接服务,客户端链接服务器。因为服务器需要向多台客户端提供服务,所以需要一直保持监听状态,不断地监听客户端请求,在这个过程中,ServerSocket一直处于阻断状态,直到有客户端连接,马上返回一个Socket对象,然后通过IO流传输数据,在这个过程中,当有数据传输的时候,IO流才被激活,其余时间都处于阻断状态,等待数据发送过来,然后进行处理。
注意:
当我们发送数据的时候,必须使用flush()方法,将数据提交,否则只会存在于缓冲中,不会发送数据,有时候即使我们使用flush()方法,也无法从另一台电脑上读取到数据,这是因为Socket会将数据先存储起来,等到数据量达到一定大小的时候,会一起提交(或者这样理解,flush()会根据换行符’\n’判断用户是否完成输入,如果没有看到’\n’,Socket认为用户数据还没有写完,仍在保留在缓存池中)所以我们可以在要提交的数据加上’\n’强制提交就可以了。
不过,如果我们没必要很多的交互,只需要交互一次就可以了,就可以不在乎这些,当关闭Socket的输入输出流的时候,Socket会将缓存池中的数据全部提交到另一台机器。
最后最重要的,当我们用完流的时候,一定要及时关闭,养成良好的习惯。
在下面的实例中,我通过多线程的方式保证服务器和客户端一直处于数据交互状态,并且使用线程池的方式维护线程。、
线程类:
package com.best.alivn.socketservice;
import java.io.*;
import java.net.Socket;
import java.util.Scanner; /**让服务器处理与客户端通讯放在线程中
* Created by Alivn on 2017/3/18.
*/
public class SessionThread implements Runnable{ private final Socket client;
private static Integer Tag=0; public SessionThread(Socket client) {
this.client = client;
} @Override
public void run() { //输出流
PrintWriter writer=null;
//获取客户端输入流,得到客户端发来的消息
try {
//读取客户端的数据
//读取到的一行数据
String line=null;
//客户端发来的数据
//开启一个读线程
Thread write_thread= new Thread(new Runnable() {
@Override
public void run() {
//输入流
InputStream inputStream=null;
BufferedReader reader=null;
try {
while (true) { inputStream= client.getInputStream();
//为了提高效率,转换成字符流
reader=new BufferedReader(new InputStreamReader(inputStream));
String line=null;
while ((line=reader.readLine())!=null)
{
System.out.println(line);
}
}
} catch (IOException e) {
//流异常
e.printStackTrace();
}finally {
//关闭流
try {
inputStream.close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
} }
}
});
write_thread.start();
//获取输出流,往客户端发送数据
writer=new PrintWriter(client.getOutputStream());
//客户端刚连接的时候,客户端反馈信息
String send_msg ="您已成功连接到服务器......--[Server]";
writer.println(send_msg);
writer.flush();
//可以多次发送
//从键盘输入
while ("1".equals("1")) {
Scanner scanner=new Scanner(System.in);
send_msg =scanner.nextLine()+"--[Server]";
writer.println(send_msg);
//注意(我们发送数据的时候,flush方法会提交我们的数据,但是还不会发送,当数据达到一定容量才会发送,
// 或者读到换行符的时候发送)
writer.flush();
} } catch (IOException e) {
//获取客户端输入流失败
e.printStackTrace();
}finally {
//关闭流
try {
client.shutdownInput();
client.shutdownOutput();
writer.close();
} catch (IOException e) {
//关闭输入输出流失败
e.printStackTrace();
} } }
}
服务器类:
package com.best.alivn.socketservice;
import scala.actors.threadpool.ExecutorService;
import scala.actors.threadpool.Executors;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket; /**服务器
* Created by Alivn on 2017/3/18.
*/
public class Server { //甚至端口号
private final static Integer PORT=8888;
//设置开启的线程数
private final static Integer THREAD_SIZE=5;
//创建线程池
private static ExecutorService executorService=null;
public static void main(String[]args)
{
executorService = Executors.newFixedThreadPool(THREAD_SIZE);
//创建服务器套接字
try {
//创建三个线程放入到线程池中....
ServerSocket serverSocket=new ServerSocket(PORT);
//开启服务器,一直处于监听的状态
System.out.println("[Server]:服务器已启动.........");
while (true)
{
//服务器根据端口号,监听客户端链接,
Socket client = serverSocket.accept();
System.out.println("[Server]:有客户端链接至服务器.......");
//将交互放到线程中
SessionThread session=new SessionThread(client);
//放入到线程池中
session.run();
executorService.execute(session);
}
} catch (IOException e) {
//实例化服务器失败
e.printStackTrace();
} } }
客户端类:
package com.best.alivn.socketservice;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner; /**客户端
* Created by Alivn on 2017/3/18.
*/
public class Client { private final static String host="localhost";
private final static Integer port=8888;
public static void main(String[] args) {
Socket socket=null;
//输入流
BufferedReader reader=null; //创建客户端对象
try {
//连接服务器
socket=new Socket(host,port);
//客户端刚连接的时候,客户端反馈信息
//输出流
final PrintWriter writer=new PrintWriter(socket.getOutputStream());;
String send_msg ="你好,我是小白......--[Client]";
writer.println(send_msg);
writer.flush();
//开启一个线程,处理循环
Thread write_Stream= new Thread(new Runnable() {
@Override
public void run() {
//循环输入数据
Scanner scanner=new Scanner(System.in);
while ("1".equals("1"))
{
String send_msg=scanner.nextLine()+"--[Client]";
writer.println(send_msg);
writer.flush();
}
}
});
write_Stream.start();
while ("1".equals("1")) {
//获取输入流,得服务端的数据
reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line=null;
while ((line=reader.readLine())!=null)
{
System.out.println(line);
}
}
writer.close();
} catch (IOException e) {
//链接失败 文件读取失败
e.printStackTrace();
}finally {
//关闭流
try {
reader.close(); socket.shutdownInput();
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}
} }
}
在Java中使用Socket模拟客户端和服务端(多线程)的更多相关文章
- Java实现UDP之Echo客户端和服务端
Java实现UDP之Echo客户端和服务端 代码内容 采用UDP协议编写服务器端代码(端口任意) 编写客户机的代码访问该端口 客户机按行输入 服务器将收到的字符流和接收到的时间输出在服务器consol ...
- Java实现TCP之Echo客户端和服务端
Java实现TCP之Echo客户端和服务端 代码内容 采用TCP协议编写服务器端代码(端口任意) 编写客户机的代码访问该端口 客户机按行输入 服务器将收到的字符流和接收到的时间输出在服务器consol ...
- 自定义socket 模拟B/S服务端
目录 通过什么实现连接? B/S 客户端与服务端交互过程 socket server端 python代码 (静态html反馈) socket server端 python代码 (动态html反馈) 小 ...
- Python socket编程客户端与服务端通信
[本文出自天外归云的博客园] 目标:实现客户端与服务端的socket通信,消息传输. 客户端 客户端代码: from socket import socket,AF_INET,SOCK_STREAM ...
- c++ 网络编程(一)TCP/UDP windows/linux 下入门级socket通信 客户端与服务端交互代码
原文作者:aircraft 原文地址:https://www.cnblogs.com/DOMLX/p/9601511.html c++ 网络编程(一)TCP/UDP 入门级客户端与服务端交互代码 网 ...
- 用PHP的socket实现客户端到服务端的通信
服务端 <?php error_reporting(E_ALL); set_time_limit(0); ob_implicit_flush(); //本地IP $address = 'loca ...
- Socket通信客户端和服务端代码
这两天研究了下Socket通信,简单实现的客户端和服务端代码 先上winfrom图片,客户端和服务端一样 服务端代码: using System; using System.Collections.G ...
- 基于socket.io客户端与服务端的相互通讯
socket.io是对websocket的封装,用于客户端与服务端的相互通讯.官网:https://socket.io/. 下面是socket.io的用法: 1.由于使用express开的本地服务,先 ...
- 基于socket的客户端和服务端聊天简单使用 附Demo
功能使用 服务端 分离一个不停接受客户端请求的线程 接受不客户端请求的线程中,再分离就收消息的线程 几大对象分别是 IPEndPoint IP终结点 服务端Socket,绑定终结点Bind,启动监听L ...
随机推荐
- C#下用于同时使用OpenCvSharp和Emgu.CV两个库的相互转换库
很久以前做的一个图像处理项目,在.NET平台上使用OpenCV,但因为同时使用了C#支持的两个比较有名的库,由于封装方式不同,难免要转换两个库之间的Mat对象. 同时还封装了一些WPF下,System ...
- sha256_transform
DECLSPEC void sha256_transform (const u32 *w0, const u32 *w1, const u32 *w2, const u32 *w3, u32 *dig ...
- 转码器ffmpeg安装
网络上很多帖子 但是基本上都是没有验证过复制粘贴的 以下是我自己装时流程和网络上的差不多但是中间不通的地方已经改正 centos7 1. 安装autoconf cd /App/srcwget http ...
- Linux中Nginx安装教程
Nginx 是一个很强大的高性能Web和反向代理服务器,它具有很多非常优越的特性: 在连接高并发的情况下,Nginx是Apache服务器不错的替代品:Nginx在美国是做虚拟主机生意的老板们经常选择的 ...
- 128bit 整数运算的实现
对于128bit的长整型运算,GCC提供了扩展类型:__int128.然而该类型不在C/C++语言的标准之中,并且对于不同种类的编译器,它的实现情况不同.因此,在编写相关的可移植程序时,我们有必要实现 ...
- C# 木马功能的简单实现
1.首先解决开机启动木马.通过建立开机启动服务达到目的:2.伪装问题.通过c#反射性能,将正常的.net的exe文件添加监控盗传播取等其他功能,执行正常程序同时,后台悄悄释放windows服务,通过服 ...
- ES6学习笔记(函数)
1.函数参数的默认值 ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面. function log(x, y = 'World') { console.log(x, y); } log(' ...
- Linux rhcsa认证考试试题模拟
声明: 此套试题是2017年rhcsa考试题库,本题库需配合相对应的机器操作,实验环境在我的网盘下载 考试环境: server.group8.example.com 172.24.8.254/24 s ...
- cacti 流量图合并
cacti 安装:https://www.cnblogs.com/weijie0717/p/4072711.html 一.需求介绍 由于交换机的多端口跑同一种流量,需要汇总统计.因此需要见多个端口的流 ...
- airTest 应用到项目并优化
之前已经介绍了airTest的原理,该文主要指引大家能够将airTest框架应用到具体的测试项目当中去. 首先要考虑的是: 1. 你是用airTest 去做什么自动化 (android, ios, w ...