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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。! v2 N. K. \0 I! B
# O3 W4 R# B  ]3 ?- P# m* `
$ r; ]* n2 M  L" t$ V4 V
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。& e, U) D* a  m2 j6 J! r: f

+ u) P0 _/ \  }( N4 O
  ?/ X, u1 u! T. ^, X, O
TCP协议
# G+ x. |; a/ k7 OTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
; ~$ t* h% b+ d. p
* k& k$ p- @3 w/ X% r
2 q. a- l/ k6 ?# @5 l
关键词:三次握手,可靠,基于字节流。9 d- i' c2 o9 s

( s& |! H+ L( ?% \- U
: L2 a' N( C& T( K3 ?
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。% d3 U4 E* f; o8 ~
+ t/ Y# F, k( K- h3 v  W! a
TCP服务器端和客户端的运行流程
" N0 ^* q  K9 `6 L( S( I5 u- w6 g" _( W( I2 c) |" T
8 _: G9 h0 x, I2 \
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
) g& L9 P- f$ M% k5 [- L
2 y) B/ ~# n; j. h# |8 s
$ t. j0 b' F0 Y
1.创建socket8 T  l' T7 A1 o+ H
socket是一个结构体,被创建在内核中
# q+ z8 |; U5 M8 R1 N2 N+ V sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议2 [! S; E  J( m' u2 h
3 A8 }2 {0 [& z1 l
% L; X' i  I& K% U1 r) z  c
2.调用bind函数
/ Q+ e0 _2 r6 w) F2 S& h# e. E0 ^ 将socket和地址(包括ip、port)绑定。, P& q  P/ a2 C8 C$ [9 \
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序& A: s" R$ m1 d1 b
struct sockaddr_in myaddr; //地址结构体
  h/ }+ H* @5 O7 j# F bind函数
5 b2 \6 ?+ c* C8 ] bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* W6 Q4 t: A* V- C6 x
- z  m2 k! `+ G8 R

3 S. d+ |  a8 e) e1 k9 A1 I- y3.listen监听,将接收到的客户端连接放入队列: G, M' U/ A) f! H3 x) p) \! d* a% R
listen(sockfd,8) //第二个参数是队列长度
4 v# ]2 v7 V% N( Z0 A! h9 j1 o- E
% _' l1 N) T. R
' Y8 Y' N, N$ O* D
4.调用accept函数,从队列获取请求,返回socket描 述符; H3 v2 s: O$ W/ ]6 b- k
  如果无请求,将会阻塞,直到获得连接
) I; V- X4 d4 w$ j. M8 ^  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数$ O+ g- Z7 r- y; l- Y! {2 ]

! z: O& [6 _" s. l
# U+ [/ ?6 A& z6 r& [2 s# B
5.调用read/write进行双向通信% Q# U2 C) K( R) m  N

  }3 R& L0 X9 j. F+ Y! j& C* c0 r
0 O/ x* o; v6 ?, b
6.关闭accept返回的socket8 `7 F1 t" \& k
  close(scokfd);
) N) ^6 b9 T$ ^9 l0 m1 Y# P* r0 P& b

