1、Java中客户端和服务器端通信的简单实例

Java中能接收其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态 ServerSocket包含一个监听来自客户端连接请求的方法。
ServerSocket accept():  接收到一个客户端Socket的连接请求,该方法将返回一个与客户端Socket对应的Socket。如果没有客户端连接上来,该方法将一直处于等待状态,线程也被阻
塞。
客户端:Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号。  
服务端:ServerSocket(int port, int backlog) 创建服务器套接字并使用指定的待办事项将其绑定到指定的本地端口号。 
 
 服务器端ServerSocket代码:
public class ServerTest {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(12345);
/**
* 死循环接收客户端请求,accept会阻塞
*/
while (true){
//阻塞等待客户端连接
Socket clientSocket = serverSocket.accept();
try(PrintStream outputStream = new PrintStream(clientSocket.getOutputStream())) {
outputStream.println("Hello,请求已收到。");
}finally {
clientSocket.close();
}
}
}
}

客户端Socket:

public class ClientTest {
public static void main(String[] args) throws IOException {
try (Socket clientSocket = new Socket("127.0.0.1",12345);
BufferedReader clientIn = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));){
String line = clientIn.readLine();
System.out.println("收到服务端返回:" + line);
} }
}

2、采用多线程方式处理网络请求

前面的Server,Client程序只是进行了简单的通信操作,服务器端接收到客户端连接之后,服务器端向客户端输出了一个字符串,而客户端也只是读取服务器端的字符串后就退出了。实际应用中的客户端则可能需要和服务器端保持长时间通信,即服务器端需要不断地读取客户端数据,并向客户端写入数据,户端也需要不断地读取服务器端数据,并向服务器端写入数据。
比如我们的QQ群发消息,比如群里面有100个人上线,就有100个客户端连上了服务器,我们读取到用户发上来的消息之后,我们还要同步给剩余的99个人。这个时候如果我们使用同步的方式处理,会造成在处理我们具体的业务逻辑的时候(如果耗时比较长),我们无法获取到其他用户连接上来的Socket。
群发消息服务器端:
package tcpandudp.threadssockettest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set; /**
* @ClassName Server
* @projectName: object1
* @author: Zhangmingda
* @description: 群发消息
* date: 2021/5/11.
*/
public class Server {
private static Set<Socket> sockets = Collections.synchronizedSet(new HashSet<>());
private static class MyRunnable implements Runnable {
/**
* 当前线程要处理的客户端Socket
*/
private Socket socket; /**
* @param socket 构造客户端socket
*/
public MyRunnable(Socket socket) {
this.socket = socket;
} /**
* 具体处理每个客户端连接请求的逻辑,封装到线程run方法中
*/
@Override
public void run() {
//获取客户端发送来的数据
String content = null;
try (BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()))){
/**
* 每次读取一行,读取一行就广播给所有客户端,直到读尽,阻塞住,
*/
while ((content = br.readLine()) != null){
//广播给所有客端 通过socket.getOutputStream()获取输出流OutputStream
for (Socket s : sockets){
PrintStream printStream = new PrintStream(s.getOutputStream());
printStream.println(content);
}
}
}catch (SocketException e){
System.out.println(socket.getInetAddress() + "已断开");;
}
catch (IOException e) {
e.printStackTrace();
}finally {
/**
* 当客户端断开连接while循环跳出,关闭客户端连接,从客户端Set集合中移除客户端Socket
*/
sockets.remove(this.socket);
try {
this.socket.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
this.socket = null;
}
}
}
}
public static void main(String[] args) throws IOException {
/**
* 服务端监听端口
*/
ServerSocket serverSocket = new ServerSocket(8888);
/**
* 启动后一直等待客户端连接,获取到一个客户端就保存到客户端集合中,
* 然后启动一个新的线程处理对应客户端请求,
* 主线程再次等待新的客户端建联
*/
while (true){
Socket client = serverSocket.accept();
sockets.add(client);
new Thread(new MyRunnable(client)).start();
}
}
}
客户端:
package tcpandudp.threadssockettest;

import java.io.*;
import java.net.Socket; /**
* @ClassName ClientTest
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/5/11.
*/
public class ClientTest {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8888);
/**
* 启动一个线程持续读取服务端返回的数据
*/
new Thread(){
@Override
public void run() {
String content = null;
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
//阻塞式等待服务端回复的数据
while ((content = bufferedReader.readLine()) != null){
System.out.println("收到服务端返回数据:" + content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
/**
* 主线程发送数据
*/
try (PrintStream printStream = new PrintStream(socket.getOutputStream());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))
){
String line = null;
/**
* 如果输入的不是exit字符串,就发送输入的数据,然后一直死循环阻塞等待再次输入数据
* 获取到了字符,内容是exit while循环false 退出
*/
while ((line = bufferedReader.readLine()) != null && !line.equals("exit")){
printStream.println(line);
}
}
}
}

