你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

通过 tcp 来实现一个简单的 web 服务器

[复制链接]
gaosmile 发布时间:2020-10-30 18:41

说到 web 服务器想必大多数人首先想到的协议是 http,那么 http 之下则是 tcp,本篇文章将通过 tcp 来实现一个简单的 web 服务器。

本篇文章将着重讲解如何实现,对于 http 与 tcp 的概念本篇将不过多讲解。

一、了解 Socket 及 web 服务工作原理

既然是基于 tcp 实现 web 服务器,很多学习 C 语言的小伙伴可能会很快的想到套接字 socket。socket 是一个较为抽象的通信进程,或者说是主机与主机进行信息交互的一种抽象。socket 可以将数据流送入网络中,也可以接收数据流。

socket 的信息交互与本地文件信息的读取从表面特征上看类似,但其中所存在的编写复杂度是本地 IO 不能比拟的,但却有相似点。在 win 下 socket 的交互交互步骤为:WSAStartup 进行初始化--> socket 创建套接字--> bind 绑定--> listen 监听--> connect 连接--> accept 接收请求--> send/recv 发送或接收数据--> closesocket 关闭 socket--> WSACleanup 最终关闭。

微信图片_20201030183943.png

了解完了一个 socket 的基本步骤后我们了解一下一个基本 web 请求的用户常规操作,操作分为:打开浏览器-->输入资源地址 ip 地址-->得到资源。当目标服务器接收到该操作产生掉请求后,我们可以把服务器的响应流程步骤看为:获得 request 请求-->得到请求关键数据-->获取关键数据-->发送关键数据。服务器的这一步流程是在启动socket 进行监听后才能响应。通过监听得知接收到请求,使用 recv 接收请求数据,从而根据该参数得到进行资源获取,最后通过 send 将数据进行返回。

二、创建sokect完成监听

2.1 WSAStartup初始化

首先在c语言头文件中引入依赖 WinSock2.h:

#include <WinSock2.h>, v) w* F+ k+ \" f- C. E

在第一点中对 socket 的创建步骤已有说明,首先需要完成 socket 的初始化操作,使用函数 WSAStartup,该函数的原型为:

int WSAStartup(8 j. T" K1 u) {
  WORD      wVersionRequired,
! A* ~2 [. F- W8 ]0 E  LPWSADATA lpWSAData6 ]. n+ S  v7 H* g! G4 }- s
);
. e* ~9 p0 Q1 Y- [+ G$ ]; {+ ]

该函数的参数 wVersionRequired 表示 WinSock2 的版本号;lpWSAData 参数为指向 WSADATA 的指针,WSADATA 结构用于 WSAStartup 初始化后返回的信息。

wVersionRequired 可以使用 MAKEWORD 生成,在这里可以使用版本 1.1 或版本2.2,1.1 只支持 TCP/IP,版本 2.1 则会有更多的支持,在此我们选择版本 1.1。

首先声明一个 WSADATA 结构体  :

WSADATA wsaData;
( E/ g) P9 J4 {, m7 j

随后传参至初始化函数 WSAStartup 完成初始化:

WSAStartup(MAKEWORD(1, 1), &wsaData)9 I! Z" p& J# H

WSAStartup 若初始化失败则会返回非0值:

if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
% j3 p3 E, B$ m$ ]3 p1 r{9 l2 A+ @/ Z! q. `1 k* C
exit(1);; [% A+ Y6 C5 a6 f: T  ~( y( i
}
7 b* `3 ^# N6 h8 Y$ d% ]& t% a

2.2 创建socket 套接字

初始化完毕后开始创建套接字,套接字创建使用函数,函数原型为:

SOCKET WSAAPI socket(- A1 _# U$ D% F- L
  int af,1 Z6 R) s% Y6 ], W4 n
  int type,