# p( v+ t2 ]' O& a
5 H0 ^- P5 d2 @4 Z* q
* t. h; B  P, B
下面放出完整代码. `/ E7 V7 A3 X* V
6 C$ A5 U4 F. a2 q5 @* b' D1 K6 b
  1. /*服务器*/% d0 u/ m' ]. B' Q
  2. #include <stdio.h>' v$ I- X6 e) C7 z- c" D; h0 e
  3. #include <string.h>6 _; }2 z5 c  T/ E
  4. #include <stdlib.h>3 v1 K7 T4 Y& _2 ]
  5. #include <strings.h>2 h6 t* {( N7 X; F# O: j% O
  6. #include <sys/types.h>" s: l1 z* ]5 h& u0 E" g
  7. #include <sys/socket.h>/ G  ?4 I+ k, e
  8. #include <arpa/inet.h>
    8 e& ?( e. v0 _0 E0 ?+ g) q* z
  9. #include <netinet/in.h>
    3 H7 x) Y/ c" x- y* {
  10. int main()# {0 R" p9 n5 Q0 a% r
  11. {
    $ n4 B# h7 x5 j9 ~! C, `
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字" ?9 H- k% i# `: u& M2 f% r. n6 z
  13.         if (sockfd < 0)
    1 }" k9 j1 m5 u' y) y1 K
  14.         {3 S/ V8 G6 U  G! O$ j' h3 s6 {$ Z, D
  15.                 perror("socket");4 X, s. Y( T# V
  16.                 return -1;
    ( T8 D( {3 v% w! ]3 c( U
  17.         } //创建失败的错误处理
    4 ]! |& i# n' Q: E4 F# y
  18.          printf("socket..............( S1 X# `% C8 w$ M2 X3 k4 o* S
  19. "); //成功则打印“socket。。。。”
    7 h6 f* M( x# e3 R7 X0 X7 ~
  20.          
    & S1 h9 \$ {: b3 a  M$ ~2 k7 W
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    0 }8 Q7 s% }5 {$ y
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)1 r. c8 J- q# p( b; t
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型/ z5 |" K) z4 i4 n/ [
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    . ]2 r0 ^* F( E0 u
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址9 ~: Z4 y7 q1 z3 j0 b2 g
  26. 1 _  o) \& u9 R8 }- v
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    % Z7 a1 C" P: B+ Q7 E% W
  28.          {
      ~- m/ F8 w8 i
  29.                  perror("bind");
    ! j2 @$ |2 j8 B# k+ _+ g. s6 r
  30.                  return -1;2 t3 ]  G* v/ R) G! n
  31.          }
    & o, j- ~/ r$ [/ s9 e9 y- y
  32.          printf("bind..........
    * F! R& s7 o  {" h! X
  33. ");
    5 h4 U: T5 f9 D' r, A" G

  34. % v  E& v  \( x1 G* B& t( M
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    3 R; @! u7 s! Z% X
  36.          {! E$ |# \, J9 i: g0 W, o  a
  37.                  perror("listen");# v! `& W( \7 I0 y* h$ z
  38.                  return -1;, Z/ ~0 q/ d. I; M4 }
  39.          }
    8 n1 y* [. y4 C- G$ F7 W% s& N/ i/ a( W& E
  40.          printf("listen............
      U% i: o3 L' x/ I7 c. f7 }
  41. ");
    # R) o5 R/ c- F  j1 u: s) I
  42.          6 H' U6 U% T2 _  ~
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求/ k1 o& v9 |. ~) V9 d9 O1 F
  44.         if (connfd < 0)$ \2 q0 b; J. {# b
  45.         {) i) j3 ]. e8 X7 G
  46.                 perror("accept");
    5 P! U. @) N9 l3 t! g- D; v- T; s
  47.                 return -1;
      d7 _; \0 R$ w4 U
  48.         }
    0 t" b5 @- q5 R% B* x  k# w  u
  49.         printf("accept..............
    6 u$ ]9 {/ N9 G" m1 s
  50. ");# A7 q2 m9 p) D& @) K
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    7 r! i  T1 ~6 S  @, m2 `2 r
  52.         int ret;
    - E4 @3 {' c  y, r; S+ d# e
  53.         while (1)9 O* L* ]( Y) z' b1 n0 ]$ C
  54.         {
    * h2 @5 I, E$ @
  55.                 memset(buf, 0, sizeof(buf));4 h9 V/ ^7 N: j
  56.                 ret = read(connfd, buf, sizeof(buf));
    ( Z+ k3 j0 f3 A' K9 ]6 O" O- r
  57.                 if (0 > ret)
    * l, e: w, I7 M4 g
  58.                 {
    * u; f% e  V! @
  59.                         perror("read");: l7 D& Z- k* A$ r
  60.                         break;
    ) I) ^! j" B) i% f
  61.                 }//执行while循环读取数据,当, D) r# i& `- ]% m7 n
  62.                 else if (0 == ret)/ o, ^3 \6 R- ?% e: W. Z
  63.                 {  h6 H, n0 Q5 r" }7 x4 }8 p
  64.                         printf("write close!8 E" K3 X4 [/ g2 H2 F* X4 b0 @
  65. ");
    ! z" R' f; m+ o9 H: ^
  66.                         break;
    , b3 Z( {; m% v  x3 l- T9 I
  67.                 }
    8 W0 f  x# H0 G; I. y6 C1 q
  68.                 printf("recv: ");& L# @9 w2 h: S1 G1 w1 O
  69.                 fputs(buf, stdout);//打印接收到的数据
    # D( E& B9 T! e- X  J
  70.         }  ]7 n3 k, o) R  E: K
  71.         close(sockfd);//关闭套接字& @! |* Z) f. t* y% p+ ~! V' z+ E6 `
  72.         close(connfd);//断开连接
    " |2 G3 `$ i: {, N
  73.         return 0;6 h4 M% K  ]/ {) a
  74. }
复制代码
; }& I* e9 h3 \5 U3 x( j
3 I' Z0 m9 h" M" k
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)  f- J) j7 K8 X( r
  2. #include <stdio.h>, q$ A/ R( _6 @$ ?
  3. #include <string.h>
    # J/ i+ R! ^! B, {& F8 j7 ?
  4. #include <stdlib.h>' v* u3 R/ Y' x" K4 i4 b
  5. #include <strings.h>
    " Z* l) O  W5 R/ f& m
  6. #include <sys/types.h>6 n0 G5 }* G0 \# W0 r+ `
  7. #include <sys/socket.h>0 b+ i- S4 o6 m6 ~# r# V7 u: t" H
  8. #include <netinet/in.h>
    : ~; f& k5 P; S( \
  9. #include <arpa/inet.h>
    " B( b5 I& k5 N6 L3 i
  10. int main()
    . S+ a# ], y: S4 E
  11. {9 }( \* m) q# x1 l+ G2 Y8 b  ]3 N- k' ?
  12. int sockfd;
    8 h  T- T) G' D+ \, L
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))8 a. B6 f4 e$ t. G4 \! c
  14.         {( \& p* o% Z7 P' \
  15.                 perror("socket");
    # L' N  V2 ~3 Z% G
  16.                 return -1;. V2 k! ~. |* d+ Z% \
  17.         }
    2 D: L; {- D: p5 l1 l4 ?, T0 r
  18.         printf("socket...........0 K7 q$ i& p7 d) k4 ^
  19. ");
    $ n8 q3 T1 f( F8 o2 ~+ l  ^
  20.         
    ! f1 a4 b/ V8 ~" ]
  21.         struct sockaddr_in srv_addr;8 L- J& A1 a# b: l0 k% C
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    0 ], z8 ?6 m% _  }6 ?' t! u  U6 L
  23.         srv_addr.sin_family                 = AF_INET;
    ) f5 j2 A" l, z1 q
  24.         srv_addr.sin_port                         = htons(8888);( I$ B: O6 M) x+ P5 b4 Z! O
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");: O$ X8 I/ }  Y9 O; Y' h5 J; M
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    # L' E# l3 \2 \( d; S9 i
  27.         {
    0 C! X. g( A6 O& }4 a6 t
  28.                 perror("connect");
    3 S) P# P: R  Y' m
  29.                 return -1; //exit //pthread_exit4 g0 M& ?' M5 }9 t( m, _# d; g
  30.         }
      _0 ?, n6 r! Y  Z7 I2 `, }. G
  31.         printf("connect..............
    3 r2 \  }! S$ c. J: P
  32. ");
    % R' ^- X7 A) t, I' y' m, L
  33.         char buf[100];
    2 E, J6 _$ h" h0 \& ^) _& m
  34.         int ret;
      s. O2 m  n* v( {7 g
  35.         while (1)
    8 C' h, w7 C) a% P, u8 G8 G
  36.         {
    ! |- z' Z+ T1 f2 }6 }) f' V& i
  37.                 printf("send: ");4 Z0 p9 F5 z  s) l9 w# \
  38.                 fgets(buf, sizeof(buf), stdin);! M# Y. M% j" V3 @6 j. ~2 f) ~
  39.                 ret = write(sockfd, buf, sizeof(buf));% c% x" s6 P  \3 u" a
  40.                 if (ret < 0)
    8 `( R. d1 `, g$ G
  41.                 {
    9 _8 S- n! d4 \" `, [" v  i* M% S: E8 ^% I$ {
  42.                         perror("write");3 q7 _/ Y: a: @9 G8 U% b
  43.                         break;" W" P) C, Y' o$ P
  44.                 }
    . b8 M% U& c. u! J9 N0 |: D* a9 i
  45.                 if (strncmp(buf, "quit", 4) == 0)) \* Q# E5 V$ }8 p8 X
  46.                         break;5 a9 W0 w, I8 S  C1 l& P% E
  47.         }
    # f7 A  f8 A, {! w7 n$ M9 N4 ?+ z
  48.         close(sockfd);
    % m( n& G" \' m* P' Z1 T6 n
  49.         return 0;1 n/ O1 q  {- @% d2 W
  50. }
复制代码
( T) G. k2 H% e( y  A6 v, `6 e

) W# d" ?8 ?6 m: d( M
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-19 19:32 , Processed in 0.063034 second(s), 22 queries .

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