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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
/ e  D! M; u: @/ l3 i2 k( C8 _# ?1 E) O) k+ t$ R( {
0 }7 O5 O3 y7 V) |/ J7 L  m
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。# _& v7 E  ^' X2 f9 d# i
: H( R2 f" i) F" R1 m- V

7 a. A& N: y$ f, z6 f6 \4 W# n2 A6 Z) _TCP协议' m. s" E' Y6 k, [
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
' d3 q, i6 g8 j# R9 S, z( J% _/ ~0 R/ i

8 u) ?. M: V3 p7 W( o7 w关键词:三次握手,可靠,基于字节流。8 T' z& e* {6 o! w, Y
& [8 k3 @2 O& q7 F5 s! W$ B- f; {
" W: ~* i* d9 c& v* [: w
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
$ b9 `1 B8 R- ?& k% y$ }
; T+ O' M: A& X  E3 Y; CTCP服务器端和客户端的运行流程
3 d% i9 M$ C0 ^$ H/ t
2 {, p0 S% j- h, e
$ d: ]# A: a- |$ l$ f( }
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
* C4 u. X: K- M- O- l
3 B; c' f" t2 O3 C8 c" S  Z

' ~  Y/ m3 J8 m3 l# f1.创建socket: i8 Z) o( L9 q" q2 R
socket是一个结构体,被创建在内核中& `7 P8 {; D6 r5 K* I# n
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议. V, }4 W: Q5 T# A, _; _8 P$ f* y; P
5 y: m2 u. x0 ~% v  _

- N$ r8 i* K6 G4 ?7 N+ L6 E7 W2.调用bind函数
/ [0 B3 J; y9 K3 A& Y. X) | 将socket和地址(包括ip、port)绑定。
. n) d; ]$ u4 ~" S 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
2 A8 u, G2 M( V# s struct sockaddr_in myaddr; //地址结构体7 C  R7 T0 u4 v; O6 P" T
bind函数
( }$ t% v2 |( d) M% K bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
" S1 `) b+ R( Z9 {) G4 b) I3 ]" h7 ?9 K( F7 b5 x  I3 S; v

  z/ I$ n( R$ N0 h) U4 j3.listen监听,将接收到的客户端连接放入队列. d; l: [" m7 l) u# i0 ?
listen(sockfd,8) //第二个参数是队列长度
5 b4 f7 v+ ?% F" {9 O0 e8 N7 b+ L( N. K5 K- `9 k6 l2 w8 E
) {" n6 P1 j. ^$ V+ [5 V' t, J" _/ o
4.调用accept函数,从队列获取请求,返回socket描 述符5 p" `7 w9 K; v9 P& `$ p  S" }7 c
  如果无请求,将会阻塞,直到获得连接
4 b( `- d7 c% Z' e+ U/ Y9 G4 E  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
: Z% H5 C' @6 }+ B  d" A0 m% n9 b( t! a: p( C3 w9 f( R  q
. p9 L% O! r  B: h$ j& ^
5.调用read/write进行双向通信6 }0 ?- I1 ?9 g" }' L9 C

$ X* k+ z) ?" L3 `
" e) s* K/ K) K9 w
6.关闭accept返回的socket
& e1 J) [; \2 z7 R$ q  close(scokfd);
, [$ }; ~1 Y% B8 h: O* b; {3 V- I6 y. J' i0 a" g/ m  \
0 c5 E* n3 s0 G9 \

1 f! j/ S# u! T' e: O) o

5 p- z2 Y/ ^3 b# {下面放出完整代码
( V- W: e5 T# |2 U, Q8 I$ {+ R0 v$ Y& P# K! q% {- p( ]9 m/ I
  1. /*服务器*/6 b5 X6 i% d9 l
  2. #include <stdio.h>' o( ~. |! z9 l( `" j
  3. #include <string.h>
    % I5 r4 t( u) @% ~
  4. #include <stdlib.h>
    2 z; L+ E- ?$ e0 A! P4 r
  5. #include <strings.h>8 u0 {0 N3 r7 Q0 {% S1 t
  6. #include <sys/types.h>/ l- y+ d( E# A# y( _8 k
  7. #include <sys/socket.h>5 G% D  G9 c+ _) w8 q6 N
  8. #include <arpa/inet.h>+ T. `9 A2 f1 Q- F4 F& e% O
  9. #include <netinet/in.h>
    4 v3 V, O/ q$ N/ A* q6 O
  10. int main()# U8 C/ n8 y0 n* }6 S
  11. {
    0 J4 r3 q8 o: v3 X! _: Y# Y
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- M# @: j* `; l* \
  13.         if (sockfd < 0): n+ E* c2 n8 I8 @) i! W$ L6 V& Z
  14.         {: A/ y5 K# n, H0 P  {
  15.                 perror("socket");
    5 l: Z# w  z/ z2 }. {9 z
  16.                 return -1;
    - C4 y1 U( ^1 ^' V) h
  17.         } //创建失败的错误处理
      R, H, w  ^! v5 L; \: W  c1 `
  18.          printf("socket..............8 ?" B! _0 K# a
  19. "); //成功则打印“socket。。。。”
    , [. u4 ?+ K6 ^- A5 z8 c
  20.          
    ' _5 ^, ]' o; S8 @4 ^3 z* q! i3 `/ J
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体# X1 c" ^3 S+ X
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见). k! t% ~( e% c' V- j  _
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型7 _% a  X/ J' D# r, {  D; X
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    / _. b- B: x6 E" j- |
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ( U' k+ F/ }1 B( v6 [- P0 C

  26. * r+ v" O3 ^. P5 a7 z& p; Q
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字* k/ z; `1 H7 N" i+ m( }
  28.          {$ m3 j9 `$ g2 z$ r% `) k
  29.                  perror("bind");
    % p( N, M. v! `) Y( Z
  30.                  return -1;
    / u! q5 @  U/ i+ m
  31.          }7 V* s# u( @9 a" p) x) b6 `
  32.          printf("bind..........+ Z$ g  t* B* c, Z+ D% ~4 x+ r
  33. ");1 \$ Q$ Q+ A! r4 h

  34. ( V% z  }, `, i$ w8 F! l, [; q
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听$ i6 Y- D- V  h+ q4 j$ R
  36.          {, K' K+ o" j/ ~0 H% o
  37.                  perror("listen");
    3 z$ @5 e! \* r) j3 {
  38.                  return -1;4 b4 K- l7 g6 C2 _5 E4 L/ c
  39.          }
    7 h# @9 p; S% i# y+ d9 h7 j
  40.          printf("listen............2 Q3 L: f8 o  g) B% j- C
  41. ");5 v$ N8 L4 f8 s) ^- l4 f3 F% x
  42.          2 W. m4 O$ O6 x3 h3 o+ q( k. n6 g9 r
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求9 b- I; {# M6 ]7 W5 p) T, k
  44.         if (connfd < 0)
    ' a" p* M2 S9 r; `4 c* ]6 s
  45.         {, n0 Y: V: b+ X/ b7 k
  46.                 perror("accept");
    2 s4 e/ g, b  X7 u/ z, R9 y3 r8 B5 N3 C
  47.                 return -1;
    8 k9 a' u* U/ F) z3 f
  48.         }
    3 Q) _; _+ \$ ?
  49.         printf("accept..............+ ^* \+ f# i. T, [: g4 t& |* R
  50. ");
    . _$ m/ S1 ?( B+ A  Q  O' o2 J) F
  51.         char buf[100];//定义一个数组用来存储接收到的数据% G. i( K6 R  s- s' ]5 S
  52.         int ret;
    0 t4 n1 E5 N% f4 H% F* w
  53.         while (1)( ]) n2 a! d, \$ A9 m
  54.         {: [/ e. n0 S6 q& R2 N
  55.                 memset(buf, 0, sizeof(buf));+ t1 C: a. Y1 U! L
  56.                 ret = read(connfd, buf, sizeof(buf));
    1 \" L7 W8 p7 G, W9 ~$ R6 o
  57.                 if (0 > ret)
    0 d7 \0 R- }6 M4 p( f
  58.                 {
    , Z# \; }, g! I" ~' f4 @
  59.                         perror("read");5 M+ |- k' r. g& n5 P% t! ~
  60.                         break;
    ( L6 U+ K* M; V) |
  61.                 }//执行while循环读取数据,当
    : ?1 A' e. z% H% x9 s
  62.                 else if (0 == ret)* W, m5 N0 {0 g' C) d
  63.                 {2 C4 H& l: u+ O* I; l
  64.                         printf("write close!. D! k! `& W, o* Z5 d% b; s7 }
  65. ");
    " `/ x) f# y6 @- @. ^
  66.                         break;
    4 _; v  p6 K+ }. z5 Y
  67.                 }
    " Q: a4 ^+ |# m# s, o
  68.                 printf("recv: ");' i6 T( L% I7 k8 c
  69.                 fputs(buf, stdout);//打印接收到的数据/ C, ?0 c6 r/ c9 t4 [. C( r
  70.         }6 o/ i: q- l) Q% O, R
  71.         close(sockfd);//关闭套接字" v# O+ a7 z  `3 K& x- ]
  72.         close(connfd);//断开连接
    # e  h/ b4 f/ q
  73.         return 0;0 V& R  q$ J# j" [
  74. }
复制代码
, g# d* f% a4 ~7 f. x) M

3 n/ O6 \9 }& Z- S2 @. d: E7 T
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    # r  W4 G' c  A
  2. #include <stdio.h>5 ?( K: L, T. C4 L
  3. #include <string.h>
    + b- x* j4 ~+ s
  4. #include <stdlib.h>
      K5 t* L2 r& D! R/ ]3 P
  5. #include <strings.h>
    ( g, R0 s" a: Q  F6 h$ @+ g" H
  6. #include <sys/types.h>
    3 v9 H+ n, J8 q3 z$ N" R2 p7 o5 a
  7. #include <sys/socket.h>
    . Q& W5 ^2 X: \1 U
  8. #include <netinet/in.h>
    . x( F; h; Y0 p: f
  9. #include <arpa/inet.h>6 I+ `- J- t( g7 Q2 ?
  10. int main()
    1 D* |. R/ {! Y2 ~* M7 k% A8 K
  11. {' l: {8 r) y9 V# L/ S
  12. int sockfd;2 Z# H+ l( l" \0 A8 l# f
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ; C! T/ q; S! D1 N* p
  14.         {
    $ F- h; B& k0 J. P) t9 l
  15.                 perror("socket");
    & C, ]% h) W1 K' N
  16.                 return -1;
      F+ E" M- N+ E6 f7 h
  17.         }1 D0 q; U1 R* c" t
  18.         printf("socket...........! |+ r8 _! W& T4 o
  19. ");4 g0 Z. G4 S" C! J. q/ i2 h9 {7 c- I
  20.         5 B/ N, U5 D1 M# ^+ K! N4 ~+ A
  21.         struct sockaddr_in srv_addr;7 P2 J' m* o) k  }) ^; h; Q
  22.         memset(&srv_addr, 0, sizeof(srv_addr));9 [) E  @$ F2 m. }
  23.         srv_addr.sin_family                 = AF_INET;' Z  X" U: k$ D$ z; s% g- M/ @
  24.         srv_addr.sin_port                         = htons(8888);1 }, K3 Z2 b. k1 h/ ?4 h
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");( x+ h3 T4 b' H8 L
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
      v3 _0 A: e* X) h9 C
  27.         {
    / k5 N- c3 x# t8 M
  28.                 perror("connect");
    2 s8 u9 n& \' G. I% N# t, {+ @
  29.                 return -1; //exit //pthread_exit  J  Q/ H0 a) A" p5 Q6 C
  30.         }0 e9 G& w  ?( L. p
  31.         printf("connect.............." t+ h8 N' G* U( u+ q
  32. ");
    ' Y" b, @5 H: n: t. I$ B" R
  33.         char buf[100];
    5 t. q) h- b$ w- L: A2 n
  34.         int ret;3 h5 x/ w- |% {& l, l: E1 Q
  35.         while (1)0 r" z, V6 c" z. Q/ I
  36.         {# z# b, z6 O. R* U9 y, h
  37.                 printf("send: ");
    & ?. {, y8 e5 H" ~# s2 x5 t  r
  38.                 fgets(buf, sizeof(buf), stdin);
    ! r# \8 Z5 k$ z  t3 D/ g) C& |
  39.                 ret = write(sockfd, buf, sizeof(buf));
    , f. B9 v+ L8 s9 L
  40.                 if (ret < 0)
    + }& u1 T; w2 D  v: i1 |
  41.                 {
    " v. M( }6 T2 Z& ]& E& [
  42.                         perror("write");# G# {1 }2 Z) ^5 u$ q/ j8 D
  43.                         break;- O: S( k# a9 F) r6 j4 g) j
  44.                 }
    1 C. [  u! }0 `- X
  45.                 if (strncmp(buf, "quit", 4) == 0)
    ! O: l' s. f: d, ~( j
  46.                         break;
    ) a0 ?$ h/ ~7 z) F/ ]+ K
  47.         }
    , F5 ]. q. ]4 @+ W1 X4 N7 S* S
  48.         close(sockfd);: h& S$ ?; M1 ~) W6 m! V: W
  49.         return 0;2 y& a" s- m. M: k- g- z* n
  50. }
复制代码

: ~5 a; t+ u% Z" P; ]/ O( J6 s9 s9 A0 C; v: v
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2025-2-5 20:02 , Processed in 0.128981 second(s), 24 queries .

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