网络编程 : 几种语言使用 Socket 实现 TCP

写在前面

在编程里,我们使用 Socket 来实现 TCP 或 UDP 的通信,Socket 是对它们的上层封装。虽然是不同的语言,但它们的 API 接口都遵循一致的逻辑。本篇主要是使用不同语言(C、Java 和 Dart),通过其 Socket API 来实现一个简单的 TCP 通信进行记录。

内容

API

这里主要罗列几种语言使用 Socket 时的几个基本 API,能够完成一个大致的流程。

C

在 C 语言的sys/socket.h头文件里定义了使用 Socket 的几个 API ,分别是:

  • int socket(int domain , int type , int protocol);
    • 创建套接字
  • int bind(int sockfd , struct sockaddr *myaddr , socklen_t addrlen);
    • 绑定 IP 地址和端口(服务端调用)
  • int listen(int sockfd , int backlog);
    • 进入等待连接请求状态(服务端使用)
  • int accept(int sockfd , struct sockaddr *addr , socklen_t * addrlen);
    • 受理客户端连接请求(服务端使用)
  • int connect(int sockfd , struct sockaddr *serv_addr , socklen_t addrlen);
    • 客户端请求连接(客户端调用)
  • ssize_t write(int fd , const void * buf , size_t nbytes);
    • 写数据
  • ssize_t read(int fd , void * buf , size_t nbytes);
    • 读数据
  • int close(int fd);
    • 关闭

不管是客户端还是服务端,C 语言里都是先使用 socket()进行创建。

Java

Java 里创建 Socket 的时候,客户端和服务端会使用不同的类,客户端使用的是 Socket类,服务端使用的是ServerSocket

Socket
  • Socket()
    • 创建套接字
  • public void connect(SocketAddress endpoint)
    • 连接到服务端
  • public InputStream getInputStream()
    • 获取输入流(服务端传来的)
  • public OutputStream getOutputStream()
    • 获取输出流(用于传数据给服务端)
  • public synchronized void close()
    • 关闭 Socket(同时会关闭其输入流和输出流)
ServerSocket
  • ServerSocket()
    • 创建套接字
  • public void bind(SocketAddress endpoint)
    • 绑定 IP 地址和端口
  • public Socket accept()
    • 监听连接的到来,并获取到一个新的 Socket,这个 Socket 就是客户端的操作对象
  • public void close()
    • 关闭 Socket

因为服务端通过accept()拿到了操作客户端的对象,所以输入输出也就对应Socket的 API。

Dart

待续

代码示例

C

