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协议一
计算机网络: 硬件方面:通过线缆将网络设备和计算机连接起来 软件方面:操作系统,应用软件,应用程序通过通信线路互连 实现资源共享,信息传递 计算机网络的功能: 数据通信 资源共享 增加可靠性 提高系统 ...
随机推荐
- 从零开始学Kotlin第五课
函数式编程入门: package EL fun main(args: Array<String>) { var names= listOf<String>("tom& ...
- 基于Docker搭建Maven私服Nexus,Nexus详解
备注:首先在linux环境安装Java环境和Docker,私服需要的服务器性能和硬盘存储要高一点,内存不足可能到时启动失败,这里以4核8GLinux服务器做演示 一:基于Docker安装nexus3 ...
- C# 将Excel转为PDF时自定义表格纸张大小
通过后端程序将Excel表格转为PDF格式时,直接转换后的PDF效果可能出现表格页面过小或者过大,导致页面内容分布不均.要改善转换后的文档效果,只需在转换前自定义表格纸张大小,即可调整转换后的PDF页 ...
- Java培训机构如何选择才能避免被骗?
近年来,随着IT行业的快速崛起,各类互联网人才供不应求,而Java工程师作为目前最为火爆的岗位之一,更是以高薪+高新技术的标签受到了人们的广泛关注.许多年轻人也看到了这个行业的发展前景,决定报名培训机 ...
- 洛谷 P6783 - [Ynoi2008] rrusq(KDT+势能均摊+根号平衡)
洛谷题面传送门 首先显然原问题严格强于区间数颜色,因此考虑将询问离线下来然后用某些根号级别复杂度的数据结构.按照数颜色题目的套路,我们肯定要对于每种颜色维护一个前驱 \(pre\),那么答案可写作 \ ...
- 洛谷 P3239 [HNOI2015]亚瑟王(期望+dp)
题面传送门 感觉是道挺好的题,可惜当时没写题解来着的? 根据期望的线性公式,我们求出每个卡牌被发动的概率 \(q_i\),然后 \[ans=\sum\limits_{i=1}^np_id_i \] 于 ...
- R 小知识积累
1.grep 1 ## a为一个data frame,取含有RNA-Seq的行 2 index <- grep("RNA-Seq", a$Assay_Type) 3 b &l ...
- mac 下 如何在同一窗口打开多个终端并实现快捷键切换
相信大家编代码的时候都会遇到,每次需要在头文件,库文件和源码文件中编代码的时候,总是需要在几个文件中切换来切换去的,而且一个文件就一个终端窗口,每次都要用鼠标点来点去,非常麻烦,所以如果能把这几个文件 ...
- IPFS是什么?IPFS原理、IPFS存储
以下内容调研截止到2021/11/5日 IPFS简介 IPFS是一种内容可寻址.点对点.分布式文件系统.IPFS采用内容-地址寻址技术,即通过文件内容进行检索而不是通过文件的网络地址.简单来说,就是对 ...
- 学习java 7.26
学习内容: 进度条是图形界面中广浅个较大的文件时,操作系统会显示一个进度条,用于标识复制操作完成的比例:当启动Eclipse等程序时,因为需要加载较多的资源,故而启动速度较慢,程序也会在启动过程中显示 ...