您尚未登录,请登录后浏览更多内容! 登录 | 立即注册

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 16986|回复: 0
打印 上一主题 下一主题

[C] 自己动手用c语言写一个基于服务器和客户端(TCP)

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
& k- p7 ?3 J$ @. v8 x
, W. K1 L* K1 Z7 Z4 H( [/ H2 I
% d3 }7 J1 w. |; S6 z2 I
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。9 {, ]3 H9 h3 h, }" y( \) X
6 d/ z) h8 A7 [8 U! i

# k. ?* K- C; d+ W" CTCP协议$ {* C3 P+ X5 P: E" i' H2 G+ t# {
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
$ C  _# k, b3 m& ^9 u: j: s: ~( Y' V. o: ]% Y, d/ V3 b- z) {: i: p
5 j" R8 z# L+ m" c
关键词:三次握手,可靠,基于字节流。
, g0 @4 S! F- m
5 j' t" \' {, x

* R. L, k- @; |# E5 f" w% e0 ^可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。/ ~# Q1 {( [; W( D$ D+ t

: F; d+ t, \& }, D( k) STCP服务器端和客户端的运行流程2 W3 Z- a& p! t" z0 W
0 j+ S5 `% |% I2 `! r1 a2 v; c4 z

3 T6 ~1 t; H2 e如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?; R: b. m$ w/ |1 t6 z" c# ]' s! z

! k) j3 l  l9 ]" G! A2 }
9 H2 `/ L  U8 l! z" M
1.创建socket. ^: f% B7 b" N3 I1 D
socket是一个结构体,被创建在内核中+ Q; a: Z& q2 n' F
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& {5 y; P: M8 K2 A$ _) \+ \2 Q0 L; N2 v
6 ?- Z4 J: V# A: z  w
2.调用bind函数# H2 W3 x' \' b6 D7 {
将socket和地址(包括ip、port)绑定。
- C0 D# a! O; g2 v! T 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序7 V# e* Z4 h. {- F
struct sockaddr_in myaddr; //地址结构体0 `7 R% }8 z0 u/ k& g) Y* m
bind函数3 E& q9 d" g- V& }
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
/ O- V. n: r3 ?/ b. o. E
2 i8 }) p2 k" Z7 z! E3 D+ t
- K& A3 i- v0 f' ~8 U& B3 T* O) l
3.listen监听,将接收到的客户端连接放入队列/ s% a* C9 P; i3 C& M; Z* n
listen(sockfd,8) //第二个参数是队列长度3 F; V2 v" x" Y( s$ S& G9 t9 ^' A
( v/ x: p; |. Q9 P- X  X
, W3 n7 d3 ^# ?" D
4.调用accept函数,从队列获取请求,返回socket描 述符  B: t: _& f9 u, ~) x# q. m
  如果无请求,将会阻塞,直到获得连接
