网络编程 自制通信小工具(内附源代码)

目录

 

一 介绍

二 编写源代码

2.1 创建服务端和客户端

2.3 创建功能

三 效果图

四 总结

五 源代码


一 介绍

读者掌握多线程,io流,网络编程基础知识即可看懂代码以及自制,十分易懂,简单易学,个别代码不懂的也可在网上找资料,后面会附上源代码,希望你们都有收获

二 编写源代码

2.1 创建服务端和客户端

通信肯定至少要2台服务器的,这里为了方便测试就建立2个项目,然后导出去方便观察,读者可以直接建立2个包,一样可以实现,甚至可以在我的基础上再增加或者修改一些功能

public class MyServer  {
	//服务端
	public static void main(String[] args) throws Exception {//抛出异常
		//创建侦听端口
		ServerSocket ss=new ServerSocket(8998);//端口要一致且不能跟电脑已有端口重复
		//调用侦听端口对象的accept方法接收客户端的连接请求
		Socket socket;//下面应该要接收一个accept方法接收客户端信息的,我放在下面
public class MyClient  {
//客户端
	public static void main(String[] args) throws Exception {
		//创建一个socket对象,请求连接远程服务器
		Socket socket=new Socket("localhost",8998);//本机及端口
		

TCP三次握手过程

1 主机A通过向主机B 发送一个含有同步序列号的标志位的数据段给主机B ,向主机B 请求建立连接,通过这个数据段, 主机A告诉主机B 两件事:我想要和你通信;你可以用哪个序列号作为起始数据段来回应我.

//向客户端发送消息
		boolean flag=true;
		Scanner input=new Scanner(System.in);
		while(true) {
			while(flag) {
				System.out.println("协议:群聊前缀“all:”私聊“人名:”,登录“login:”");
				flag=false;
				
			}
			System.out.println("请输入消息:");
			String message=input.next();
			byte[] buffer=message.getBytes();
			
			//使用输出流发送消息给服务器
			OutputStream os=socket.getOutputStream();
			
			os.write(buffer);

2 主机B 收到主机A的请求后,用一个带有确认应答(ACK)和同步序列号(SYN)标志位的数据段响应主机A,也告诉主机A两件事: 我已经收到你的请求了,你可以传输数据了;你要用哪佧序列号作为起始数据段来回应我

while(true){
			
			System.out.println("准备接收客户端的连接");
			 socket=ss.accept();//负责和连接的客户通信
			System.out.println("有一个客户端连接过来:"+socket.getRemoteSocketAddress());
			
			//创建线程,能同时接收端口请求和接收消息
			ServerRecive sr=new ServerRecive(socket);
			sr.start();
			
			
			
		}

3 主机A收到这个数据段后,再发送一个确认应答,确认已收到主机B 的数据段:"我已收到回复,我现在要开始传输实际数据了 这样3次握手就完成了,主机A和主机B 就可以传输数据了. 

//创建一个线程接收服务器回复
		ClientReceiver cr=new ClientReceiver(socket);
		cr.start();

我这里只用了一个线程,你们也可以考虑用2个线程做一下

2.3 创建功能

聊天一般分为群聊和私聊,所以要有这2个功能,但是怎么区分群聊和私聊呢?QQ里是@全体成员和@个人昵称来区分的,我们也可以定一个类似的协议,然后做一个功能得到区分他们的办法,

这里做了个没有密码的登录功能

while(true) {
							
						
						//判断当前用户是否登录
						String address=socket.getRemoteSocketAddress().toString();
						InputStream is = socket.getInputStream();
						byte[] buffer=new byte[1024];
						int r=is.read(buffer);
						String mess=new String(buffer,0,r);
						 int i=0;
						if(mess.startsWith("login:")) {
							
							UserManager.login(address, new Customer(mess.split(":")[1], socket));
							OutputStream os=socket.getOutputStream();
							
								byte[] reply="登录成功".getBytes();
								os.write(reply);



else {
								
								OutputStream os=socket.getOutputStream();
								byte[] reply="请先登录".getBytes();
								os.write(reply);
							
								
							}}




//将登录信息记录到map集合中
	public static void login(String address, Customer customer) {
		users.put(address, customer);
	}

然后就是区分群聊跟私聊了

if(UserManager.checkLogin(address)) {
								
								String[] s=mess.split(":");//mess里面存储了客户端传过来的信息
								//根据消息
								//判断群聊还是私发
								if(s[0].equals( "all")) {//意思就是说前缀是all:
									Collection<Customer> users=UserManager.getAllCustomer();//得到所有人的信息,创了个类,里面存储了用户名和地址
									System.out.println("现在有"+users.size()+"人在线");
									
									for (Customer customer : users) {//遍历,把自己说的话传给每一个人
										if(!(customer.getUsername().equals(UserManager.getUsername(address).getUsername()))) {
										
											OutputStream os=customer.getSocket().getOutputStream();
											byte[] messages=(UserManager.getUsername(address).getUsername()+
													"说:"+s[1]).getBytes();
											os.write(messages);
										}//忽略自己
												
												
									}
								}else {
									//根据消息要转发给的用户名从map集合获取用户对应的socket
									Socket target=UserManager.getSocketByUsername(s[0]);
									OutputStream os=target.getOutputStream();
									byte[] messages=(UserManager.getUsername(address).getUsername()+
											"对你说:"+s[1]).getBytes();
									os.write(messages);
											
								}

三 效果图

四 总结

简单吧,一下子就弄完了,代码就是个不断写的过程,希望读者可以多写几遍,加深理解,光copy没用的,这里面要注意的细节太多了,一个不小心就是一连串的bug。比如测试时中间要用英文符号

五 源代码

package qqserver;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class ServerRecive extends Thread {
	//服务器处理线程
	
		private Socket socket;

		public ServerRecive(Socket socket) {
			super();
			this.socket = socket;
		}
		@Override
		public void run() {
			//接收客户端发来的消息
			
					
					try {
						while(true) {
							
						
						//判断当前用户是否登录
						String address=socket.getRemoteSocketAddress().toString();
						InputStream is = socket.getInputStream();
						byte[] buffer=new byte[1024];
						int r=is.read(buffer);
						String mess=new String(buffer,0,r);
						 int i=0;
						if(mess.startsWith("login:")) {
							
							UserManager.login(address, new Customer(mess.split(":")[1], socket));
							OutputStream os=socket.getOutputStream();
							
								byte[] reply="登录成功".getBytes();
								os.write(reply);
							
							
						}else {
							
							if(UserManager.checkLogin(address)) {
								
								String[] s=mess.split(":");
								//根据消息
								//判断群聊还是私发
								if(s[0].equals( "all")) {
									Collection<Customer> users=UserManager.getAllCustomer();
									System.out.println("现在有"+users.size()+"人在线");
									
									for (Customer customer : users) {
										if(!(customer.getUsername().equals(UserManager.getUsername(address).getUsername()))) {
										
											OutputStream os=customer.getSocket().getOutputStream();
											byte[] messages=(UserManager.getUsername(address).getUsername()+
													"说:"+s[1]).getBytes();
											os.write(messages);
										}
												
												
									}
								}else {
									//根据消息要转发给的用户名从map集合获取用户对应的socket
									Socket target=UserManager.getSocketByUsername(s[0]);
									OutputStream os=target.getOutputStream();
									byte[] messages=(UserManager.getUsername(address).getUsername()+
											"对你说:"+s[1]).getBytes();
									os.write(messages);
											
								}
								
								//System.out.println(UserManager.getUsername(address)+"说"+mess);
							}else {
								
								OutputStream os=socket.getOutputStream();
								byte[] reply="请先登录".getBytes();
								os.write(reply);
							
								
							}}
						}
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

		}
		
	}


class UserManager{
	//保存所有已经登录的用户信息
	private static  Map<String, Customer> users=new HashMap<String, Customer>();
	
	//将登录信息记录到map集合中
	public static void login(String address, Customer customer) {
		users.put(address, customer);
	}
	//判断用户是否登录
	public static boolean checkLogin(String address) {
		return users.containsKey(address);
	}
	//根据address返回用户名
	public static Customer getUsername(String address) {
		
		return users.get(address);
	}
//根据用户名获取对应的socket
	public static Socket getSocketByUsername(String username) {
		Collection<Customer> customers=users.values() ;
		Socket target =null;
		for (Customer customer : customers) {
			if(customer.getUsername().equals(username)) {
				target=customer.getSocket();
			}
		}
		return target;
	}
	//返回所有已经登录的用户
	public static Collection<Customer> getAllCustomer() {
		return users.values();
	}
}
package qqserver;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

import sun.applet.Main;

public class MyServer  {
	
	public static void main(String[] args) throws Exception {
		//创建侦听端口
		ServerSocket ss=new ServerSocket(8998);
		//调用侦听端口对象的accept方法接收客户端的连接请求
		Socket socket;
		while(true){
			
			System.out.println("准备接收客户端的连接");
			 socket=ss.accept();//负责和连接的客户通信
			System.out.println("有一个客户端连接过来:"+socket.getRemoteSocketAddress());
			
			//创建线程,能同时接收端口请求和接收消息
			ServerRecive sr=new ServerRecive(socket);
			sr.start();
			
			
			
		}
		
		
	
	}
	

}
package qqclient;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class MyClient  {
	public static void main(String[] args) throws Exception {
		//创建一个socket对象,请求连接远程服务器
		Socket socket=new Socket("localhost",8998);
		
		//创建一个线程接收服务器回复
		ClientReceiver cr=new ClientReceiver(socket);
		cr.start();
		
		
		
		//向客户端发送消息
		boolean flag=true;
		Scanner input=new Scanner(System.in);
		while(true) {
			while(flag) {
				System.out.println("协议:群聊前缀“all:”私聊“人名:”,登录“login:”");
				flag=false;
				
			}
			System.out.println("请输入消息:");
			String message=input.next();
			byte[] buffer=message.getBytes();
			
			//使用输出流发送消息给服务器
			OutputStream os=socket.getOutputStream();
			
			os.write(buffer);
		}
		
		
		
	}

}
class ClientReceiver extends Thread{
	private Socket socket;
	
	public ClientReceiver(Socket socket) {
		super();
		this.socket = socket;
	}

	@Override
	public void run() {
		try {
			while(true) {
				
				InputStream in=socket.getInputStream();
				byte[] buffer=new byte[1000];
				int r=in.read(buffer);
				System.out.println(new String(buffer,0,r));
			}
			
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}
package qqserver;

import java.net.Socket;

public class Customer {
	private String username;
	private Socket socket;
	public Customer(String username, Socket socket) {
		super();
		this.username = username;
		this.socket = socket;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public Socket getSocket() {
		return socket;
	}
	public void setSocket(Socket socket) {
		this.socket = socket;
	}
	

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值