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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
) U( I5 w; q2 H' h6 {4 J) s) V7 P8 {
+ ]- f* I3 R0 h9 j% l3 b
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
0 a: j& e8 s0 ?" U8 \$ x  \& ~3 P6 G8 u2 Z+ J
" R& s# ?. p; j' N1 b" U" Q1 I
TCP协议
# ~' ~) z2 Z/ N; `$ _. S, m" N0 xTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。" D+ p" e3 e% y) Z% y
3 l/ X& G' O$ `' u' [1 c5 Y$ S

9 g# p' d" g* ~, e+ n; ~关键词:三次握手,可靠,基于字节流。' ?' S  o" W" f3 t
$ k5 I3 N- s" m3 {" b; z3 W2 @

, j5 ~# ^, x3 C( v" E5 d" `! ]. Q可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
' k+ ?% C% A+ g6 l  e8 @ ! e8 a! f3 u( T5 k
TCP服务器端和客户端的运行流程
6 M' M2 L4 o. {" `8 c! e# m6 P( X6 k6 ?- e8 P# ?7 s1 n2 a
; @5 z/ d* S. }0 W, z
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ _7 Z4 w+ v8 z6 E2 B( K
& M2 Z$ a7 V' ~5 B3 G+ o

, o, W5 g, @( }3 s7 t) g4 N. H1.创建socket
( W/ [* C7 k! E socket是一个结构体,被创建在内核中
  w! Z! p9 D: { sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议+ ~" `  A# t: O4 D& B
( g0 O# D' h8 L) d
0 s6 T  a/ _/ Y8 E
2.调用bind函数
: ~; J: X+ q, M  l+ y  [ 将socket和地址(包括ip、port)绑定。
: X& D) q9 T9 C" H9 A 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
- _+ l% j8 s5 K/ G: X* q struct sockaddr_in myaddr; //地址结构体5 ^3 R" t+ F- a; r' X2 }; A4 ~8 L0 B
bind函数
+ a7 v# c* q* l  E9 H bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
! a8 b- R' G, p9 P0 C) E8 s  I9 K/ M

$ I1 @, b, u0 f' S% T3.listen监听,将接收到的客户端连接放入队列- K  R/ k8 l1 m0 D0 h, ]
listen(sockfd,8) //第二个参数是队列长度) p- k% y' d, A
0 X$ l; O8 R! z# {' [

; o7 x' ^2 r# }0 v/ b4.调用accept函数,从队列获取请求,返回socket描 述符
' ?- m% T4 {& J' S" R7 U+ Z% r  如果无请求,将会阻塞,直到获得连接
* n( |$ f. B7 N+ V  ^' Q7 Q/ q  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数$ ]$ c. z7 f: V! N

4 y9 G: J. k4 Z7 G+ ^5 c
- q0 y* G4 k% h8 a. [+ s
5.调用read/write进行双向通信
0 U; A* d$ D# h& H& Q5 z
8 T0 s! r7 }) D/ Q% L+ u  G) }- X

7 Y& L; y: ^5 x) p+ j* G) u6.关闭accept返回的socket2 G7 K4 k9 r( p5 z3 Z
  close(scokfd);2 X- t9 f1 @) O. H3 }" [2 N3 [

/ u, Z1 b  s. h8 ]$ y

