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. usingnamespace std;
  11. constint port =9090;
  12. constint buffer_size =1<<20;
  13. constint method_size =1<<10;
  14. constint filename_size =1<<10;
  15. constint 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. return0;
  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. }elseif(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 编程的面纱