翻译自TCP Client-Server Implementation in C
1.什么是套接字
套接字是一种允许一台或不同台计算机上的不同进程通信的结构,一个套接字允许我们通过在网络中收发数据的方式进行通信。简单的说,这是一种一台计算机在网络上和另一台计算机沟通的方式。
一个套接字是两个运行在网络中的程序之间的双工通信连接的一个端点。一个套接字和一个端口号绑定,以至于TCP层能够识别数据将被发送到的应用程序。
2.什么是TCP
TCP指的是传输控制协议,这是用于互联网通信的主要协议之一。该协议的一些特征是:
(1) 这是一种基于连接的通信协议
(2) 它使用三次握手来建立可靠的连接。
(3) TCP 保证数据包的可靠传输。
3.客户端-服务端架构
一个客户端-服务端结构是计算机网络中的一个模型,服务端给客户端提供一些服务。在这个架构中,客户端通过网络向服务端发送请求。服务端收到请求并将需要的数据传输给用户。
简单说,我们能说:
(1) 服务器是一台提供一些服务的远程计算机
(2) 客户端是一台向服务器索要这些服务的计算机。
4.服务器端代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main()
{
char *ip = "127.0.0.1";
int port = 5566;
int server_sock, client_sock;
struct sockaddr_in server_addr, client_addr;
socklen_t addr_size;
char buffer[1024];
int n;
server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock < 0){
perror("[-]Socket error");
exit(1);
}
printf("[+]TCP server socket created.\n");
memset(&server_addr, '\0', sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = port;
server_addr.sin_addr.s_addr = inet_addr(ip);
n = bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (n < 0){
perror("[-]Bind error");
exit(1);
}
printf("[+]Bind to the port number: %d\n", port);
listen(server_sock, 5);
printf("Listening...\n");
while(1){
addr_size = sizeof(client_addr);
client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_size);
printf("[+]Client connected.\n");
bzero(buffer, 1024);
recv(client_sock, buffer, sizeof(buffer), 0);
printf("Client: %s\n", buffer);
bzero(buffer, 1024);
strcpy(buffer, "HI, THIS IS SERVER. HAVE A NICE DAY!!!");
printf("Server: %s\n", buffer);
send(client_sock, buffer, strlen(buffer), 0);
close(client_sock);
printf("[+]Client disconnected.\n\n");
}
return 0;
}
服务器端的过程可以被分解成如下几个步骤:
(1) socket() —— 创建TCP套接字
(2) bind() —— 将TCP套接字与服务器地址绑定
(3) listen() —— 等待客户端
(4) accept() —— 在客户端和服务端之间建立通信
(5) recv()和send() —— 与对方通信
(6) close() —— 关闭来自客户端的连接
需要包括下列头文件:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
定义IP(互联网协议)地址和端口号,这将会用于创建套接字。这里,我们在服务端和客户端上都使用本地主机地址:
char *ip = "127.0.0.1";
int port = 5566;
这里,我们定义所需的变量,它们将在程序的后面使用:
int server_sock, client_sock;
struct sockaddr_in server_addr, client_addr;
socklen_t addr_size;
char buffer[1024];
int n;
创建一个TCP套接字,就会返回一个套接字描述符。套接字描述符将被用于和客户端通信。
server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock < 0){
perror("[-]Socket error");
exit(1);
}
printf("[+]TCP server socket created.\n");
在这里,我们通过提供所需的端口号和IP地址来初始化服务器地址,服务器将服务端和客户端所有的地址信息保存到了struct sockaddr_in中:
memset(&server_addr, '\0', sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = port;
server_addr.sin_addr.s_addr = inet_addr(ip);
将套接字描述符与服务器地址信息绑定:
n = bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (n < 0){
perror("[-]Bind error");
exit(1);
}
printf("[+]Bind to the port number: %d\n", port)
现在,我们监听客户端即将到来的连接:
listen(server_sock, 5);
printf("Listening...\n");
服务端一次只处理一个客户端。所以只有一个客户端将会通信,其余的客户端就要等着通信结束了。
while(1)
{
addr_size = sizeof(client_addr);
client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_size);
printf("[+]Client connected.\n");
bzero(buffer, 1024);
recv(client_sock, buffer, sizeof(buffer), 0);
printf("Client: %s\n", buffer);
bzero(buffer, 1024);
strcpy(buffer, "HI, THIS IS SERVER. HAVE A NICE DAY!!!");
printf("Server: %s\n", buffer);
send(client_sock, buffer, strlen(buffer), 0);
close(client_sock);
printf("[+]Client disconnected.\n\n");
}
为了与所有的客户端通信,我们启动了一个while循环,然后接下来的事就发生了:
(1) 接收客户端连接
(2) 我们从客户端那里接受信息并打印在终端上
(3) 接下来,服务器给客户端发送回复信息
(4) 关闭来自客户端的连接。
5.客户端代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main()
{
char *ip = "127.0.0.1";
int port = 5566;
int sock;
struct sockaddr_in addr;
socklen_t addr_size;
char buffer[1024];
int n;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0){
perror("[-]Socket error");
exit(1);
}
printf("[+]TCP server socket created.\n");
memset(&addr, '\0', sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = port;
addr.sin_addr.s_addr = inet_addr(ip);
connect(sock, (struct sockaddr*)&addr, sizeof(addr));
printf("Connected to the server.\n");
bzero(buffer, 1024);
strcpy(buffer, "HELLO, THIS IS CLIENT.");
printf("Client: %s\n", buffer);
send(sock, buffer, strlen(buffer), 0);
bzero(buffer, 1024);
recv(sock, buffer, sizeof(buffer), 0);
printf("Server: %s\n", buffer);
close(sock);
printf("Disconnected from the server.\n");
return 0;
}
客户端进程可以被分解成下面几个步骤:
(1) socket( ) —— 创建TCP套接字
(2) connect( ) —— 连接服务器
(3) recv( )和send( ) —— 与对方通信
(4) close( ) —— 关闭连接
刚开始我们包括所有所需的头文件,定义IP地址和端口号。我们也定义了程序后面所需的变量:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
char *ip = "127.0.0.1";
int port = 5566;
int sock;
struct sockaddr_in addr;
socklen_t addr_size;
char buffer[1024];
int n;
我们开始创建一个TCP套接字,套接字将被用于连接服务器并开始通信:
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0){
perror("[-]Socket error");
exit(1);
}
printf("[+]TCP server socket created.\n");
我们为必要的数据结构提供了必要的IP地址和端口号:
memset(&addr, '\0', sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = port;
addr.sin_addr.s_addr = inet_addr(ip);
我们向服务器发送连接请求并等待服务器接收连接请求:
connect(sock, (struct sockaddr*)&addr, sizeof(addr));
printf("Connected to the server.\n");
我们向服务器发送消息并等待回复:
bzero(buffer, 1024);
strcpy(buffer, "HELLO, THIS IS CLIENT.");
printf("Client: %s\n", buffer);
send(sock, buffer, strlen(buffer), 0);
我们接收服务器请求并将其打印在终端:
bzero(buffer, 1024);
recv(sock, buffer, sizeof(buffer), 0);
printf("Server: %s\n", buffer);
最终,我们关闭服务器的连接:
close(sock);
printf("Disconnected from the server.\n");