基于TCP的socket通讯代码实例-字节流和字符流

笔者今天写socket通讯时发现read方法会出现阻塞,查找资料后终于找到了原因,在此记录下载,希望能帮到其他的朋友,也避免自己以后再犯这个错误
socket通讯有用字节流的,有用字符流的,字符流是对字节流的包装,
字节流:
服务端

package bybyte;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer {
	public static void main(String[] args) throws IOException {
		ServerSocket serverSocket = new ServerSocket(9999);
		//此方法在没有客户端连接时会一直处于阻塞状态
		Socket socket = serverSocket.accept();
		InputStream is =socket.getInputStream();
		OutputStream os = socket.getOutputStream();
		
		//读数据
		byte[] 

    readbuf = new byte[1024];
    		StringBuilder stringBuilde

r  = new StringBuilder();
		int len = is.read(readbuf);
		while(len!=-1)
		{
			stringBuilder.append(new String(readbuf,0,len));
			len = is.read();
		}
		System.out.println("message from client:"+stringBuilder.toString());
		
		
		//写数据
		os.write("Hello,client".getBytes());
		os.flush();
		//shutdownOutput会关闭输出流,但是连接还是建立着的,相当于提示客户端服务器输出完毕了。
		//这样客户端才能在read方法里读出-1来,退出循环
		socket.shutdownOutput();
		
		os.close();
		is.close();
		socket.close();
		serverSocket.close();
		
		
	}
}

客户端:

package bybyte;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class SocketClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//以这种方式创建的socket会自动去连本地的9999端口
		Socket socket = new Socket("127.0.0.1",9999);
		
		//写数据
		OutputStream os =socket.getOutputStream();
		os.write("hello,server,I'm client".getBytes());
		os.flush();
		//shutdownOutput会关闭输出流,但是连接还是建立着的,相当于提示服务器我客户端输出完毕了。
		//这样服务器才能在read方法里读出-1来,退出循环,不加shutdown服务器会在read方法处阻塞
		socket.shutdownOutput();
		//读数据
		InputStream is = socket.getInputStream();
		byte[] readbuf = new byte[1024];
		StringBuilder sb = new StringBuilder();
		int len = is.read(readbuf);
		while(len!=-1)
		{
			sb.append(new String(readbuf,0,len));
			len = is.read(readbuf);
		}
		System.out.println("message from server:"+sb.toString());
		
		
	}
}

注:客户端socket通过输出流写数据时,数据会传输到服务端,服务段通过read方法会读取到数据,第一次读取完后,因为没有到流的末尾,所以执行while循环,此时却无数据可读,read就会处于阻塞状态。利用shutdownoutput方法使得服务端在读完数据后识别出流的末尾,read方法返回-1,自然就退出循环了。
字符流:
服务端:

package bystring;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer {
	public static void main(String[] args) throws IOException {
		ServerSocket serversocket = new ServerSocket(9999);
		Socket accept = serversocket.accept();
		
		//把字节流封装成字符流
		BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream()));
		PrintWriter pw = new  PrintWriter(accept.getOutputStream());
		
		//读数据
		//此方法会读取一行,一行的判断标准是是否读取到了"\r"或"\n"
		String message = br.readLine();
		System.out.println("message from client:"+message);
		
		pw.write("hello,client,I'm server \n");
		pw.flush();
	
		pw.close();
		br.close();
		serversocket.close();
		accept.close();
		
		
	}
}

客户端:

package bystring;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
/**客户端
 */
public class SocketClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		Socket socket  = new Socket("127.0.0.1",9999);
		
		//封装为字符流
		PrintWriter pw = new PrintWriter(socket.getOutputStream());
		BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		
		//写数据
		//在写数据是一定要加上"\n"或"\r",不然服务器读数据readline因为没有独好换行符会一直处于阻塞状态
		pw.write("hello,server,I'm client \n");
		pw.flush();
		
		//读数据
		String message  = br.readLine();
		System.out.println("message from server :"+message);
		
		br.close();
		pw.close();
		socket.close();
		
	}
}

注:在实际开发中可考虑把服务端放置在一个线程里,死循环里调用accept方法,因为accept方法是阻塞的,只有在客户端进行连接时才执行后续代码,才需要cpu资源。在阻塞时是不会消耗cpu资源的。
在实际生产中进行数据传输时,应该使用这种模式:规定一定长度的头(比如10个字节长度),头里的数据保存着实际要传输数据的字节长度,则读数据时,只需先读取出头里的数据变换为整数length,在读取length个字节长度的数据即为要传输的实际数据

当一个客户端需要和多个服务器中的一个建立链接时

   //服务器的ip和port组成的map
public void SocketClient(Map<String,String> addressMap) {
	int sum=0;
	for(Map.Entry<String, String> entry:addressMap.entrySet())
	{
		try {
			Socket socket = new Socket(entry.getKey(), Integer.parseInt(entry.getValue()));
			//只要与其中一个建链就不再连接其他服务器了了
			break;
		} catch (Exception e) {
			System.out.println("与服务器("+entry.getKey()+")端口("+entry.getValue()+")建链失败");
		}
	}
	if(sum==addressMap.size())
	{
		System.out.println("与所有的服务器建立连接失败");
	}

笔者学识有限,若有不当或错误的地方,希望大家能指正,我会尽快进行修改

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值