8 U5 v  A8 ^. ]; g; w  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数, M, W6 B) B8 u" F" [9 k/ R
- t5 \3 G' A3 S5 M/ O, }( J( G( K

+ Y+ n4 ^2 `; n( ?# H4 v$ |5.调用read/write进行双向通信7 y; X- U  R7 V( {5 P
; @& f! w+ G0 f& T0 t

) _& M" O  X2 T& D* k: s& W6.关闭accept返回的socket# `& ~% {9 }% f$ g
  close(scokfd);
8 b) v, e/ E; i# F  F3 g# u2 Z8 |% r/ w
( q" m0 t: ~) {( [* }7 ^  K( Z3 p$ {( Q; }

& ~6 n5 t7 X) }+ Q% e
: }, i; Z6 u/ V2 W9 M1 n- l8 l- H
下面放出完整代码
8 s$ R& f7 b$ s* g0 V/ Z, z; x  G' W# L
# O/ F2 c8 v. S+ O7 K4 D. N/ y
  1. /*服务器*/
    & F  y& [# I* X; V4 s% b
  2. #include <stdio.h>
    7 i/ w! c# o  @' G4 |+ p" g, ]- d6 g
  3. #include <string.h>
    8 O$ O& [  g9 P5 R
  4. #include <stdlib.h>
    ! @: a- A: W. c+ S0 X+ e5 e7 o
  5. #include <strings.h>
    % W5 R9 i# Z& n$ `
  6. #include <sys/types.h>
    ) v( J$ r; e! y/ o
  7. #include <sys/socket.h>" H9 s# |8 I4 T1 i( c
  8. #include <arpa/inet.h>
    9 v$ }0 I/ F5 k) L! W$ c9 e
  9. #include <netinet/in.h>8 v; i1 Q* o2 w
  10. int main()
    5 @4 y& C5 {7 f
  11. {% y+ Q' o8 F6 `' ~6 I
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字1 K" A7 Z0 }  c3 w, B; y& s
  13.         if (sockfd < 0)
    " f6 n5 M- m8 s$ Z( ]# n) O3 o+ T
  14.         {
    , R# t2 U0 b. ]" J, b
  15.                 perror("socket");
    # h1 y* k, o$ ?% N- `' X
  16.                 return -1;
    ) Z4 X0 Q2 A7 k
  17.         } //创建失败的错误处理
    # U( n7 ~9 q) |$ ]2 }
  18.          printf("socket..............
    2 q6 R! s5 T0 V8 g- x
  19. "); //成功则打印“socket。。。。”$ ^, S2 E4 q, j$ l2 C7 @+ a7 l1 {
  20.          3 V; m! i  F$ n8 Q6 \4 L0 C
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体- o$ u, @- ~3 t% @' `
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)! x* Z1 M* x  M8 `% a
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型+ R. X& e+ U' J# R$ N' d  b
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    * c. ~/ K/ K/ X4 O
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ' D. b9 s; p/ `- Q" [# m8 X+ q" U
  26. ) R7 y: _2 T3 _- m8 i
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    3 Y$ d& _: j3 {# C: e/ z6 g( U
  28.          {
    9 a! v' O+ k# t( d
  29.                  perror("bind");
    $ w8 n' {9 T& u0 _! T
  30.                  return -1;
      a! i% f; L8 ^. @, o
  31.          }; m% f0 x' q7 v5 g9 l2 ~% a
  32.          printf("bind..........
    # ^* F% l6 y5 W( M$ _5 ~
  33. ");$ v' |8 ^! i1 }5 m2 L# N
  34. 0 ^4 I9 d6 h; w( T7 P8 m
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听8 i6 T# p1 l& v7 F- g; B; t' ~& N
  36.          {
    " O' o+ g0 ], I! g
  37.                  perror("listen");
    " u% s) X* \9 K9 ], t5 P/ K
  38.                  return -1;
    . A" l/ X; |9 g4 ]2 X( N
  39.          }" a: v3 M8 I$ z/ S) Y9 w
  40.          printf("listen............
    & c+ n8 C# z, [7 ^/ T
  41. ");
    + }$ ^" D' H' C- |8 z$ ?" @) d
  42.          
    - e' ]6 ]. c: S1 m- a6 N
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求) E: y  z( c" t, e  A1 E
  44.         if (connfd < 0)
    * ~* \8 ^# z/ @
  45.         {
    # T) R7 z) {( x$ u7 |2 r3 t
  46.                 perror("accept");
    ! g- I3 g' U2 {9 g9 f1 J
  47.                 return -1;
    . k0 y4 g) o, I
  48.         }
    # t" R0 ?7 w8 }0 p
  49.         printf("accept..............( Q) R% K; o6 K$ f! ~
  50. ");
    . O2 y# K: T: j
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    ) [. a" `5 n2 S0 z6 n( x
  52.         int ret;
    5 y+ e8 ~% _5 S" y
  53.         while (1)$ w# g8 R( R& B% k+ `0 q
  54.         {4 \1 [2 U( O  u5 S
  55.                 memset(buf, 0, sizeof(buf));
    3 c- p! b7 W' d/ E- ?$ t5 @
  56.                 ret = read(connfd, buf, sizeof(buf));
    1 [: u) \$ V  n# I" D
  57.                 if (0 > ret)
    + V' H( O$ f' C1 x7 M
  58.                 {
    9 R9 E% r7 C! O5 f8 e  Q, D
  59.                         perror("read");
    % K, C; `# N. a1 @5 e/ w
  60.                         break;
    3 }1 |# b8 X4 X
  61.                 }//执行while循环读取数据,当
    5 E( ?6 j& ?$ Y. H6 F: E) @, v
  62.                 else if (0 == ret)
    - D; J  E! u4 d! g2 f: B0 k; n
  63.                 {' S7 B, W% r$ ]
  64.                         printf("write close!! A9 L# t2 ^- o) `) ?
  65. ");* E: t3 g! y. m1 A+ R
  66.                         break;$ X1 b; L- v2 w0 z0 D
  67.                 }  a2 v2 j  U& ?. U* d
  68.                 printf("recv: ");
    + `) {) ^9 D' V0 l4 \# X9 v# [
  69.                 fputs(buf, stdout);//打印接收到的数据9 X$ F2 N; \& m; t( z9 O
  70.         }+ U6 r% [" d" Z
  71.         close(sockfd);//关闭套接字
    % f& J7 V$ S, j+ G8 X
  72.         close(connfd);//断开连接1 l* b4 |+ p; P0 `6 Q, v- g
  73.         return 0;# B  X' ~; Q/ R9 w; E' l+ L
  74. }
复制代码
. U" E, b, u+ [+ \" T% t" T

8 n5 v$ p) I1 @% ~
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    - f9 Z3 `; e5 k1 f. O
  2. #include <stdio.h>
    ! V" F" K0 n* _( L& J
  3. #include <string.h>+ S, y9 M+ d/ U1 ^. F" I0 m/ p5 |
  4. #include <stdlib.h>
    : s/ _' Z8 T) E
  5. #include <strings.h>
    - D& N- ?8 @. M& m0 z
  6. #include <sys/types.h>! D) ]6 F. ]# `/ W# x
  7. #include <sys/socket.h>& Q- N5 Y. Y4 z2 _% ]3 c2 l& m; l
  8. #include <netinet/in.h>5 j: s. s% Y" [, C. R
  9. #include <arpa/inet.h>
    8 F* J: V1 p6 B/ ^, P
  10. int main()0 @* v/ S- Y, [. w3 V5 Q) m
  11. {. y3 ?: K5 |! r& \
  12. int sockfd;7 v# D8 m- t) g# K: G2 q- S& c- f
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))0 Z) ^- x* N% k/ V
  14.         {
    5 r1 B) D4 p, s: A2 s
  15.                 perror("socket");& p! }8 D) X: H: k) S! T( p  G8 ]
  16.                 return -1;
    7 y+ o; z/ m1 U0 H* `! `& R
  17.         }- F, L8 d2 S- d/ O: G- ^. Z
  18.         printf("socket...........7 ]9 i! G  E, n3 k" e7 U
  19. ");
    3 ?5 h7 `6 f: e
  20.         9 @* C2 T# ?1 z& ~
  21.         struct sockaddr_in srv_addr;7 W7 O( a4 I# c4 J" S2 d; V: `: s
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    0 k7 }4 s; f' Q% [/ `
  23.         srv_addr.sin_family                 = AF_INET;
    ! \% R0 N- \* X0 n
  24.         srv_addr.sin_port                         = htons(8888);
    $ u% G4 e' t" T& W( ^: \3 d6 c
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");4 T( `6 T8 e: P  q, I0 R- X% I
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    7 n7 Z$ E3 w3 t) I6 U
  27.         {0 f. E" K1 l3 Z/ \# h
  28.                 perror("connect");7 S( |+ v3 V6 N: }, j, Y! ^% i
  29.                 return -1; //exit //pthread_exit
    9 b& Y) ^- c, \7 `* Q. L7 \
  30.         }& I- a8 a- }" w# `; N6 P
  31.         printf("connect..............
    ) A7 A8 _  U/ E$ F
  32. ");* D8 i3 G! F7 \& h9 l
  33.         char buf[100];! a# \: w6 R8 z9 Y& V
  34.         int ret;
      ^* y0 U; _" F% g
  35.         while (1)& E  P& ~- ~: ?7 V, B: ^$ s. F
  36.         {' l1 @# H* T; f7 e2 }
  37.                 printf("send: ");
    ! R2 v0 W# s, j% t# V5 c
  38.                 fgets(buf, sizeof(buf), stdin);7 r4 n! H1 `3 \( G6 W
  39.                 ret = write(sockfd, buf, sizeof(buf));
    3 [( U) S- w1 ]+ q) }+ v+ u1 r
  40.                 if (ret < 0)
    - M5 R% A& }/ y+ r! j
  41.                 {
    ) a9 b4 ~7 ]" [; i. Z
  42.                         perror("write");* z# e# j" d: T, L' {: J" q
  43.                         break;
    & }' Q+ j! l) R% D6 O3 Z: f2 r9 o% `
  44.                 }  w( y5 h* x4 `% R. l
  45.                 if (strncmp(buf, "quit", 4) == 0)% A" a6 ]/ g: j, z8 h5 w
  46.                         break;0 }' F4 {6 ?/ L3 ~) I) i
  47.         }
      ^! D/ I9 C& f7 Q. l% S
  48.         close(sockfd);$ w% p- }7 H( U  r8 r5 `; }* B
  49.         return 0;% `/ q0 k( }3 z$ X
  50. }
复制代码
5 t0 _$ p8 _6 w+ C  `) r) V- Y% T

- u4 v( d% o5 Z6 Y
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-4-30 16:17 , Processed in 0.078761 second(s), 22 queries .

Copyright © 2001-2026 Powered by cncml! X3.2. Theme By cncml!