Socket 基本概念
Socket 是对 TCP/IP 协议族的一种封装,是应用层与TCP/IP协议族通信的中间软件抽象层。从设计模式的角度看来,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Socket 还可以认为是一种网络间不同计算机上的进程通信的一种方法,利用三元组(ip地址,协议,端口)就可以唯一标识网络中的进程,网络中的进程通信可以利用这个标志与其它进程进行交互。
Socket 起源于 Unix ,Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开(open) –> 读写(write/read) –> 关闭(close)”模式来进行操作。因此 Socket 也被处理为一种特殊的文件。
写一个简易的 WebServer
一个简易的 Server 的流程如下:
- 1.建立连接,接受一个客户端连接。
- 2.接受请求,从网络中读取一条 HTTP 请求报文。
- 3.处理请求,访问资源。
- 4.构建响应,创建带有 header 的 HTTP 响应报文。
- 5.发送响应,传给客户端。
省略流程 3,大体的程序与调用的函数逻辑如下:
- socket() 创建套接字
- bind() 分配套接字地址
- listen() 等待连接请求
- accept() 允许连接请求
- read()/write() 数据交换
- close() 关闭连接
代码如下:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <cstring>
#include <iostream>
using namespace std;
const int port = 9090;
const int buffer_size = 1<<20;
const int method_size = 1<<10;
const int filename_size = 1<<10;
const int common_buffer_size = 1<<10;
void handleError(const string &message);
void requestHandling(int *sock);
void sendError(int *sock);
void sendData(int *sock, char *filename);
void sendHTML(int *sock, char *filename);
void sendJPG(int *sock, char *filename);
int main()
{
int server_sock;
int client_sock;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
socklen_t client_address_size;
server_sock = socket(PF_INET, SOCK_STREAM, 0);
if (server_sock == -1)
{
handleError("socket error");
}
memset(&server_address,0,sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(port);
if(bind(server_sock,(struct sockaddr*)&server_address, sizeof(server_address)) == -1){
handleError("bind error");
}
if(listen(server_sock, 5) == -1) {
handleError("listen error");
}
while(true) {
client_address_size = sizeof(client_address);
client_sock = accept(server_sock, (struct sockaddr*) &client_address, &client_address_size);
if (client_sock == -1) {
handleError("accept error");
}
requestHandling(&client_sock);
}
//system("open http://127.0.0.1:9090/index.html");
close(server_sock);
return 0;
}
void requestHandling(int *sock){
int client_sock = *sock;
char buffer[buffer_size];
char method[method_size];
char filename[filename_size];
read(client_sock, buffer, sizeof(buffer)-1);
if(!strstr(buffer, "HTTP/")) {
sendError(sock);
close(client_sock);
return;
}
strcpy(method, strtok(buffer," /"));
strcpy(filename, strtok(NULL, " /"));
if(0 != strcmp(method, "GET")) {
sendError(sock);
close(client_sock);
return;
}
sendData(sock, filename);
}
void sendData(int *sock, char *filename) {
int client_sock = *sock;
char buffer[common_buffer_size];
char type[common_buffer_size];
strcpy(buffer, filename);
strtok(buffer, ".");
strcpy(type, strtok(NULL, "."));
if(0 == strcmp(type, "html")){
sendHTML(sock, filename);
}else if(0 == strcmp(type, "jpg")){
sendJPG(sock, filename);
}else{
sendError(sock);
close(client_sock);
return ;
}
}
void sendHTML(int *sock, char *filename) {
int client_sock = *sock;
char buffer[buffer_size];
FILE *fp;
char status[] = "HTTP/1.0 200 OK\r\n";
char header[] = "Server: A Simple Web Server\r\nContent-Type: text/html\r\n\r\n";
write(client_sock, status, strlen(status));
write(client_sock, header, strlen(header));
fp = fopen(filename, "r");
if(!fp){
sendError(sock);
close(client_sock);
handleError("failed to open file");
return ;
}
fgets(buffer,sizeof(buffer), fp);
while(!feof(fp)) {
write(client_sock, buffer, strlen(buffer));
fgets(buffer, sizeof(buffer), fp);
}
fclose(fp);
close(client_sock);
}
void sendJPG(int *sock, char *filename) {
int client_sock = *sock;
char buffer[buffer_size];
FILE *fp;
FILE *fw;
char status[] = "HTTP/1.0 200 OK\r\n";
char header[] = "Server: A Simple Web Server\r\nContent-Type: image/jpeg\r\n\r\n";
write(client_sock, status, strlen(status));
write(client_sock, header, strlen(header));
fp = fopen(filename, "rb");
if(NULL == fp){
sendError(sock);
close(client_sock);
handleError("failed to open file");
return ;
}
fw = fdopen(client_sock, "w");
fread(buffer, 1, sizeof(buffer), fp);
while (!feof(fp)){
fwrite(buffer, 1, sizeof(buffer), fw);
fread(buffer, 1, sizeof(buffer), fp);
}
fclose(fw);
fclose(fp);
close(client_sock);
}
void handleError(const string &message) {
cout<<message;
exit(1);
}
void sendError(int *sock){
int client_sock = *sock;
char status[] = "HTTP/1.0 400 Bad Request\r\n";
char header[] = "Server: A Simple Web Server\r\nContent-Type: text/html\r\n\r\n";
char body[] = "<html><head><title>Bad Request</title></head><body><p>400 Bad Request</p></body></html>";
write(client_sock, status, sizeof(status));
write(client_sock, header, sizeof(header));
write(client_sock, body, sizeof(body));
}