服务端
echo_sever.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc, char const *argv[])
{
	int serv_sock;
	int clnt_sock;

	struct sockaddr_in serv_addr;
	struct sockaddr_in clnt_addr;
	socklen_t clnt_addr_size;

	int str_len, i;

	char message[]="hello world!";

	if(argc != 2){
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	serv_sock = socket(PF_INET , SOCK_STREAM , 0);
	if(serv_sock == -1){
		error_handling("socket() error");
	}

	memset(&serv_addr , 0 , sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(atoi(argv[1]));

	if(bind(serv_sock , (struct sockaddr*) &serv_addr , sizeof(serv_addr)) == -1){
		error_handling("bind() error");
	}

	if(listen(serv_sock , 5) == -1){
		error_handling("listen() error");
	}

	clnt_addr_size = sizeof(clnt_addr);
	for (int i = 0; i < 5; i++)
	{
		clnt_sock = accept(serv_sock , (struct sockaddr*)&clnt_addr , &clnt_addr_size);

		if(clnt_sock == -1){
			error_handling("accept() error");
		}else{
			printf("Connected client %d \n" , i+1);
		}

		while((str_len = read(clnt_sock , message , BUF_SIZE)) != 0){
			write(clnt_sock , message , str_len);
		}
		
		close(clnt_sock);
	}
	
	close(serv_sock);

	return 0;
}

void error_handling(char *message){
	fputs(message , stderr);
	fputc('\n' , stderr);
	exit(1);
}
客户端
echo_client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc, char const *argv[])
{
	int sock;
	struct sockaddr_in serv_addr;
	char message[30];
	int str_len;

	if(argc != 3){
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}

	sock = socket(PF_INET , SOCK_STREAM , 0);
	if(sock == -1){
		error_handling("socket() error");
	}

	memset(&serv_addr , 0 , sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
	serv_addr.sin_port = htons(atoi(argv[2]));

	if(connect(sock , (struct sockaddr*)&serv_addr , sizeof(serv_addr)) == -1){
		error_handling("connect() error");
	}else{
		puts("Connected............");
	}

	while(1){
		fputs("Input message(Q to quit): " , stdout);
		fgets(message , BUF_SIZE , stdin);

		if(!strcmp(message , "q\n") || !strcmp(message , "Q\n")){
			break;
		}

		write(sock , message , strlen(message));
		str_len = read(sock , message , BUF_SIZE-1);
		message[str_len] = 0;
	    printf("Message from server : %s \n", message);
	}
	close(sock);
	return 0;
}

void error_handling(char *message){
	fputs(message , stderr);
	fputc('\n' , stderr);
	exit(1);
}

Java

服务端
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(2000);
        System.out.println("服务器准备就绪");
        System.out.println("服务端信息 : " + serverSocket.getInetAddress() + " P : " + serverSocket.getLocalPort());

        // 等待客户端连接
        for (; ; ) {
            // 得到客户端
            Socket clientSocket = serverSocket.accept();
            // 构建异步线程用于处理多个到来的客户端
            ClientHandler clientHandler = new ClientHandler(clientSocket);
            clientHandler.start();
        }


    }

    // 定义一个线程类
    private static class ClientHandler extends Thread {
        private Socket socket;
        private boolean flag = true;

        public ClientHandler(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            super.run();
            System.out.println("新客户端连接 : " + socket.getInetAddress() + " P : " + socket.getPort());
            try {
                // 得到打印流,用于数据输出;服务器回送数据使用
                PrintStream socketOutput = new PrintStream(socket.getOutputStream());
                // 得到输入流,用于接收数据
                BufferedReader socketInput = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                do {
                    // 从客户端拿到数据
                    String str = socketInput.readLine();
                    if ("bye".equalsIgnoreCase(str)) {
                        flag = false;
                        // 回送 bye
                        socketOutput.println("bye");
                    } else {
                        System.out.println(str);
                        socketOutput.println("接收到的数据长度为 : " + str.length());
                    }

                } while (flag);
                socketInput.close();
                socketOutput.close();

            } catch (Exception e) {
                System.out.println("连接异常断开");
            } finally {
                // 关闭连接
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("客户端已关闭 : " + socket.getInetAddress() + " P : " + socket.getPort());
        }
    }
}
客户端
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket();
        socket.setSoTimeout(3000);
        socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(), 2000), 3000);
        System.out.println("已发送服务器连接");
        System.out.println("客户端信息 : " + socket.getLocalAddress() + " P : " + socket.getLocalPort());
        System.out.println("服务端信息 : " + socket.getInetAddress() + " P : " + socket.getPort());
        try {
            todo(socket);
        } catch (Exception e) {
            System.out.println("异常关闭");
        }

        socket.close();
        socket.shutdownInput();
        System.out.println("客户端已退出");

    }

    private static void todo(Socket client) throws IOException {
        // 构建键盘输入流
        InputStream systemInputStream = System.in;
        BufferedReader inputReader = new BufferedReader(new InputStreamReader(systemInputStream));

        // 得到 Socket 输入流,并转换为打印流
        OutputStream outputStream = client.getOutputStream();
        PrintStream socketPrintStream = new PrintStream(outputStream);
        // 获取 Socket 输入流,即服务端输入进来的
        InputStream inputStream = client.getInputStream();
        BufferedReader socketBufferedReader = new BufferedReader(new InputStreamReader(inputStream));

        // 轮询
        boolean flag = true;
        do {
            // 键盘读取一行
            String str = inputReader.readLine();
            // 发送到服务器
            socketPrintStream.println(str);

            // 从服务器读取一行
            String echo = socketBufferedReader.readLine();
            if ("bye".equalsIgnoreCase(echo)) {
                flag = false;
            } else {
                System.out.println(echo);
            }
        } while (flag);

        // 资源释放
        socketPrintStream.close();
        socketBufferedReader.close();
    }
}

Dart

待续

参考

《TCP/IP网络编程》

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值