在编程里,我们使用 Socket 来实现 TCP 或 UDP 的通信,Socket 是对它们的上层封装。虽然是不同的语言,但它们的 API 接口都遵循一致的逻辑。本篇主要是使用不同语言(C、Java 和 Dart),通过其 Socket API 来实现一个简单的 TCP 通信进行记录。
这里主要罗列几种语言使用 Socket 时的几个基本 API,能够完成一个大致的流程。
在 C 语言的sys/socket.h
头文件里定义了使用 Socket 的几个 API ,分别是:
int socket(int domain , int type , int protocol);
int bind(int sockfd , struct sockaddr *myaddr , socklen_t addrlen);
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 里创建 Socket 的时候,客户端和服务端会使用不同的类,客户端使用的是 Socket
类,服务端使用的是ServerSocket
类
Socket()
public void connect(SocketAddress endpoint)
public InputStream getInputStream()
public OutputStream getOutputStream()
public synchronized void close()
ServerSocket()
public void bind(SocketAddress endpoint)
public Socket accept()
public void close()
因为服务端通过accept()
拿到了操作客户端的对象,所以输入输出也就对应Socket
的 API。
待续
echo_sever.c
#include
#include
#include
#include
#include
#include
#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 \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
#include
#include
#include
#include
#include
#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 \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);
}
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();
}
}
待续
《TCP/IP网络编程》