小目标1:编写一个基本的TCP服务器程序
1 #include<cstdio>//C++标准库的头文件 2 #include<unistd.h>//Unix标准头文件 3 #include<sys/types.h>//这个头文件定义了各种系统相关的数据类型 4 #include<sys/socket.h>//这个头文件用于网络编程,包含了与套接字(socket)相关的函数和数据结构的声明 5 #include<arpa/inet.h>//通常用于处理IP地址和套接字地址的转换 6 #include<string.h>//字符串头文件,因为后面有用到memset
第一步:创建套接字描述符
PS:相当于要买一部手机
1 //创建一个套接字描述符 2 int server_socket;//这是一个唯一标识套接字的整数 3 server_socket=socket(AF_INET,SOCK_STREAM,0); 4 /* 5 创建了一个套接字,并将其文件描述符存储在 server_socket 变量中 6 AF_INET 表示IPv4地址族 7 SOCK_STREAM: 这是套接字类型,表示创建的套接字将使用面向连接的TCP协议 8 0: 这是套接字的协议参数,通常设置为0 9 */
第二步:要告诉这个服务器我们的IP地址和端口号
PS:相当于购买一个电话卡
我们要有一个保存IP地址和端口的变量,引入#include<arpa/inet.h>头文件
struct sockaddr_in server_addr://存储套接字信息的变量
server_addr.sin_family=AF_INET;//指定了地址族为 AF_INET
server_addr.sin_addr.s_addr=INADDR_ANY;//表示服务器将接受来自任何可用网络接口的连接请求
server_addr.sin_port=htons(6666);//端口号不可以直接用数字赋值,htons将主机字节序(通常是小端字节序)的端口号转换为网络字节序(大端字节序)
第三步:把我们设置好的ip地址和端口号绑定到我们的server_socket描述符上
PS:相当于把电话卡插到手机上
bind
函数将服务器套接字 server_socket
绑定到特定的地址,成功绑定返回0
bind的参数解释如下:
-
server_socket
: 它将被绑定到特定的地址和端口。 -
&server_addr
:bind
函数需要一个指向struct sockaddr
类型的指针 -
sizeof(server_addr)
: 要绑定的地址结构体的大小,通常使用sizeof
运算符来获取。
perror 函数用于打印与上一个系统调用相关的错误信息。在这里,它将打印错误信息并指明是因为绑定失败而导致的。
if(bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))<0){
perror("server bind error:");
return 0;
}
第四步:调用listen开始监听程序
PS:相当于把电话放在身上,电话铃响了我们就可以听到声音
listen
函数的参数如下:
-
server_socket
: 服务器套接字 -
10
: 这是指定服务器套接字可以排队等待的连接请求的最大数量。这个数字通常被称为待处理连接队列的长度。在这里,设置为10表示服务器可以同时处理的最大连接请求数为10个。
如果 listen
函数成功,它将返回0,否则将返回-1。
if(listen(server_socket,10)<0){
perror("server listen error:");
return 0;
}
第五步:等待服务器连接
accept函数的特点,我们的程序调用这个函数的时候,如果没有客户端连接到我们的服务器上,那么这个函数将会堵塞(停在这里不走了),直到有客户端链接到服务器上,这个函数将解堵塞,并且返回一个新的套接字描述符,那么后期和客户端的通讯都交给这个新的套接字描述符来负责
printf("TCP服务器准备完成,等待客户端的连接");
int accept_socket;//创建一个存储接受到的客户端连接的套接字文件描述符。
char buffer[50]={0};//定义缓冲区,用于暂时存储接收和发送的数据
int res=0;//后续用到
accept_socket=accept(server_socket,NULL,NULL);
printf("有客户端连接到服务器!\n");
while(1)//服务器将持续接收和发送数据,直到手动停止程序。
{
//read函数就是接受客户端发来的数据,存储到buffer里面,返回值表示实际上从accept_socket那边读取到的字节数
res=read(accept_socket,buffer,sizeof(buffer));
printf("client read %s\n",buffer);
//向accept_socket写入buffer中数据,写的数据的字节数为res
write(accept_socket,buffer,res);
memset(buffer,0,sizeof(buffer));//缓冲区清零,便于接收下一次的数据
//服务器收到数据之后原封不动地返回客户端
}
完整代码如下:
#include<cstdio>//C++标准库的头文件
#include<unistd.h>//Unix标准头文件
#include<sys/types.h>//这个头文件定义了各种系统相关的数据类型
#include<sys/socket.h>//这个头文件用于网络编程,包含了与套接字(socket)相关的函数和数据结构的声明
#include<arpa/inet.h>//通常用于处理IP地址和套接字地址的转换
#include<string.h>//字符串头文件,因为后面有用到memset
int main() {
//创建一个套接字描述符
int server_socket;//这是一个唯一标识套接字的整数
server_socket = socket(AF_INET, SOCK_STREAM, 0);
/*
创建了一个套接字,并将其文件描述符存储在 server_socket 变量中
AF_INET 表示IPv4地址族
SOCK_STREAM: 这是套接字类型,表示创建的套接字将使用面向连接的TCP协议
0: 这是套接字的协议参数,通常设置为0
*/
struct sockaddr_in server_addr;//存储套接字信息的变量
server_addr.sin_family = AF_INET;//指定了地址族为 AF_INET
server_addr.sin_addr.s_addr = INADDR_ANY;//表示服务器将接受来自任何可用网络接口的连接请求
server_addr.sin_port = htons(6666);//端口号不可以直接用数字赋值,htons将主机字节序(通常是小端字节序)的端口号转换为网络字节序(大端字节序)
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("server bind error:");
return 0;
}
if (listen(server_socket, 10) < 0) {
perror("server listen error:");
return 0;
}
printf("TCP服务器准备完成,等待客户端的连接");
int accept_socket;//创建一个存储接受到的客户端连接的套接字文件描述符。
int res = 0;//后续用到
char buffer[50] = { 0 };//定义缓冲区,用于暂时存储接收和发送的数据
accept_socket = accept(server_socket, NULL, NULL);
printf("有客户端连接到服务器!\n");
while (1) //服务器将持续接收和发送数据,直到手动停止程序。
{
//read函数就是接受客户端发来的数据,存储到buffer里面,返回值表示实际上从accept_socket那边读取到的字节数
res = read(accept_socket, buffer, sizeof(buffer));
printf("client read %s\n", buffer);
//向accept_socket写入buffer中数据,写的数据的字节数为res
write(accept_socket, buffer, res);
memset(buffer, 0, sizeof(buffer));//缓冲区清零,便于接收下一次的数据
//服务器收到数据之后原封不动地返回客户端
}
return 0;
}
运行结果如下