socket编程 UDP和TCP通信实现
socket编程 UDP和TCP通信实现
1、 socket通信模型
应用程序通过socket可以进行通信,可以使用UDP或者TCP协议
客户端和服务端的协议必须相对应,才能进行通信。
2、 TCP通信方式如下
2.1、TCP(传输控制协议)是一种面向连接的通讯协议,特点是能够保证成功率,数据安全性高但效率低。使用基于TCP协议的Socket通信,首先要通过IP地址和监听端口连接服务器端,然后获取输入输出流,从流中读取或写入数据。
2.2、创建服务器端
我们把服务器端建在Windows端,以电脑IP和一个未被占用的端口创建服务器端程序。在服务器端可能要维护多个连接的客户端,把所有的与服务器连接的客户端发来的消息经过处理返回给所有的客户端显示。首先创建一个JavaProject,并创建类TCPServer,
1)、新建一个java工程(工程名:TCPServer)
2)、点击finish后
3)、创建一个包(com.java)
4)、创建一个类(TCPSever)
5)、运行该服务器
单击右键找到(Run AS)点击java Application
启动了服务器
TCPServer源码如下:
package com.java;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 聊天室的服务端
*/
public class TCPServer {
//监听端口号
int port = 9987;
//Socket服务端
private ServerSocket mServerSocket;
//保存与服务端相连接的Socket
private Set<Socket> allSockets;
//线程池用于线程的重用
private ExecutorService mExecutorService;
//退出命令
private static final String exit = "EXIT";
public TCPServer(){
try {
//服务端Socket创建
mServerSocket = new ServerSocket(port);
//初始化与服务端相连接的所有Socket
allSockets = new HashSet<Socket>();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 启动服务端方法
* @throws IOException
*/
public void init(){
try {
//创建一个线程池
mExecutorService = Executors.newCachedThreadPool();
System.out.println("TCP服务端已启动,监听端口:"+port);
//循环等待客户端连接
while(true){
Socket mSocket = mServerSocket.accept();
//当有一个客户端连接
if(mSocket!=null){
//获取当前日期
String date = getDateByFormat(new Date(),"yyyy-MM-dd");
System.out.println(mSocket.getInetAddress()+"加入"+"\t["+date+"]");
allSockets.add(mSocket);
//开启一个客户端线程,如果已经开启会重用
mExecutorService.execute(new ServerThread(mSocket));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理客户端连接的线程
*/
class ServerThread extends Thread{
private Socket mSocket;
private BufferedReader mBufferedReader;
public ServerThread(Socket socket){
this.mSocket = socket;
try {
//从Socket中获取缓冲字符输入流 编码为UTF-8
mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream(),"UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run(){
try {
while(true){
//按行读取
String str = mBufferedReader.readLine();
if(exit.equals(str)){
allSockets.remove(mSocket);
mSocket.close();
//将某人离开的消息发到所有和服务端相连的客户端
sendMessageToAllClient("离开聊天室!");
break;
}
//将获取的消息发到所有和服务端相连的客户端
sendMessageToAllClient(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 将消息发到所有和服务端相连的客户端
* @param msg 消息字符串
*/
private void sendMessageToAllClient(String msg){
try {
if(msg!=null && !"".equals(msg.trim())){
String date = getDateByFormat(new Date(),"yyyy-MM-dd");
System.out.println("获取到消息:"+mSocket.getInetAddress()+"\t"+msg+"\t["+date+"]");
for(Socket socket:allSockets){
try {
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.println(mSocket.getInetAddress()+"\t["+date+"]");
pw.println(msg);
pw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 格式化时间
* @param strDate new Date()
* @param format "yyyyMMddHHmmss"
* @return String 表示时间的字符串
*/
public String getDateByFormat(Date strDate, String format){
String curTime = "";
SimpleDateFormat ss = new SimpleDateFormat(format);
curTime = ss.format(strDate);
return curTime;
}
/**
* 启动主程序
* @param args
*/
public static void main(String[] args) {
new TCPServer().init();
}
}
2.3客户端创建
客户端主要职责是在启动的时候连接服务器端,在任意时刻可以发送数据到服务器端,这样当服务器端接收到数据后就可以将数据转发给其他与服务器相连的客户端程序,同时客户端程序需要时将服务器发送的数据更新显示在界面上。
package com.android;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class ActivityMain extends Activity {
//客户端连接Socket
Socket mSocket = null;
//输入流
BufferedReader mBufferedReader = null;
//输出流
PrintWriter mPrintWriter = null;
//发送的消息内容
EditText msgEditText = null;
//显示消息内容
LinedText msgText = null;
//读取数据的线程标记
boolean flag = false;
//更新视图
Handler mHandler = new Handler() {
public void handleMessage(Message msg){
super.handleMessage(msg);
try{
String msgStr = msg.obj.toString();
//消息更新到TextView
msgText.append(msgStr);
msgText.append("\n");
} catch (Exception e) {
e.printStackTrace();
}
}
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Button sendBtn = (Button)findViewById(R.id.sendBtn);
msgEditText =(EditText)findViewById(R.id.msgEditText);
//ListView显示消息
msgText =(LinedText)findViewById(R.id.msgText);
//连接服务端
try {
//连接服务器
mSocket = new Socket("192.168.1.102", 9987);
//取得输入、输出流
//编码为GB2312
mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream(),"GB2312"));
mPrintWriter = new PrintWriter(mSocket.getOutputStream(), true);
}catch (Exception e) {
e.printStackTrace();
}
//发送消息
sendBtn.setOnClickListener(new OnClickListener(){
public void onClick(View v){
try {
//取得编辑框中我们输入的内容
String msg = msgEditText.getText().toString();
mPrintWriter.println(msg);
mPrintWriter.println();
mPrintWriter.flush();
msgEditText.setText("");
}catch (Exception e) {
e.printStackTrace();
}
}
});
flag = true;
//启动线程获取服务端发来的数据显示在界面上
Thread mThread = new Thread(new Runnable() {
public void run(){
while (flag){
try{
//读取一行
String msg = mBufferedReader.readLine();
if (!TextUtils.isEmpty(msg)){
//更新界面显示
Message message = new Message();
message.obj = msg;
mHandler.sendMessage(message);
}
//Thread.sleep(500);
}catch (Exception e){
e.printStackTrace();
}
}
}
});
mThread.start();
}
/**
* 返回退出
*/
@Override
public void onBackPressed() {
// TODO Auto-generated method stub
try {
mPrintWriter.print("EXIT\n");
mPrintWriter.flush();
flag = false;
//关闭连接
mPrintWriter.close();
mBufferedReader.close();
mSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
super.onBackPressed();
}
}
3、 UDP通信协议
UDP(用户数据协议)是一种无线连接的通信协议,类似于常用的短信、邮件。这种方式不需要连接状态,在每个数据报中包含了目的地址和内容,用户只需要简单的投递即可。该方法比TCP高效,但安全性不如TCP。
3.1、服务器的创建
package com.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPServer {
//监听端口号
int port = 2345;
//数据报对象
DatagramSocket socket = null;
public UDPServer(){
//实例化数据报Socket
try {
socket = new DatagramSocket(port);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 启动服务端方法
* @throws IOException
*/
public void init(){
//数据报对象
DatagramPacket packet;
try{
//缓冲区数组
byte[] buffer = new byte[256];
//输出运行信息
System.out.println("UDP服务端已启动,监听端口:"+port);
while (true){
//实例化数据报
packet = new DatagramPacket(buffer,buffer.length);
//接受请求
socket.receive(packet);
System.out.println("Received buffer:" + buffer.length);
//计算buffer中的有效数据长度
int l = 0;
for(byte b:buffer){
if(b!=0){
l++;
}
}
String receivedMessage = new String(packet.getData(),0,l);
//得到请求地址
InetAddress requestIp = packet.getAddress();
//输出请求地址
System.out.println("Received from ip:" + requestIp);
//得到请求端口
int requestPort = packet.getPort();
System.out.println("Received from port:" + port);
System.out.println("Received message:" + receivedMessage);
//Thread.sleep(5000);
//服务器返回信息
byte[] sendMessage = "消息已送达。".getBytes("UTF-8");
//创建发送数据报
DatagramPacket sendPacket = new DatagramPacket(sendMessage,sendMessage.length,requestIp,requestPort);
//发送数据报
socket.send(sendPacket);
}
}
catch (Exception e){
e.printStackTrace();
}
}
/**
* 启动主程序
* @param args
*/
public static void main(String[] args) {
new UDPServer().init();
}
}
3.2创建客户端
package com.android;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class ActivityMain extends Activity {
//发送的消息内容
EditText msgEditText = null;
//显示消息内容
LinedText msgText = null;
//数据报Socket
DatagramSocket socket = null;
//服务器地址
InetAddress address = null;
//连接IP端口号
String ip = "192.168.1.102";
int port = 2345;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Button sendBtn = (Button)findViewById(R.id.sendBtn);
msgEditText =(EditText)findViewById(R.id.msgEditText);
//ListView显示消息
msgText =(LinedText)findViewById(R.id.msgText);
try{
//实例化一个数据报Socket
socket = new DatagramSocket();
//服务器地址
address = InetAddress.getByName(ip);
}
catch (Exception ex){
ex.printStackTrace(); //输出出错信息
}
//发送消息
sendBtn.setOnClickListener(new OnClickListener(){
public void onClick(View v){
try {
//取得编辑框中我们输入的内容
String msg = msgEditText.getText().toString().trim();
//编码为GB2312发送
byte[] msg_byte = msg.getBytes("GB2312");
//实例化一个数据报
DatagramPacket packet = new DatagramPacket(msg_byte,msg_byte.length,address,port);
//发送报文
socket.send(packet);
msgEditText.setText("");
msgText.append("send:" + msg);
msgText.append("\n");
//接受回应
//缓冲区数组
byte[] buffer = new byte[256];
DatagramPacket receivePacket = new DatagramPacket(buffer,buffer.length,address,port);
socket.receive(receivePacket);
//计算buffer中的有效数据长度
int l = 0;
for(byte b:buffer){
if(b!=0){
l++;
}
}
String receivedMessage = new String(buffer,0,l);
//显示服务器返回信息更新到TextView
msgText.append("received:"+receivedMessage);
msgText.append("\n");
}catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* 返回退出
*/
@Override
public void onBackPressed() {
// TODO Auto-generated method stub
try {
//关闭端口
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
super.onBackPressed();
}
}
浙公网安备 33010602011771号