• Socket 基本概念
  • 写一个简易的 WebServer
    • 参考资料

    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() 关闭连接

    代码如下:

    1. #include <cstdio>
    2. #include <cstdlib>
    3. #include <cstring>
    4. #include <unistd.h>
    5. #include <sys/socket.h>
    6. #include <arpa/inet.h>
    7. #include <string>
    8. #include <cstring>
    9. #include <iostream>
    10. using namespace std;
    11. const int port = 9090;
    12. const int buffer_size = 1<<20;
    13. const int method_size = 1<<10;
    14. const int filename_size = 1<<10;
    15. const int common_buffer_size = 1<<10;
    16. void handleError(const string &message);
    17. void requestHandling(int *sock);
    18. void sendError(int *sock);
    19. void sendData(int *sock, char *filename);
    20. void sendHTML(int *sock, char *filename);
    21. void sendJPG(int *sock, char *filename);
    22. int main()
    23. {
    24. int server_sock;
    25. int client_sock;
    26. struct sockaddr_in server_address;
    27. struct sockaddr_in client_address;
    28. socklen_t client_address_size;
    29. server_sock = socket(PF_INET, SOCK_STREAM, 0);
    30. if (server_sock == -1)
    31. {
    32. handleError("socket error");
    33. }
    34. memset(&server_address,0,sizeof(server_address));
    35. server_address.sin_family = AF_INET;
    36. server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    37. server_address.sin_port = htons(port);
    38. if(bind(server_sock,(struct sockaddr*)&server_address, sizeof(server_address)) == -1){
    39. handleError("bind error");
    40. }
    41. if(listen(server_sock, 5) == -1) {
    42. handleError("listen error");
    43. }
    44. while(true) {
    45. client_address_size = sizeof(client_address);
    46. client_sock = accept(server_sock, (struct sockaddr*) &client_address, &client_address_size);
    47. if (client_sock == -1) {
    48. handleError("accept error");
    49. }
    50. requestHandling(&client_sock);
    51. }
    52. //system("open http://127.0.0.1:9090/index.html");
    53. close(server_sock);
    54. return 0;
    55. }
    56. void requestHandling(int *sock){
    57. int client_sock = *sock;
    58. char buffer[buffer_size];
    59. char method[method_size];
    60. char filename[filename_size];
    61. read(client_sock, buffer, sizeof(buffer)-1);
    62. if(!strstr(buffer, "HTTP/")) {
    63. sendError(sock);
    64. close(client_sock);
    65. return;
    66. }
    67. strcpy(method, strtok(buffer," /"));
    68. strcpy(filename, strtok(NULL, " /"));
    69. if(0 != strcmp(method, "GET")) {
    70. sendError(sock);
    71. close(client_sock);
    72. return;
    73. }
    74. sendData(sock, filename);
    75. }
    76. void sendData(int *sock, char *filename) {
    77. int client_sock = *sock;
    78. char buffer[common_buffer_size];
    79. char type[common_buffer_size];
    80. strcpy(buffer, filename);
    81. strtok(buffer, ".");
    82. strcpy(type, strtok(NULL, "."));
    83. if(0 == strcmp(type, "html")){
    84. sendHTML(sock, filename);
    85. }else if(0 == strcmp(type, "jpg")){
    86. sendJPG(sock, filename);
    87. }else{
    88. sendError(sock);
    89. close(client_sock);
    90. return ;
    91. }
    92. }
    93. void sendHTML(int *sock, char *filename) {
    94. int client_sock = *sock;
    95. char buffer[buffer_size];
    96. FILE *fp;
    97. char status[] = "HTTP/1.0 200 OK\r\n";
    98. char header[] = "Server: A Simple Web Server\r\nContent-Type: text/html\r\n\r\n";
    99. write(client_sock, status, strlen(status));
    100. write(client_sock, header, strlen(header));
    101. fp = fopen(filename, "r");
    102. if(!fp){
    103. sendError(sock);
    104. close(client_sock);
    105. handleError("failed to open file");
    106. return ;
    107. }
    108. fgets(buffer,sizeof(buffer), fp);
    109. while(!feof(fp)) {
    110. write(client_sock, buffer, strlen(buffer));
    111. fgets(buffer, sizeof(buffer), fp);
    112. }
    113. fclose(fp);
    114. close(client_sock);
    115. }
    116. void sendJPG(int *sock, char *filename) {
    117. int client_sock = *sock;
    118. char buffer[buffer_size];
    119. FILE *fp;
    120. FILE *fw;
    121. char status[] = "HTTP/1.0 200 OK\r\n";
    122. char header[] = "Server: A Simple Web Server\r\nContent-Type: image/jpeg\r\n\r\n";
    123. write(client_sock, status, strlen(status));
    124. write(client_sock, header, strlen(header));
    125. fp = fopen(filename, "rb");
    126. if(NULL == fp){
    127. sendError(sock);
    128. close(client_sock);
    129. handleError("failed to open file");
    130. return ;
    131. }
    132. fw = fdopen(client_sock, "w");
    133. fread(buffer, 1, sizeof(buffer), fp);
    134. while (!feof(fp)){
    135. fwrite(buffer, 1, sizeof(buffer), fw);
    136. fread(buffer, 1, sizeof(buffer), fp);
    137. }
    138. fclose(fw);
    139. fclose(fp);
    140. close(client_sock);
    141. }
    142. void handleError(const string &message) {
    143. cout<<message;
    144. exit(1);
    145. }
    146. void sendError(int *sock){
    147. int client_sock = *sock;
    148. char status[] = "HTTP/1.0 400 Bad Request\r\n";
    149. char header[] = "Server: A Simple Web Server\r\nContent-Type: text/html\r\n\r\n";
    150. char body[] = "<html><head><title>Bad Request</title></head><body><p>400 Bad Request</p></body></html>";
    151. write(client_sock, status, sizeof(status));
    152. write(client_sock, header, sizeof(header));
    153. write(client_sock, body, sizeof(body));
    154. }

    参考资料

    1. Linux Socket编程
    2. 揭开 Socket 编程的面纱