! {3 X8 e& |( @; y# {( }$ ]2 H9 \% r3 k; D- _' o/ p* U

4 _: ?- q8 e( ~5 [; W$ A! b: u9 s下面放出完整代码8 Y6 g5 `4 b7 k0 [% R) h
8 u: v% G) `8 X3 B2 V
  1. /*服务器*/
    7 }6 K8 Y! B7 J, d  l
  2. #include <stdio.h>7 q6 d) y/ p$ h# o6 j
  3. #include <string.h>! o  H; ^* Z6 T3 \0 d
  4. #include <stdlib.h>
    : n* |) T4 T- S
  5. #include <strings.h>! I* h5 \4 O  S) c5 u, ~2 |
  6. #include <sys/types.h>0 |4 z* U  d" X/ Y: ?1 h
  7. #include <sys/socket.h>  z. P6 ]' l0 t
  8. #include <arpa/inet.h>9 R  h* Q/ V4 \! I8 G3 C+ |2 e
  9. #include <netinet/in.h>
    ' `# g0 U! W+ n3 U: u  |' V
  10. int main()
    7 U0 Y+ n, R6 M$ e
  11. {" _4 E7 S/ L; U- d% u
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    . S# l( z; I2 h2 ~9 r
  13.         if (sockfd < 0)  {- V( P! d# g1 V6 f, L
  14.         {; g( L# |% s' h! L3 a
  15.                 perror("socket");
    8 J0 \, b3 I; a9 q3 w
  16.                 return -1;* Z3 Q/ m) `$ \* O: G
  17.         } //创建失败的错误处理
    2 j6 W* R# L8 e  o) _( @
  18.          printf("socket..............! P0 [+ {0 y! j/ q/ B/ _
  19. "); //成功则打印“socket。。。。”
    - x* S$ o3 f6 V+ S' J$ H
  20.          
    & A" s+ r: D( A9 H/ a5 ~! }
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体, A& |5 L- N, m. [% M- c+ g& \
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)% M5 a: ?4 d- c5 L  V
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型% @0 E  b3 X# L5 ^  y
  24.          myaddr.sin_port                 = htons(8888); //选择端口号& O0 q: T% x# ]7 s
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址) J& }0 J/ O) m8 o! u

  26. , ]6 z; F: M" R
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    7 `& I- Z& }. K0 @- Q+ Y
  28.          {9 j5 n( ~0 G  @! {
  29.                  perror("bind");- E  q9 i4 b/ e: b
  30.                  return -1;
    ' h  I5 P# n3 Q1 ?& m& h
  31.          }
    % N& f& b( f8 S, A( n: D5 W, L
  32.          printf("bind..........
    ; w; N6 j) B0 |4 A+ r0 j7 `: N
  33. ");: A" t8 j3 V5 T+ G( b
  34. / z: K; T5 I3 w" k9 u
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听0 D5 \* A1 n: b6 r) E& s
  36.          {; E2 f& G" R/ B$ U
  37.                  perror("listen");, L6 V5 D; z8 `6 J' g( D
  38.                  return -1;! Y# \; N% B, v% n/ m9 o# w; j
  39.          }
    # X8 A8 C3 v$ `# t7 X' |
  40.          printf("listen............9 p! r& N* }5 Q( P) v# J
  41. ");1 R) R4 Z  P# X- y+ \# {
  42.          
    8 Z6 w; V0 q; M0 M; s6 \
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
      C, B4 j( P. G! X( z
  44.         if (connfd < 0)! o0 q' ?' h2 Y
  45.         {
      K: q& ?& Z+ U9 A
  46.                 perror("accept");0 @- w9 d, A8 M6 e' `
  47.                 return -1;
    # H9 a: [& Z4 O  O
  48.         }& W* ^& T1 N, A
  49.         printf("accept..............! O. W( k8 u% A9 Y7 @
  50. ");# \$ w4 o; v9 h. u5 ~) G. `6 E
  51.         char buf[100];//定义一个数组用来存储接收到的数据1 |3 F/ \# s& N) g# @
  52.         int ret;
    ! B7 X( J+ p6 b1 Y0 c# K5 X
  53.         while (1)4 J: p  Z  T& \# f. d. M$ c" j
  54.         {
    9 R% I1 V+ f6 z( P, W# f
  55.                 memset(buf, 0, sizeof(buf));5 o0 x% y/ S; Q) `5 V0 r4 E7 G. h
  56.                 ret = read(connfd, buf, sizeof(buf));
    4 \  q! M& `8 d" D; g
  57.                 if (0 > ret)9 W* S1 M# O" q
  58.                 {
    ! g1 Q" t7 |9 D9 {* {5 M+ d
  59.                         perror("read");+ e* K6 T8 m* z! c0 _7 h) {
  60.                         break;1 D) f1 J' N: g, Z9 l7 l
  61.                 }//执行while循环读取数据,当
    $ a9 m0 a5 v" X2 ]! l+ m5 I
  62.                 else if (0 == ret)
    0 l+ a& I: k$ a$ V' ^8 R" v
  63.                 {, s6 L8 K* V9 C# m( @1 r% B) Z
  64.                         printf("write close!) c$ V5 w. S; s1 O- v" @% p4 Z
  65. ");9 p. G, x9 J5 i# u4 ]
  66.                         break;8 q$ j  b! g" Q5 w2 c
  67.                 }6 I9 S/ _8 [% s7 R5 ?- E
  68.                 printf("recv: ");
    % q/ g2 [8 j2 r3 d
  69.                 fputs(buf, stdout);//打印接收到的数据
    ; T) P1 \$ e2 |8 }4 f
  70.         }" Z+ T* x+ @+ r1 }$ W
  71.         close(sockfd);//关闭套接字4 {( C$ u/ t- k! T, C4 g2 m
  72.         close(connfd);//断开连接
    8 R/ I  O' F5 E/ @
  73.         return 0;
    6 b5 ?. W, m( _; ~
  74. }
复制代码

1 U1 p* b% V) y( H" O+ H3 \
2 s6 B' r* T" @; t
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    ; ]- B, t, i! d
  2. #include <stdio.h>" H, b- {& X) s5 z
  3. #include <string.h>% M9 Y! B- N' F: m2 q/ f3 x
  4. #include <stdlib.h>, r- [! U0 o. L6 u9 e. \) X6 g0 l
  5. #include <strings.h>
    $ j4 R, e8 R: ?$ u6 {& ?/ r: n" |
  6. #include <sys/types.h>
    ( @4 ]# n6 I3 Z
  7. #include <sys/socket.h>9 l4 E% j$ S' ]* \1 ^0 \5 _
  8. #include <netinet/in.h>) n) U; \, o. [+ C) }; {
  9. #include <arpa/inet.h>
    0 [5 a9 G) |& d# w
  10. int main()
    9 N9 D1 N' q0 n1 m* E/ H- [
  11. {, c7 u5 O3 u! I7 K9 |* R+ W# Q
  12. int sockfd;0 k+ h3 {& ]& r6 f" F2 p- J
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    % {- Z$ S! p+ K: H' z- [: W
  14.         {
    9 K2 X) x7 r) o( D
  15.                 perror("socket");
    9 Q! t/ a2 W% B) l8 s6 \/ m
  16.                 return -1;+ D4 t4 D. i: |& ~) `+ H$ Q
  17.         }
    , {  ]4 r5 B5 [
  18.         printf("socket...........
    & k1 I( R# I1 t/ d4 P1 W
  19. ");
    ! O( ~3 V2 i! T2 R: c: z
  20.         
    2 S# e8 r( O! b& b3 h) g( T
  21.         struct sockaddr_in srv_addr;4 C9 T) G" P  s6 d/ t' ?4 p
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    3 c; ~* }2 o7 Q9 ~
  23.         srv_addr.sin_family                 = AF_INET;
    5 |) Q7 F% Z1 C5 T8 q; |
  24.         srv_addr.sin_port                         = htons(8888);
    ) q. \5 }9 n: k: a! V
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");0 u- h& R5 b) x; T  Q
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))) E+ K: m6 x8 P2 n3 H0 G- N: J1 t
  27.         {8 Y+ S  t/ }7 }+ x* @% Q" Q* V
  28.                 perror("connect");. i* R0 \- Y4 B
  29.                 return -1; //exit //pthread_exit( h' `1 E  R4 ?5 U8 N1 {; G
  30.         }% N1 ~/ i4 L; v" k4 }% a2 {
  31.         printf("connect..............
    # V! c; O. X& w: G2 R$ l' u, O
  32. ");, r- h7 O4 U& C. h
  33.         char buf[100];
    5 |# W( d( p. Z' X/ ^5 D# d# D
  34.         int ret;: h* F0 y9 A1 o1 i. _3 e
  35.         while (1)
    3 ~6 V+ O9 k6 o* E
  36.         {
    ) Y# n# w; p6 j& b
  37.                 printf("send: ");
    ; h* N2 I$ {: K: c* i* T% S
  38.                 fgets(buf, sizeof(buf), stdin);
    # M& J2 Y  T$ }; F3 E
  39.                 ret = write(sockfd, buf, sizeof(buf));; b0 v: x/ D/ {8 ^% [1 o# J
  40.                 if (ret < 0)
    ; z# X  q/ i1 J: Q2 _
  41.                 {4 r2 @" m6 Y$ v0 i
  42.                         perror("write");
    ; @$ C) b0 G4 u5 Q6 N" Q
  43.                         break;
    3 S# y, j+ H4 Q3 i$ i$ b$ ]: {
  44.                 }
    - m" Q# w5 S4 \/ o
  45.                 if (strncmp(buf, "quit", 4) == 0)7 n3 A  A& T' ~9 G/ \) h3 h
  46.                         break;% C0 x1 V; G  |  d
  47.         }0 t/ Q- [# v: ~/ t# e
  48.         close(sockfd);
    % d# q6 f& G! x2 n+ g* V6 j
  49.         return 0;+ w' {$ j6 k9 ~, Z, k9 b/ Q) T
  50. }
复制代码

" ?) V* `5 V  w8 K2 T9 e1 D: O5 X, Z! A9 A& u+ ]" u
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 12:08 , Processed in 0.122696 second(s), 25 queries .

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