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 编程的面纱