使用线程安全的ConcurrentHashMap记录客户端Socket信息(要求客户端连接后提供用户名,否则不加入群聊):

package tcpandudp.threadssockettest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; /**
* @ClassName Server
* @projectName: object1
* @author: Zhangmingda
* @description: 群发消息
* date: 2021/5/11.
*/
public class ServerRemberUsername {
/**
* 记录客户端信息的线程安全Map
*/
private static ConcurrentHashMap<Socket,String> sockets = new ConcurrentHashMap<>(); /**
* 单线程处理逻辑
*/
private static class MyRunnable implements Runnable {
/**
* 当前线程要处理的客户端Socket
*/
private Socket socket; /**
* @param socket 构造客户端socket
*/
public MyRunnable(Socket socket) {
this.socket = socket;
} /**
* 具体处理每个客户端连接请求的逻辑,封装到线程run方法中
*/
@Override
public void run() {
//获取客户端发送来的数据
String content = null;
try (BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()))){
/**
* 每次读取一行,读取一行就广播给所有客户端,直到读尽,阻塞住,
*/
while ((content = br.readLine()) != null){
/**
* 判断是否要设置用户名
*/
if (content.startsWith("username:")){
PrintStream printStream = new PrintStream(socket.getOutputStream());
if (sockets.keySet().contains(socket)){
printStream.println("您已设置用户名:" +sockets.get(socket) + "无需重新设置");
}else {
String username = "";
//格式正确
if (content.split(":").length > 1){
//获取用户名并去除前后空格
username = content.split(":")[1].trim();
System.out.println("username:"+username);
//用户名不为空设置用户名
if (! "".equals(username)){
sockets.put(socket,username);
printStream.println("成功设置用户名:"+username);
}else {
printStream.println("用户名不允许为空");
}
}
else {
printStream.println("用户名设置有误,格式为username:XXXXX");
}
}
}else {
/**
* 普通消息判断是否已登录,登录后发消息否则提示先登录
*/
if (sockets.keySet().contains(socket)){
//广播给所有客端 通过socket.getOutputStream()获取输出流OutputStream
for (Socket s : sockets.keySet()){
PrintStream printStream = new PrintStream(s.getOutputStream());
printStream.println(sockets.get(socket) + ":" + content);
}
}else {
//未登录提示先登录
PrintStream printStream = new PrintStream(socket.getOutputStream());
printStream.println("请先登录");
}
}
}
}catch (SocketException e){
System.out.println(socket.getInetAddress() + "已断开");;
}
catch (IOException e) {
e.printStackTrace();
}finally {
/**
* 当客户端断开连接while循环跳出,关闭客户端连接,从客户端Set集合中移除客户端Socket
*/
sockets.remove(this.socket);
try {
this.socket.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
this.socket = null;
}
}
}
}
public static void main(String[] args) throws IOException {
/**
* 服务端监听端口
*/
ServerSocket serverSocket = new ServerSocket(8888);
/**
* 启动后一直等待客户端连接,获取到一个客户端就保存到客户端集合中,
* 然后启动一个新的线程处理对应客户端请求,
* 主线程再次等待新的客户端建联
*/
while (true){
Socket client = serverSocket.accept();
new Thread(new MyRunnable(client)).start();
}
}
}
 

