什么是Socket?
在开发网络应用程序的时候,会遇到Socket这个概念。Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络。
Socket、TCP和部分IP的功能都是由操作系统提供的,不同的编程语言只是提供了对操作系统调用的简单的封装,例如,Java提供的几个Socket相关的类就封装了操作系统提供的接口:ServerSocket类,Socket类。
为什么需要Socket进行网络通信?
因为仅仅通过IP地址进行通信是不够的,同一台计算机同一时间会运行多个网络应用程序。当操作系统抽象出Socket接口,每个应用程序需要各自对应到不同的Socket,数据包才能根据Socket正确地发到对应的应用程序。
一个Socket就是由IP地址和端口号(范围065535)组成,可以把Socket简单理解为IP地址加端口号。端口号总是由操作系统分配,它是一个065535之间的数字,其中,小于1024的端口属于特权端口。需要管理员权限,大于1024的端口可以由任意用户的应用程序打开。
使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器和客户端就成功的建立了一个TCP连接,双方后续就可以随时发送和接收数据。
因此,当Socket连接成功地在服务器和客户端之间建立后:
1、对服务器端来说,它的Socket是指定的IP地址和指定的端口号;
2、对客户端来说,它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。
注:
TCP协议是一种可靠、面向连接的协议,而TCP编程依靠于TCP协议,通过Socket来建立连接。
TCP编程实例:
TCP编程实现游戏人物选角
服务器端:
先创建一个List集合,用来存放人物角色,再创建一个Map集合,用来存放玩家与对应的人物角色<玩家,人物角色>。
再创建一个ServerSocket对象,通过while(true)死循环来使服务器一直运行。使用accept()方法来接收客户端对象,返回一个Socket对象。通过输入流来获取客户端发来的选角请求。通过输出流将人物角色返回至Socket并且反馈给客户端。
在获取玩家姓名时,先判断是否为空,如果为空则显示"【获取角色失败,请输入正确的玩家姓名】",如果不为空判断玩家姓名是否重复,如果玩家姓名重复则显示"【该玩家已存在!】",最后判断人物角色是否分配完成(List 集合是否为空【当人物角色被选择则会在List集合中将此角色删除,从而避免角色被重复分配】)若人物角色分配完成则显示"【角色分配完成,游戏开始~】"。
代码实现:
package com.hwh.demo1;
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;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class CharacterServer {
public static void main(String[] args) {
List<String> role=new ArrayList<String>() {
{
add("圣女");add("下毒者");add("杀手");add("士兵");
add("市长");add("洗衣妇");add("间谍");add("图书管理员");
add("守鸦人");add("小恶魔");add("酒鬼");add("厨师");
add("调查员");add("男爵");add("隐士");add("共情者");
add("占卜师");add("僧侣");add("管家");add("圣徒");add("猩红女郎");
}
};
Map<String, String> map=new LinkedHashMap<String, String>();
try (ServerSocket server = new ServerSocket(3333)) {
while(true) {
Socket client=server.accept();
String clientIp=client.getInetAddress().getHostAddress();
try(BufferedReader reader=new BufferedReader(new InputStreamReader(client.getInputStream()));
BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))){
//来自玩家的选角
String player=reader.readLine().trim();
if(player.length()!=0) {
if(role.isEmpty()) {
writer.write("【角色分配完成,游戏开始~】");
writer.newLine();
writer.flush();
}else {
if(!map.containsKey(player)){
System.out.println("来自玩家:"+player+"的选角");
//获取(随机下标)玩家角色
int index=(int)(Math.random()*role.size());
String character=role.get(index);
map.put(player, character);
role.remove(index);
//发送角色至玩家
writer.write(character);
writer.flush();
}else {
writer.write("【该玩家已存在!】");
writer.flush();
}
}
}else {
writer.write("【获取角色失败,请输入正确的玩家姓名】");
writer.flush();
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客户端:
先创建一个Socket对象。将服务器端的IP 地址和端口号传入对象中,通过Scanner在控制台输入玩家姓名;
根据人物角色的个数来设置客户端循环的次数,避免循环过多。
然后通过输出流将请求发给Socket对象并反馈给服务器端,发送完后调用shutdownOutput()这个方法暂时结束本次输出。
服务器接收到请求后将人物角色发送Socket对象并且返回给客户端,客户端通过输入流接收到信息。
代码实现:
package com.hwh.tcp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class PlayerClient {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("请输入玩家姓名:");
int i=0;
while(i++<=21) {
try (Socket client=new Socket("192.168.254.189",3333);
BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
BufferedReader reader=new BufferedReader(new InputStreamReader(client.getInputStream()))){
//从控制台获取输入的玩家姓名
String player=input.nextLine();
if(player.length()!=0) {
//发送玩家姓名至角色
writer.write(player);
writer.flush();
//暂时结束本次输出
client.shutdownOutput();
//获取玩家的角色
String character=reader.readLine();
if(character.equals("【角色分配完成,游戏开始~】")) {
System.out.println(character);
}else if(character.equals("【获取角色失败,请输入正确的玩家姓名】")){
System.out.println(character);
}else if(character.equals("【该玩家已存在!】")){
System.out.println(character);
}else{
System.out.println("您获得的角色为:"+character);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
版权归原作者 鸣筝鸣筝 所有, 如有侵权,请联系我们删除。