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>

11. using namespace std;

13. const int port = 9090;
14. const int buffer_size = 1<<20;
15. const int method_size = 1<<10;
16. const int filename_size = 1<<10;
17. const int common_buffer_size = 1<<10;

19. void handleError(const string &message);
20. void requestHandling(int *sock);
21. void sendError(int *sock);
22. void sendData(int *sock, char *filename);
23. void sendHTML(int *sock, char *filename);
24. void sendJPG(int *sock, char *filename);

26. int main()
27. {
28. int server_sock;
29. int client_sock;

31. struct sockaddr_in server_address;
32. struct sockaddr_in client_address;

34. socklen_t client_address_size;

36. server_sock = socket(PF_INET, SOCK_STREAM, 0);

38. if (server_sock == -1)
39. {
40. handleError("socket error");
41. }

43. memset(&server_address,0,sizeof(server_address));
44. server_address.sin_family = AF_INET;
45. server_address.sin_addr.s_addr = htonl(INADDR_ANY);
46. server_address.sin_port = htons(port);

48. if(bind(server_sock,(struct sockaddr*)&server_address, sizeof(server_address)) == -1){
49. handleError("bind error");
50. }

52. if(listen(server_sock, 5) == -1) {
53. handleError("listen error");
54. }

56. while(true) {
57. client_address_size = sizeof(client_address);
58. client_sock = accept(server_sock, (struct sockaddr*) &client_address, &client_address_size);

60. if (client_sock == -1) {
61. handleError("accept error");
62. }
63. requestHandling(&client_sock);
64. }

66. //system("open http://127.0.0.1:9090/index.html");
67. close(server_sock);

69. return 0;
70. }

72. void requestHandling(int *sock){
73. int client_sock = *sock;
74. char buffer[buffer_size];
75. char method[method_size];
76. char filename[filename_size];

78. read(client_sock, buffer, sizeof(buffer)-1);

80. if(!strstr(buffer, "HTTP/")) {
81. sendError(sock);
82. close(client_sock);
83. return;
84. }

86. strcpy(method, strtok(buffer," /"));
87. strcpy(filename, strtok(NULL, " /"));

89. if(0 != strcmp(method, "GET")) {
90. sendError(sock);
91. close(client_sock);
92. return;
93. }

95. sendData(sock, filename);
96. }

98. void sendData(int *sock, char *filename) {
99. int client_sock = *sock;
100. char buffer[common_buffer_size];
101. char type[common_buffer_size];

103. strcpy(buffer, filename);

105. strtok(buffer, ".");
106. strcpy(type, strtok(NULL, "."));

108. if(0 == strcmp(type, "html")){
109. sendHTML(sock, filename);
110. }else if(0 == strcmp(type, "jpg")){
111. sendJPG(sock, filename);
112. }else{
113. sendError(sock);
114. close(client_sock);
115. return ;
116. }
117. }

119. void sendHTML(int *sock, char *filename) {
120. int client_sock = *sock;
121. char buffer[buffer_size];
122. FILE *fp;

124. char status[] = "HTTP/1.0 200 OK\r\n";
125. char header[] = "Server: A Simple Web Server\r\nContent-Type: text/html\r\n\r\n";

127. write(client_sock, status, strlen(status));
128. write(client_sock, header, strlen(header));

130. fp = fopen(filename, "r");
131. if(!fp){
132. sendError(sock);
133. close(client_sock);
134. handleError("failed to open file");
135. return ;
136. }

138. fgets(buffer,sizeof(buffer), fp);
139. while(!feof(fp)) {
140. write(client_sock, buffer, strlen(buffer));
141. fgets(buffer, sizeof(buffer), fp);
142. }

144. fclose(fp);
145. close(client_sock);
146. }

148. void sendJPG(int *sock, char *filename) {
149. int client_sock = *sock;
150. char buffer[buffer_size];
151. FILE *fp;
152. FILE *fw;

154. char status[] = "HTTP/1.0 200 OK\r\n";
155. char header[] = "Server: A Simple Web Server\r\nContent-Type: image/jpeg\r\n\r\n";

157. write(client_sock, status, strlen(status));
158. write(client_sock, header, strlen(header));

160. fp = fopen(filename, "rb");
161. if(NULL == fp){
162. sendError(sock);
163. close(client_sock);
164. handleError("failed to open file");
165. return ;
166. }

168. fw = fdopen(client_sock, "w");
169. fread(buffer, 1, sizeof(buffer), fp);
170. while (!feof(fp)){
171. fwrite(buffer, 1, sizeof(buffer), fw);
172. fread(buffer, 1, sizeof(buffer), fp);
173. }

175. fclose(fw);
176. fclose(fp);
177. close(client_sock);
178. }

180. void handleError(const string &message) {
181. cout<<message;
182. exit(1);
183. }

185. void sendError(int *sock){
186. int client_sock = *sock;

188. char status[] = "HTTP/1.0 400 Bad Request\r\n";
189. char header[] = "Server: A Simple Web Server\r\nContent-Type: text/html\r\n\r\n";
190. char body[] = "<html><head><title>Bad Request</title></head><body><p>400 Bad Request</p></body></html>";

192. write(client_sock, status, sizeof(status));
193. write(client_sock, header, sizeof(header));
194. write(client_sock, body, sizeof(body));
195. }

参考资料

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