" |+ F- B% F: u+ i  int protocol5 p% d' t7 T7 p, Y
);8 p( A  Z" v( \  U9 j

在函数原型中,af 表示 IP 地址类型,使用 PF_INET 表示 IPV4,type 表示使用哪种通信类型,例如 SOCK_STREAM 表示 TCP,protocol 表示传输协议,使用 0 会根据前 2 个参数使用默认值。

int skt = socket(PF_INET, SOCK_STREAM, 0);  K7 g! v0 s7 ]( L

创建完 socket 后,若为 -1 表示创建失败,进行判断如下:

if (skt == -1)
( k( M3 ]) l2 {$ ^9 h* R/ ^& l$ G{         
3 a4 g1 |1 S8 r* I% v/ s" T return -1;0 r- E/ x8 T+ z' g" v% n# p
}
1 Y" O9 [# m0 a

2.3 绑定服务器

创建完 socket 后需要对服务器进行绑定,配置端口信息、IP 地址等。 首先查看 bind 函数需要哪一些参数,函数原型如下:

int bind(; G/ S7 C- [* R6 c% r6 g4 l
  SOCKET         socket,8 t+ P* a1 V) n' W/ Y! F, I5 S8 N$ _
  const sockaddr *addr,8 f7 \/ e5 k6 I7 b$ p6 d5 _
  int            addrlen
7 ]+ L1 I9 \4 h: w3 V; t);
0 s% [( q8 x& i0 Z: _

参数 socket 表示绑定的 socket,传入 socket 即可;addr 为 sockaddr_in 的结构体变量的指针,在 sockaddr_in 结构体变量中配置一些服务器信息;addrlen 为 addr 的大小值。

通过 bind 函数原型得知了我们所需要的数据,接下来创建一个 sockaddr_in 结构体变量用于配置服务器信息:

struct sockaddr_in server_addr;* @; c! E7 s9 r; J& l8 h

随后配置地址家族为AF_INET对应TCP/IP:

server_addr.sin_family = AF_INET;
& K1 [% ~9 S; @0 V& Y7 j# J

接着配置端口信息:

server_addr.sin_port = htons(8080);
! m$ p8 g9 M. o! i$ ^

再指定 ip 地址:

server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  S  g4 Q2 ?4 g

ip 地址若不确定可以手动输入,最后使用神器 memset 初始化内存,完整代码如下:

//配置服务器
$ M( a- r" b3 Z3 o, [$ estruct sockaddr_in server_addr;/ ]8 M& Z& b3 G8 h* }, F% g8 J6 T
server_addr.sin_family = AF_INET;
( I& w: j9 W. d3 {server_addr.sin_port = htons(8080);
- o; I2 o0 p. j' i: O5 S7 c' Nserver_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
* B1 m, B) _; P0 H: U6 X+ J7 h9 Gmemset(&(server_addr.sin_zero), '\0', 8);/ V& l8 _. y/ M# R+ @2 @7 T

随后使用 bind 函数进行绑定且进行判断是否绑定成功:

//绑定+ Y* B8 ?1 a! R" {/ |
if (bind(skt, (struct sockaddr *)&server_addr,sizeof(server_addr)) == -1) {      
& ~6 J# J% P& S; ?( ?' } return -1;
7 ?+ W. U0 ^( |9 T}
4 r9 q1 v& L: Z- j7 u

2.4 listen进行监听

绑定成功后开始对端口进行监听。查看 listen 函数原型:

int listen(
* S$ G/ S& f& I# q4 ^ int sockfd,
$ H9 a8 G$ s5 H- y0 J int backlog
1 m/ h. W. F5 ~8 [& F; }8 R' X)
& e; _4 r" Q. J8 A4 g

函数原型中,参数 sockfd 表示监听的套接字,backlog 为设置内核中的某一些处理(此处不进行深入讲解),直接设置成 10 即可,最大上限为 128。使用监听并且判断是否成功代码为:

if (listen(skt, 10) == -1 ) {   
$ \" u/ _) K* T5 K" A, p6 f5 b return -1;/ V* G$ S. W4 D; W) a* y" Z! l0 \
}4 w4 H/ s% N6 J

此阶段完整代码如下:

#include <WinSock2.h>! ]% x7 Z0 A9 b
#include<stdio.h>
& f, n9 i' x1 ], Z* r+ |9 Aint main(){
4 T! Y) G8 W  [+ m //初始化
: U3 Q$ F6 _' C% L WSADATA wsaData;5 p% h) E6 a. ^0 m5 `
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
2 n/ n0 \" S% j  exit(1);( }( F4 U: `7 x& z  U; M% J
}+ T& J/ D5 W+ z9 r* w+ X9 Q3 c
//socket创建   c! ~1 b4 z) W8 P$ R) s4 k& [
int skt = socket(PF_INET, SOCK_STREAM, 0);8 d% ?1 [3 i/ D5 K7 U6 d7 s
if (skt == -1) {         
( N. y* F/ H1 _% o" Y8 ]  return -1;" w$ Z( C- \: s: _4 B  Y- A
}
8 J, y0 g# d8 H# ]5 o) Z$ E //配置服务器 3 Y  N7 [/ x4 E+ F! y/ k
struct sockaddr_in server_addr;
3 u" K7 p7 S+ V: d9 g! T. _ server_addr.sin_family = AF_INET;, W, t) v$ Z! R1 D3 o# V: r3 S/ S
server_addr.sin_port = htons(8080);
, u1 @5 A0 V/ j server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");, Q3 g. I- R2 c4 |1 ~. t8 W
memset(&(server_addr.sin_zero), '\0', 8);* o+ r4 W: j( c$ @, W. f7 {
//绑定
; s/ ?! Q9 _7 T if (bind(skt, (struct sockaddr *)&server_addr,sizeof(server_addr)) == -1){      
; B5 g) y3 N7 K1 p' r, ^/ \7 ~  return -1;
! ^3 R! ]9 G- F. q( b- M& V' c. x } 7 I* [- B6 i; j9 X3 h  _
//监听 2 A' F( X+ U8 n- G; j* F( ?6 A+ r3 m
if (listen(skt, 10) == -1 ) {    ; ?& v& K1 m9 S' t+ c/ ?: a
  return -1;
' e6 u* E( R2 l$ g' W1 f# q }# M/ Y: g9 b% E5 }4 [: ?
% P4 F0 _9 a$ `( U5 L5 m7 |/ L
printf("Listening ... ...\n");
: Y/ n; j& t% l! O' b0 C, U}, k! t; `* `( @5 P1 g

运行代码可得知代码无错误,并且输出 listening:

微信图片_20201030183946.png 在这里插入图片描述

2.5 获取请求

监听完成后开始获取请求。受限需要使用 accept 对套接字进行连接,accept 函数原型如下:

int accept(
( A" ]6 @8 A4 p int sockfd,! P* O' A! Z( h. u
struct sockaddr *addr,% N  o1 J4 u2 Y. r
socklen_t *addrlen$ y  W; r1 a5 S5 A% W: w
);
0 d- h) a9 p& {. C" t

参数 sockfd 为指定的套接字;addr 为指向 struct sockaddr 的指针,一般为客户端地址;addrlen 一般设置为设置为 sizeof(struct   sockaddr_in) 即可。代码为:

struct sockaddr_in c_skt;
* n; d# e3 O( Y2 ?+ G0 o+ |" Bint s_size=sizeof(struct   sockaddr_in);
8 u8 u3 j/ ?; J) W# i  u! uint access_skt = accept(skt, (struct sockaddr *)&c_skt, &s_size);
7 _  E3 Z8 J0 b% h5 t0 V3 W

接下来开始接受客户端的请求,使用recv函数,函数原型为:

ssize_t recv(" z- p1 C# ~- t. j: u1 b
int sockfd,
* R% x# T  l; U% I void *buf, 0 ^( g% f4 A8 p/ Q! U
size_t len,
) F4 N  N4 u# |4 G5 I int flags
3 L% e9 c6 S. k)
+ ?! J( O$ l" b+ o; B. U

参数 sockfd 为 accept 建立的通信;buf 为缓存,数据存放的位置;len 为缓存大小;flags 一般设置为0即可:

//获取数据
( J4 v2 J; f: I" a  T( @  Nchar buf[1024];6 o2 {1 f9 o% R5 X8 o3 l* r% A9 l
if (recv(access_skt, buf, 1024, 0) == -1) {
) {$ j; K4 m) l exit(1);
6 p3 V0 r% U9 w+ ~, U0 x7 p}7 G* H" m; u* l% A+ l1 T  Z- j

此时我们再到 accpt 和 recv 外层添加一个循环,使之流程可重复:

while(1){
8 H  v3 y# D, u5 v; H  //建立连接 ( r" w9 ]8 @; z+ _
  printf("Listening ... ...\n");( h$ K, f7 |; ^2 A
  struct sockaddr_in c_skt; & l0 R. n" w- {0 ~" d2 Y' E: x
  int s_size=sizeof(struct   sockaddr_in);
. p* E9 q2 Z3 _1 n% I) W  int access_skt = accept(skt, (struct sockaddr *)&c_skt, &s_size);
7 \5 p# H8 B& C) S7 W* q, ]  K  
8 |2 m. c$ H& ~7 Y3 T; X  //获取数据
+ M: M/ D! i3 a, ^  char buf[1024];
8 a1 X" d4 _1 D" S- |" P  if (recv(access_skt, buf, 1024, 0) == -1) {
- `; h6 j7 N: E) E4 v3 o( ]   exit(1);
+ _( e& f. p9 i" T/ ^9 r# @: g  }
. P, Q8 R0 @4 {) ^, M }   j7 g( T7 `% k: R4 r

并且可以在浏览器输入 127.0.0.1:8080 将会看到客户端打印了 listening 新建了链接:

微信图片_20201030183950.png


- @! y6 V. A, X2 |8 F! V
微信图片_20201030183953.png

我们添加printf语句可查看客户端请求:

while(1){$ O$ C/ P6 E# \9 S1 m+ p
  //建立连接 & h$ k7 k* X( f- F! F
  printf("Listening ... ...\n");
; }7 D1 f0 p' _3 K' p6 O8 U  struct sockaddr_in c_skt; ( Z, h; ]4 R! c. u$ ?9 U5 }2 o  j5 @
  int s_size=sizeof(struct   sockaddr_in);
+ A/ G& W$ v9 D. A+ E5 W  int access_skt = accept(skt, (struct sockaddr *)&c_skt, &s_size);+ Z! E( Z3 Y; G) X# F
  
5 {5 J9 Q' W! U2 W9 j  //获取数据 - y4 U5 U6 \) r# P8 F. k" `: W
  char buf[1024];
- R& ~' n) s- d! ~8 W  if (recv(access_skt, buf, 1024, 0) == -1) {& v: ?; W/ Y& w2 E% m0 w0 [
   exit(1);
" O& U& o$ }3 |5 J  }
9 U' c- O2 T7 Z. n! d  l) g0 O! ]  . g/ T5 G9 E) r
  printf("%s",buf);
# d& u: B; v) R7 D* V  P/ t. ~. D }
0 \  W+ ]( B$ _0 W1 f9 y

微信图片_20201030183957.png

接下来我们对请求头进行对应的操作。

2.6 请求处理层编写

得到请求后开始编写处理层。继续接着代码往下写没有层级,编写一个函数名为 req,该函数接收请求信息与一个建立好的连接为参数:

void req(char* buf, int access_socket)
/ S9 [1 m4 D. N, J4 O6 x  u{! l" b& v  p- V" [+ z! M  A
}
7 x9 y- w( V8 `8 \' R; u: f9 T

然后先在 while 循环中传递需要的值:

req(buf, access_skt);
% b+ o2 }, x1 Q5 Y7 T! \; [1 q

接着开始编写 req 函数,首先在 req 函数中标记当前目录下:

char arguments[BUFSIZ];  ) u7 A3 d' y/ |9 }7 {9 J/ w
strcpy(arguments, "./");
6 k2 [) s6 C# Y& ?

随后分离出请求与参数:

char command[BUFSIZ];     
7 J, P3 _) o$ }& }+ B4 \0 x9 g9 b1 qsscanf(request, "%s%s", command, arguments+2);& ]# A0 E$ O+ K3 n9 w

接着我们标记一些头元素:

char* extension = "text/html";   " U4 s0 n) o9 L2 s
char* content_type = "text/plain";     
8 |/ i' T1 l4 Lchar* body_length = "Content-Length: ";
1 P9 }& ]' _0 b2 r0 o! |- W

接着获取请求参数,若获取 index.html,就获取当前路径下的该文件:

FILE* rfile= fopen(arguments, "rb");9 B- k  u8 [+ |, t0 k3 Q

获取文件后表示请求 ok,我们先返回一个 200 状态:

char* head = "HTTP/1.1 200 OK\r\n";   
8 n" [* ?) a& z* z& g* mint len;
' c- ]0 O$ \: F& ichar ctype[30] = "Content-type:text/html\r\n";   
8 `0 x/ `" J7 G9 B+ jlen = strlen(head);
" m: b; p9 \8 j* v0 E, ^* [& `

接着编写一个发送函数 send_:

int send_(int s, char *buf, int *len) 5 }/ z6 c$ O' N8 }8 n
{, \) k% G; s% m: d; `, S! f% H
int total;         
% |; v* m( X# P' P( H4 b& j+ i0 R int bytesleft;                                5 f' \# z8 n. p; t0 X  v9 \
int n;
7 f" r! v6 w- Z total=0;
# `1 p9 V; [) R' x bytesleft=*len;
: \, b3 o( w0 i8 s+ t; B while(total < *len)
6 p; a, i; _# Z; O! ~' n4 g {: m  d! ~0 x- r! {! Q5 D( @% [
  n = send(s, buf+total, bytesleft, 0);, G0 T  S' b" _7 I$ M( ]
  if (n == -1) 1 p& g* H5 B5 I# w0 }# _
  {! i! r6 H& ?3 k
   break;
" ?& u" y5 B1 X, R  }
1 D/ ]$ Y' x4 i# V, j! F  total += n;
8 A. M7 P1 m' a1 ?: l  bytesleft -= n;: |' Q( i+ _  I% E4 j
}
6 y' E% c# z0 j( B8 } *len = total;          8 d7 f2 Z, G# q+ p6 o& v2 R( ~
return n==-1?-1:0;         9 M  G; ~$ l# q# \2 k
}
9 t6 l' A% W+ c, f

send 函数功能并不难在此不再赘述,就是一个遍历发送的逻辑。随后发送 http 响应与文件类型:

send_(send_to, head, &len);
# h; }, O; r/ S! c2 O/ Vlen = strlen(ctype);. `. I; t" G* g4 o: o( K
send_(send_to, ctype, &len);
8 w& j# y( O* T1 k: Z" a. Z, z

随后获得请求文件的描述,需要添加头文件#include <sys/stat.h>使用fstat,且向已连接的通信发生必要的信息 :

//获取文件描述
) S5 e$ P1 ^% K: w- ystruct stat statbuf;( f+ r: P% p: I5 e- c- t$ `) ^- `
char read_buf[1024];       4 O, P+ {1 {: M! l4 ?
char length_buf[20];
2 _, [/ u" @* {4 Y* E( Jfstat(fileno(rfile), &statbuf);
* W, ~, O" f. V. V5 e+ Aitoa( statbuf.st_size, length_buf, 10 );
$ ]6 B& R+ w8 ^9 [send(client_sock, body_length, strlen(body_length), 0);
- A: {1 S% ?, C- f2 csend(client_sock, length_buf, strlen(length_buf), 0);& S; ]- o* ^' X2 ?2 @5 R0 s& r2 d

" r. ?3 i; A$ [send(client_sock, "\n", 1, 0);
% r) s, w3 A3 d1 l: Fsend(client_sock, "\r\n", 2, 0);
, W& n) L$ H; P- z

最后发送数据:

//·数据发送; n6 U3 w% ^% B7 J( F* W
char read_buf[1024]; ( X, A. G, c' b0 [* M1 I0 b
len = fread(read_buf ,1 , statbuf.st_size, rfile);
" o7 P# e/ v, B! fif (send_(client_sock, read_buf, &len) == -1) { ' g$ h6 p8 ~& u8 f# u8 Q
printf("error!");   , D0 K: i- f- Z' A
}# Q; x( H7 C8 ]/ f; A, H

最后访问地址 http://127.0.0.1:8080/index.html,得到当前目录下 index.html 文件数据,并且在浏览器渲染:

微信图片_20201030184000.png

所有代码如下:

#include <WinSock2.h>
- i/ Q( V( d# W" W6 f#include<stdio.h>
/ o' ?. `/ P' [1 F3 h# w1 H1 Q#include <sys/stat.h> $ g6 q! p; [( O9 Z! [' O

4 P" F) ]0 {( F4 x: Eint send_(int s, char *buf, int *len) {7 ~6 R. k" e: a
int total;          . `3 z& M/ D8 I+ y2 m
int bytesleft;                                
& ~/ x  p: F5 U( }  g7 ^) S3 p1 h7 f int n;
# o$ }9 o0 H7 v! s4 D total=0;0 p, Q! }0 Q1 I: D  x+ m2 @
bytesleft=*len;
- F! U; G. X: x8 d, ?' m2 | while(total < *len)
, ^! \5 ]0 D0 J' n {
- _$ i. ]; [- r3 u- V& q  n = send(s, buf+total, bytesleft, 0);
. b* a) p# Q/ V3 n  |' }3 r3 y  if (n == -1)
. _# s* G# }+ j7 H! E  {
3 U! f" g- |5 c9 B( C" h- R8 p6 y   break;
' M. O) z) K  C( Z3 q; |  }
% Y1 n9 V' m, e2 \3 N  K' a5 I4 Q0 d0 y  total += n;
' {9 z: o# e. K0 u0 U  bytesleft -= n;2 a7 x' B" p2 Z  Y- F- ~
}% V6 h6 W( C9 D3 Z2 c2 _
*len = total;         
( e! }. F# g' ~1 ~3 e7 j1 u% i return n==-1?-1:0;         
  Z/ u) n, @8 O+ e' w$ y}, r4 X; r6 y+ b/ s/ `

$ X  n! M; y5 n0 tvoid req(char* request, int client_sock) {   
8 M0 n: L6 z/ w. S- y char arguments[BUFSIZ];  6 `: b5 Y7 _# s) i/ R# b* b
strcpy(arguments, "./");
  N* v: S$ |0 Q4 u. @9 Y
! r: \. V; o6 y& }0 K- }  K$ \/ v$ c char command[BUFSIZ];     
5 L- ~! t; q* k: n! d( j sscanf(request, "%s%s", command, arguments+2);
# Z; w' Q2 \4 ^0 X; x2 D; p) ]  f
& F: J0 q4 z$ P% ~. r) b char* extension = "text/html";   + x( P& W* a9 k. y1 ]* m7 m3 F9 ^
char* content_type = "text/plain";     # [: |4 `1 [/ P/ ~- d
char* body_length = "Content-Length: ";
" `" \3 _! X. T! Z1 o$ F
0 p4 `/ s8 h, Z  h FILE* rfile= fopen(arguments, "rb");
5 p; r! ?. E% u# f2 z$ q  ?0 b8 O ( C% g6 v, Y& K$ b# t

5 Z4 h5 Z, I% ^- q: M3 n9 R, R3 L char* head = "HTTP/1.1 200 OK\r\n";   
( T' t6 z6 w. i% d int len; ( v# v. F/ G) a2 C+ c3 b
char ctype[30] = "Content-type:text/html\r\n";   8 Q! ]. l/ r' w5 Z
len = strlen(head);
# w9 Q) Y- v0 F9 R  & Q" [  A& s1 n+ m& I1 A
send_(client_sock, head, &len);
1 H& s6 X7 u0 _# g) d5 ^ len = strlen(ctype);
% q8 U( S+ D7 J/ E send_(client_sock, ctype, &len);0 H" p* p) J& v* i. H  q5 R% u* W
2 J7 X7 {* n, t: A/ M, _
8 M. s2 a+ G* p
struct stat statbuf;' {7 u! ~+ J+ Y
       3 w: [3 U+ q! G: e+ Y9 O; V8 x8 i
char length_buf[20];* m6 d% d* p5 v
fstat(fileno(rfile), &statbuf);" \0 R5 o- _. @! L
itoa( statbuf.st_size, length_buf, 10 );! b2 i  G. w' x' e9 ?9 h
send(client_sock, body_length, strlen(body_length), 0);  {5 X* E/ q( R4 R- C
send(client_sock, length_buf, strlen(length_buf), 0);0 {8 G) _% [! Z' I8 \
0 z, {% D1 B+ o- r
send(client_sock, "\n", 1, 0);" l4 x& y3 e  i4 r" k' @
send(client_sock, "\r\n", 2, 0);, c4 o4 C' X2 I' S6 m

* h0 c2 C& x; x0 O2 S) ?+ T8 U3 X$ Y
char read_buf[1024];
/ h" N: Y' y! ^  f1 [# V- ~1 _- o len = fread(read_buf ,1 , statbuf.st_size, rfile);
+ L& B4 U( |8 ]  b- v7 S if (send_(client_sock, read_buf, &len) == -1) { 4 c5 H  L$ z2 t0 A1 O9 ~: _8 J
  printf("error!");   , L9 ~' D6 s5 ^# V; h
}
4 n% c9 L" K3 v* b& J% X2 X
8 @2 b3 \% J, M5 Z return;
6 s/ t! j7 L$ p9 b' O# z6 C}( {# J/ {" A. R, j6 K
! Q6 c  B- j3 G) l; L5 {6 k
) V# D+ t& ~, Q1 a) z' ^
int main(){% ]7 w9 ]! e4 {
WSADATA wsaData;( V. J. ^/ h0 }( \% n* F/ }
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
( ^" d* n% A+ t! o. P7 o' V; F  exit(1);) u% H1 t4 R  d1 e; u; i
}
, T' n  m* S4 o6 y+ l
5 P# Y# E* H4 I. j/ z int skt = socket(PF_INET, SOCK_STREAM, 0);; ?6 D% c' _, a# B9 H) K
if (skt == -1) {         4 P% k  }. d/ G: x0 B
  return -1;* V4 v5 k: q' f3 E: Y
}8 {# a% }) i9 O0 x' `8 j0 A

4 `" v4 ^! k3 c/ b9 g, {: Q: ? struct sockaddr_in server_addr;
/ T# Z: M: S$ M& H- Y server_addr.sin_family = AF_INET;4 T/ Y! a0 x1 n
server_addr.sin_port = htons(8080);* c; c9 t, B, B$ [
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");: G* l  b* d* P- D# m1 t( O
memset(&(server_addr.sin_zero), '\0', 8);
: L( F: R6 v( p* E8 G% o! T) {1 p. R5 k- c
if (bind(skt, (struct sockaddr *)&server_addr,sizeof(server_addr)) == -1) {       ' [" ?3 z  k6 X: y- H
  return -1;
% M: j+ N3 g4 a } 2 a& E4 b) C$ n# ?5 A

- W: J! v! T' _5 u2 [3 D/ A' X1 m if (listen(skt, 10) == -1 ) {   
1 a6 E7 ~% V+ g8 b! L4 O2 B  return -1;
; I8 I0 C' K0 i8 E+ t- r5 |7 t( p }
% w& x5 R9 r2 |
9 ^# p# Y' t/ N$ T( Y* a while(1){
6 v  e2 v0 A  a9 Q% V( M" u
! a, }' M9 H" G3 T; z1 ]( ^8 X  printf("Listening ... ...\n");* Z  j7 e" ~1 b! [) B3 \
  struct sockaddr_in c_skt; 5 Z( U2 k0 |( y( ?  Y' T: f  U; [, c
  int s_size=sizeof(struct   sockaddr_in);  T, c4 f, x2 m- D( Y" d2 _1 P
  int access_skt = accept(skt, (struct sockaddr *)&c_skt, &s_size);2 w5 q/ J/ k1 a" ~
; O; C$ N' ^. u5 w. k
  char buf[1024];
9 d( j  c2 H! @& e  if (recv(access_skt, buf, 1024, 0) == -1) {8 g) H; v; ?$ `! E9 L
   exit(1);
1 C6 {* a% D1 f  }* W( G% B0 }/ A" K, c3 ?( q: T/ [
  
5 @5 c) w: B4 I8 `- G; ^  req(buf, access_skt);
5 Q4 l- r) l( x- {7 ]+ ] } & v4 o0 d4 D4 h" r

2 u5 O1 F# y* f" {! k" a}
. j8 v6 P) s- o3 i, l1 O* j  ~

小伙伴们可以编写更加灵活的指定资源类型、错误处理等完善这个 demo。


: O+ C3 J9 h( q/ H
赞 收藏 评论0 发布时间:2020-10-30 18:41

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版