Java网络编程——TCP

TCP编程

什么是Socket?

在开发网络应用程序的时候,会遇到Socket这个概念。Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络。

Socket、TCP和部分IP的功能都是由操作系统提供的,不同的编程语言只是提供了对操作系统调用的简单的封装,例如,Java提供的几个Socket相关的类就封装了操作系统提供的接口:ServerSocket类,Socket类。

为什么需要Socket进行网络通信?

因为仅仅通过IP地址进行通信是不够的,同一台计算机同一时间会运行多个网络应用程序。当操作系统抽象出Socket接口,每个应用程序需要各自对应到不同的Socket,数据包才能根据Socket正确地发到对应的应用程序。

一个Socket就是由IP地址和端口号(范围0~65535)组成,可以把Socket简单理解为IP地址加端口号。端口号总是由操作系统分配,它是一个0~65535之间的数字,其中,小于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();
			}
			
			
		}
	}

}

运行结果:

  • 57
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值