java 网络编程基础 TCP/IP协议:服务端ServerSocket;客户端Socket; 采用多线程方式处理网络请求
1、Java中客户端和服务器端通信的简单实例
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、采用多线程方式处理网络请求
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; 采用多线程方式处理网络请求的更多相关文章
- 嵌入式linux的网络编程(1)--TCP/IP协议概述
嵌入式linux的网络编程(1)--TCP/IP协议概述 1.OSI参考模型及TCP/IP参考模型 通信协议用于协调不同网络设备之间的信息交换,它们建立了设备之间互相识别的信息机制.大家一定都听说过著 ...
- 【转载】[基础知识]【网络编程】TCP/IP
转自http://mc.dfrobot.com.cn/forum.php?mod=viewthread&tid=27043 [基础知识][网络编程]TCP/IP iooops 胖友们楼主我又 ...
- 【网络编程1】网络编程基础-TCP、UDP编程
网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...
- DSAPI多功能组件编程应用-HTTP监听服务端与客户端_指令版
前面介绍了DSAPI多功能组件编程应用-HTTP监听服务端与客户端的内容,这里介绍一个适用于更高效更快速的基于HTTP监听的服务端.客户端. 在本篇,你将见到前所未有的超简化超傻瓜式的HTTP监听服务 ...
- QTcpSocket-Qt使用Tcp通讯实现服务端和客户端
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QTcpSocket-Qt使用Tcp通讯实现服务端和客户端 本文地址:https:// ...
- 常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq
常量,字段,构造方法 常量 1.什么是常量 常量是值从不变化的符号,在编译之前值就必须确定.编译后,常量值会保存到程序集元数据中.所以,常量必须是编译器识别的基元类型的常量,如:Boolean ...
- TCP/IP网络编程之基于UDP的服务端/客户端
理解UDP 在之前学习TCP的过程中,我们还了解了TCP/IP协议栈.在四层TCP/IP模型中,传输层分为TCP和UDP这两种.数据交换过程可以分为通过TCP套接字完成的TCP方式和通过UDP套接字完 ...
- java网络编程基础——TCP网络编程一
基于TCP协议的网络编程 TCP/IP协议是一种可靠的网络协议,它的通信的两端各自建立一个Socket,从而在通信的两端之间形成网络虚拟链路. Java使用Socket对象来代表两端的通信端口,并通过 ...
- 网络基础tcp/ip协议一
计算机网络: 硬件方面:通过线缆将网络设备和计算机连接起来 软件方面:操作系统,应用软件,应用程序通过通信线路互连 实现资源共享,信息传递 计算机网络的功能: 数据通信 资源共享 增加可靠性 提高系统 ...
随机推荐
- nginx安装与配置1-nginx安装
反向代理: 客户端不需要配置就可以访问,将请求发送到反向代理服务器, 由反向代理服务器选择目标服务器获取数据,再返回客户端,对外暴露代理服务器地址,隐藏真实ip 负载均衡: 客户端请求nginx等服务 ...
- 『与善仁』Appium基础 — 12、Appium的安装详解
目录 (一)Appium server安装 方式一:(桌面方式:推荐) 1.Appium Desktop下载 2.Appium Desktop安装 3.Appium Desktop使用 方式二:(No ...
- Atcoder Grand Contest 023 E - Inversions(线段树+扫描线)
洛谷题面传送门 & Atcoder 题面传送门 毒瘤 jxd 作业-- 首先我们不能直接对所有排列计算贡献对吧,这样复杂度肯定吃不消,因此我们考虑对每两个位置 \(x,y(x<y)\), ...
- 洛谷 P7520 - [省选联考 2021 A 卷] 支配(支配树)
洛谷题面传送门 真·支配树不 sb 的题. 首先题面已经疯狂暗示咱们建出支配树对吧,那咱就老老实实建呗.由于这题数据范围允许 \(n^2\) 算法通过,因此可以考虑 \(\mathcal O(n^2 ...
- Parallel NetCDF 简介
Parallel NetCDF API 所有C接口前加ncmpi前缀,Fortran接口前加nfmpi前缀 函数返回整数 NetCDF 状态变量 1. Variable and Parameter T ...
- time 查看命令执行时间
在命令执行完成之后就会打印出CPU的使用情况: real 0m5.064s <== 实际使用时间(real time) user 0m0.020s <== 用 ...
- 宏GENERATED_BODY做了什么?
Version:4.26.2 UE4 C++工程名:MyProject \ 一般语境下,我们说c++源码的编译大体分为:预处理.编译.链接; cppreference-translation_phas ...
- 使用clion阅读eos源码
配置mingw 安装clion 从github克隆源码 使用clion open打开 在cmake上使用boost: sudo apt-get install libboost-all-dev
- 巩固javaweb的第二十二天
巩固内容: 使用表单数据 : 要对用户输入的信息进行验证,需要先获取输入信息.每个表单元素都属于一个 form 表单,要获取信息,需要先获取 form,然后访问表单元素的值. 有两种方式可以获取 fo ...
- 巩固javaweb第十五天
巩固内容: 单选按钮: 在注册功能中,用户选择学历使用的是单选按钮,并且是多个单选按钮,每个选项对 应一个单选按钮,用户只能选择其中一个,这多个单选按钮的格式相同.如果用户要输入 的信息只有少数几种可 ...