开个新坑,基于C++的Linux高性能服务器,跟着教程来的,有些地方能用模板或者啥优化之类的也会试着用一用。
谈到网络编程肯定离不开套接字(socket),套接字是网络中不同主机的应用进程之间进行双向通信的端口,通俗点来讲就是两个房子之间的门,通过这个门可以进出(收发消息),进行通信。
在Linux系统中一切皆文件,统一用文件描述符来代表一个文件,而socket也不例外,也是用文件来表示的,windows下也有相似的概念,叫句柄,通过句柄也可以操作对应的文件或者进程。
接下来就是建立一个简单的服务器,各个函数介绍都在注释里,不再赘述。
由于客户端和服务器端需要使用共同的工具函数,所以干脆把工具函数单独弄到头文件里。
#include "/home/sakura/c++/Utils/utils.h"
int main() {
//建立套接字
//第一个参数为IP地址类型,AF_INET使用IPV4,AF_INET6使用IPV6
//第二个参数为数据传输方式,SOCK_STREAM表示流格式,多用于TCP,SOCK_DGRAM表示数据包格式,用于UDP
//第三个参数,协议,设置为0表示根据根据前两个参数自动推导,有TCP和UDP两种协议.
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
//创建通用套接字地址结构体,通过bzero初始化
//sockaddr_in结构体内部有地址族,IP地址,端口和一个未使用参数,多用于保存信息然后转化为
//sockaddr结构体使用,因为后者IP与端口放在一起储存,有的时候使用不方便.两者大小一样,可以互相转化.
struct sockaddr_in serv_addr;
//初始化该结构体为0
bzero(&serv_addr, sizeof(serv_addr));
//设置地址族,ip地址和端口
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(8888);
//将socket与文件描述符绑定
//errif为错误处理函数
errif(bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "socket bind error");
//监听socket端口,第二个参数为最大监听队列,SOMAXCONN为128
errif(listen(sockfd, SOMAXCONN) == -1, "socket listen error");
//使用accpet保存客户端socket信息,比bind多一个参数socklen_t,因为需要写入客户端socket的长度
//accept会堵塞当前程序,直到接受到客户端socket
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_len = sizeof(clnt_addr);
bzero(&clnt_addr, sizeof(clnt_addr));
int clnt_sockfd = accept(sockfd, (sockaddr*)&clnt_addr, &clnt_addr_len);
errif(clnt_sockfd == -1, "socket accept error");
printf("new client fd %d! IP:%s Port:%d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr),
ntohs(clnt_addr.sin_port));
//无限循环,接受信息
while (true) {
//建立缓冲区
char buf[1024];
bzero(&buf, sizeof(buf));
//从客户端读取数据
ssize_t read_bytes = read(clnt_sockfd, buf ,sizeof(buf));
//大于0, 有信息发送过来, 使用write写回
if (read_bytes > 0) {
printf("message from client fd %d: %s\n", clnt_sockfd, buf);
write(clnt_sockfd, buf, sizeof(buf));
//读取到了EOF, 一般为对方关闭socket的写端或直接close
} else if (read_bytes == 0) {
printf("client fd %d disconnected", clnt_sockfd);
//-1, 发生错误
} else if (read_bytes == -1) {
close(clnt_sockfd);
errif(true, "socket read error");
}
}
return 0;
}
客户端代码:
#include "/home/sakura/c++/Utils/utils.h"
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(8888);
errif(connect(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "socket connect error");
while (true) {
//建立缓冲区
char buf[1024];
bzero(&buf, sizeof(buf));
//读取键盘输入
scanf("%s", buf);
//向服务器端发送数据
ssize_t write_bytes = write(sockfd, buf ,sizeof(buf));
if (write_bytes == -1) {
printf("socket already disconnected, can't write any more");
break;
}
bzero(&buf, sizeof(buf));
ssize_t read_bytes = read(sockfd, buf ,sizeof(buf));
//大于0, 接受到服务器返回信息
if (read_bytes > 0) {
printf("message from server: %s\n", buf);
//读取到了EOF, 一般为对方关闭socket的写端或直接close
} else if (read_bytes == 0) {
printf("server socket disconnected");
//-1, 发生错误
} else if (read_bytes == -1) {
close(sockfd);
errif(true, "socket read error");
}
}
}