java 网络编程基础 TCP/IP协议:服务端ServerSocket;客户端Socket; 采用多线程方式处理网络请求的更多相关文章

  1. 嵌入式linux的网络编程(1)--TCP/IP协议概述

    嵌入式linux的网络编程(1)--TCP/IP协议概述 1.OSI参考模型及TCP/IP参考模型 通信协议用于协调不同网络设备之间的信息交换,它们建立了设备之间互相识别的信息机制.大家一定都听说过著 ...

  2. 【转载】[基础知识]【网络编程】TCP/IP

    转自http://mc.dfrobot.com.cn/forum.php?mod=viewthread&tid=27043 [基础知识][网络编程]TCP/IP iooops  胖友们楼主我又 ...

  3. 【网络编程1】网络编程基础-TCP、UDP编程

    网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...

  4. DSAPI多功能组件编程应用-HTTP监听服务端与客户端_指令版

    前面介绍了DSAPI多功能组件编程应用-HTTP监听服务端与客户端的内容,这里介绍一个适用于更高效更快速的基于HTTP监听的服务端.客户端. 在本篇,你将见到前所未有的超简化超傻瓜式的HTTP监听服务 ...

  5. QTcpSocket-Qt使用Tcp通讯实现服务端和客户端

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QTcpSocket-Qt使用Tcp通讯实现服务端和客户端     本文地址:https:// ...

  6. 常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq

    常量,字段,构造方法   常量 1.什么是常量 ​ 常量是值从不变化的符号,在编译之前值就必须确定.编译后,常量值会保存到程序集元数据中.所以,常量必须是编译器识别的基元类型的常量,如:Boolean ...

  7. TCP/IP网络编程之基于UDP的服务端/客户端

    理解UDP 在之前学习TCP的过程中,我们还了解了TCP/IP协议栈.在四层TCP/IP模型中,传输层分为TCP和UDP这两种.数据交换过程可以分为通过TCP套接字完成的TCP方式和通过UDP套接字完 ...

  8. java网络编程基础——TCP网络编程一

    基于TCP协议的网络编程 TCP/IP协议是一种可靠的网络协议,它的通信的两端各自建立一个Socket,从而在通信的两端之间形成网络虚拟链路. Java使用Socket对象来代表两端的通信端口,并通过 ...

  9. 网络基础tcp/ip协议一

    计算机网络: 硬件方面:通过线缆将网络设备和计算机连接起来 软件方面:操作系统,应用软件,应用程序通过通信线路互连 实现资源共享,信息传递 计算机网络的功能: 数据通信 资源共享 增加可靠性 提高系统 ...

随机推荐

  1. Python之用型号构成一个三角形代码

    #coding=utf-8 #******直角三角形*********** #左下角三角形 for i in range(1,6):     print '*'*i print "=&quo ...

  2. Codeforces 436E - Cardboard Box(贪心/反悔贪心/数据结构)

    题面传送门 题意: 有 \(n\) 个关卡,第 \(i\) 个关卡玩到 \(1\) 颗星需要花 \(a_i\) 的时间,玩到 \(2\) 颗星需要 \(b_i\) 的时间.(\(a_i<b_i\ ...

  3. R语言与医学统计图形【7】低级绘图函数

    R语言基础绘图系统 基础绘图包之低级绘图函数--气泡图.一页多图.背景网格.添加线条和散点.数学表达式 4.气泡图 symbols是高级绘图函数,可在图上添加标记,标记的形状包括:circles,sq ...

  4. DRF知识点总结

    1. Web API接口 2. Restful接口规范 RDF请求流程及主要模块分析

  5. 中兴交换机基础配置(备份、dhcp中继、monitor)

    1. 备份配置 格式: copy tftp/sftp/ftp [vrf mng] root: 本地文件 远端文件 1. 通过tftp进行备份,vrf mng表示指定使用管理口链路连接 copy tft ...

  6. 电脑盘符为什么从C盘开始?A盘和B盘去哪了?

    虽然我们几乎每天都在跟电脑打交道,但是不知道大家有没有过疑惑,为什么电脑盘符是从C盘开始命名呢?有没有A盘和B盘??A盘和B盘去哪了??? 其实,A盘和B盘是真实存在的. 在早期的DOS时代,计算机的 ...

  7. Redis - 1 - linux中使用docker-compose安装Redis - 更新完毕

    0.前言 有我联系方式的那些半吊子的人私信问我:安装Redis有没有更简单的方式,网上那些文章和视频,没找到满意的方法,所以我搞篇博客出来说明一下我的安装方式吧 1.准备工作 保证自己的linux中已 ...

  8. A Child's History of England.16

    CHAPTER 5 ENGLAND UNDER CANUTE THE DANE Canute reigned eighteen years. He was a merciless King at fi ...

  9. Hadoop的HA机制浅析

    Zookeeper在Hadoop的HA中的应用 非HA的弊端: HDFS集群的分布式存储是靠namenode节点(namenode负责响应客户端请求)来实现.在非HA集群中一旦namenode宕机,虽 ...

  10. JS去除对象或数组中的空值('',null,undefined,[],{})

    javascript去掉对象或数组中的'',null,undefined,[],{}.思路就是创建一个新的空对象,然后对传入的对象进行遍历,只把符合条件的属性返回,保留有效值,然后就相当于把空值去掉了 ...