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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
0 X; I$ _$ m4 g  N8 V- r1 S# T! e0 m% d0 c* I' i8 I) w; e9 Z

6 P: v* Q4 c) O4 K$ lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
. ^! q' A1 }" |1 A. S1 Z+ J
4 X6 _3 W0 o! a; w
2 H1 O2 q+ f5 W5 K9 Y! v
TCP协议) `' s* q2 z5 A
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。, l2 x3 Y) v! ]" P$ o6 q% C

: R) s- f7 w7 _3 r* O2 q( B
' |6 c; K/ t; D
关键词:三次握手,可靠,基于字节流。7 [7 a' h) N2 g& w7 K: L  |

3 t( X& a8 [; Z% T

, R; E" U7 i; [# z可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
( F2 u8 |! Q$ i# ] 0 S, I) ]5 P; f# n* J! v* F- V
TCP服务器端和客户端的运行流程  h% i( s& M& h: w" ]5 j) L
4 Y8 Q# V- h5 U! C4 t3 a0 e: L/ n4 t8 O

1 Q9 e9 ?  k) M2 g: T0 [如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 T$ `7 C7 F$ Z
4 g) H( Q! ^  Z. l+ T- E

5 |% a) b5 B& b2 V5 d1.创建socket
2 K  s3 J6 F) y) L& [ socket是一个结构体,被创建在内核中8 ]. A% R! A6 N* p
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议7 g# K1 R3 Y$ K1 I# e3 f
3 h  o% B6 v/ z; w* b

  i1 I" @+ q2 q2.调用bind函数
3 j3 J( G! C% M$ u+ L! v* C 将socket和地址(包括ip、port)绑定。9 L/ r" j9 c4 o- p# h
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) y0 F! j6 }& F
struct sockaddr_in myaddr; //地址结构体
) ?: Y0 J9 l9 F* {+ [# _ bind函数
; O9 C) a+ I4 x% M bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
; @) b. ]! Y4 n4 |
. i' M/ q% Q+ @& N
  ~6 B" b! n8 D. c, b, c
3.listen监听,将接收到的客户端连接放入队列+ S9 b* n$ |* D: }
listen(sockfd,8) //第二个参数是队列长度2 i4 p" T$ S) g6 j
3 V0 Y- c" m' b* E+ k0 q

, ]/ X. G( T0 q, X4.调用accept函数,从队列获取请求,返回socket描 述符& v, `6 U* m5 K# q, S
  如果无请求,将会阻塞,直到获得连接- n! f9 W7 m+ ?% }. o; K
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数( O5 s& D7 A  x# H: d
5 u( u0 N  d% N  `
' T, s3 e+ l! [# h" l
5.调用read/write进行双向通信. V# ^6 a3 P/ M, A& M

( C( }( q* e! o

* j. B: x/ Y3 X2 L* |6.关闭accept返回的socket
+ Z4 f9 m* y7 c  g/ f1 A  close(scokfd);
. C- j* G. d: w# }# t7 e
: ]) `7 e/ X+ |6 _/ H( x

3 C, F  M+ }# [/ t& s% \  T; _3 J. w5 a1 S( k, l' \! I

4 V; @5 C- W/ x1 C7 e下面放出完整代码8 x1 y8 x& o& A& R3 D
, F2 d7 A7 _8 X' S2 ^6 X5 H5 w
  1. /*服务器*/
    9 r8 p. [7 o) {. h7 n3 y" _8 O
  2. #include <stdio.h>
    + P, X/ Q  N1 b, |
  3. #include <string.h>
    + O& L% i7 Q* j; Z% i4 u& Z7 C* U
  4. #include <stdlib.h>6 q4 g4 {6 U% U3 m
  5. #include <strings.h>$ ?. S( {) J) w# G- M  W' K
  6. #include <sys/types.h>8 U9 J9 Z- U6 E. p
  7. #include <sys/socket.h>
    ' U6 p$ c1 W" \: p
  8. #include <arpa/inet.h>/ n. F1 E$ ?# I1 N2 |4 a1 q
  9. #include <netinet/in.h>
      C# {3 n2 j- h+ W5 ?2 N0 r) |
  10. int main()- _! x8 |7 }. I  W- L* o, N- [
  11. {
    ! y4 k" `" ]& o# {: C1 V
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ! T2 r" \8 m, c0 F/ J" \; |
  13.         if (sockfd < 0)
    ; P* k9 T- g4 }$ f) N
  14.         {
    , L  c6 z6 a) I( q2 @5 D  _' e
  15.                 perror("socket");
    1 h" B4 V1 v; }+ q1 _3 v
  16.                 return -1;4 c$ r# Y6 x- x" a1 C: v
  17.         } //创建失败的错误处理( d# l  w  G: T- R6 b9 h2 J
  18.          printf("socket..............# b' \1 _: c/ j/ ?( J* S
  19. "); //成功则打印“socket。。。。”
    ' Z4 E! O9 c1 u4 I
  20.          2 t1 A: [) Q- d" t) {! k7 i
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    " y% `5 M& M; C7 k3 h' A
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)5 b# c/ g& v7 Z+ `/ |) s
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    # \. {8 O6 N# \# ]. O; q
  24.          myaddr.sin_port                 = htons(8888); //选择端口号. M  c7 n% h/ j1 @3 k8 d8 G
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ) E) H* c2 `% o' Q. e, _5 c
  26. . V7 Q2 x" H3 A  {6 A( L
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字' z+ [& w: V% K  h& {
  28.          {! w& P- K0 `+ F4 b# W- F* o
  29.                  perror("bind");1 ~$ @* p7 `* \
  30.                  return -1;* p. K' B6 A; t9 e- a7 X
  31.          }
    . v' C' U1 e$ X5 f: n2 B
  32.          printf("bind..........
    2 [7 C) o; T9 j
  33. ");# L  ^- F+ v/ ?/ e; v% A
  34. . S1 k7 [! m6 n1 w# m
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ' _6 l3 c# c- @# x
  36.          {  U3 h; _! ^  Q
  37.                  perror("listen");$ S9 U# a1 c1 c( `$ \
  38.                  return -1;$ G; t" [$ |* `( l* Q) d! d
  39.          }8 D" U" J2 h5 l, {- X! C! `
  40.          printf("listen............6 |) Y: S  v$ j& q) V# Y* Z1 v4 t. d
  41. ");% f5 K; H- C" M- o
  42.          
    ' m% z- l6 `; X5 D7 s5 l
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求3 ]. V' |! k# M8 U
  44.         if (connfd < 0)$ T5 P- @3 v0 r$ l" l1 c# k: Q
  45.         {! L1 s( k% \/ d: b( \: E/ C2 K" Q) d
  46.                 perror("accept");
    - F5 v3 R0 z" @, o6 u- I
  47.                 return -1;5 ~0 w, S) z- J+ z
  48.         }( C2 {( L9 S5 h9 Z/ e9 y
  49.         printf("accept..............
    7 @2 g! b4 @* O/ d5 f$ a
  50. ");
    ' ^' `& P) e" [+ q
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    ; b' L" F' P% h7 C: s
  52.         int ret;
      V; K, Q5 _4 F( F" p0 v  ~
  53.         while (1)
    6 B/ a% q% Q+ x/ K# @
  54.         {
    - e& W9 J" ]$ x+ G& {* ]
  55.                 memset(buf, 0, sizeof(buf));) E4 I  k2 R  p% q
  56.                 ret = read(connfd, buf, sizeof(buf));
    " D+ T& c( l* J/ Z+ j  n5 z* _
  57.                 if (0 > ret)
    8 X$ ]0 \6 E, x! J
  58.                 {! X% S! F9 V$ e! _
  59.                         perror("read");# m- j: ^6 Z8 a
  60.                         break;. D; W' q$ W, J2 t9 ^
  61.                 }//执行while循环读取数据,当3 K( a+ t: j3 w% @( ]8 h: x
  62.                 else if (0 == ret)7 v' {# `( \( N" J
  63.                 {
    : G2 k8 r: d" w, n# i4 U& ]$ y. D+ R
  64.                         printf("write close!
    & E" ]- @( ?4 A) G' F# o
  65. ");6 m; Q5 ^& e9 l" t" ~6 L$ F
  66.                         break;
    6 W& v, e1 q$ b# f: u
  67.                 }
    2 ], v+ M1 F" n9 N/ E% P* i9 V! Y
  68.                 printf("recv: ");) h& W9 }# L+ |: R% S4 v3 U
  69.                 fputs(buf, stdout);//打印接收到的数据
      n  }" T& ~0 t, l1 T$ j
  70.         }
    - I) G) k7 z" T7 Y7 [3 X. j% `
  71.         close(sockfd);//关闭套接字
    8 D: @6 q4 d+ v( c' W1 a
  72.         close(connfd);//断开连接% @( }7 w* c! E( f/ T4 L# {
  73.         return 0;
    8 x* O$ }' w! v: f2 `2 I6 {
  74. }
复制代码
8 {/ {9 Q, ]8 ]& I- [8 s
) b6 Z: {- y" y5 S+ R: L8 A2 D1 G! h
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    + `6 ^! k3 O! d& k$ G) t
  2. #include <stdio.h>
    / \+ W; `# u: {. W9 [( r( _: Y
  3. #include <string.h>, k+ A9 E: G: \& s0 d, d, C& F
  4. #include <stdlib.h>
    ( ^% m  r3 k( Y& T1 ~- ]
  5. #include <strings.h>/ U0 D6 W8 V1 W8 v6 b
  6. #include <sys/types.h>
    6 _& E" T; o) J& {! r/ U9 |1 x; b; o
  7. #include <sys/socket.h>9 J/ H# l" I6 Z7 {
  8. #include <netinet/in.h>
    2 n' R9 |( P1 Q4 h5 J0 n
  9. #include <arpa/inet.h>: N4 @! ^1 e$ X) C5 C+ N
  10. int main()1 q3 h* w9 H& z% z- E/ k9 O
  11. {' v) G0 I( u) A) Q
  12. int sockfd;( V% r# I8 o4 d" q, q
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))3 ~1 @. t+ @& Y0 J* N' E$ N
  14.         {% B' |8 j1 J9 r: d/ o
  15.                 perror("socket");/ d# ?  {4 O" m
  16.                 return -1;
    5 y; r' \6 s. a8 r$ w1 I; [4 y% X
  17.         }+ R1 {. K* V# G* O6 v# q3 r/ m: j0 \
  18.         printf("socket...........
    2 V: w" ?4 ?) W0 G9 Q
  19. ");- ?* H) ?# i7 K* S: l" l
  20.         , A# X* K" t6 y; `/ x
  21.         struct sockaddr_in srv_addr;- H& t9 T4 O1 n7 w2 O
  22.         memset(&srv_addr, 0, sizeof(srv_addr));3 M6 v3 T! A4 G- ]- r+ q- h
  23.         srv_addr.sin_family                 = AF_INET;8 c; @9 B$ i5 k
  24.         srv_addr.sin_port                         = htons(8888);2 Z2 @2 W+ ^2 V) O0 l, B
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    1 {, U) q: i7 [1 ?9 h
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    $ Q7 }+ j3 `" [& O3 Y
  27.         {
    : \, f; j$ S  U& A8 l
  28.                 perror("connect");
    6 b6 Y7 o$ Z0 O. R
  29.                 return -1; //exit //pthread_exit
    5 c3 q5 }7 a* g- r9 S, v! K/ {. e
  30.         }* N% m# V6 G+ a* y  L8 U& E
  31.         printf("connect..............- U) {2 ?  R# G# E
  32. ");
    , ~8 c) p0 f6 F! L, L  V
  33.         char buf[100];0 U6 f( G" R) @/ A2 u
  34.         int ret;2 a) A0 `* _' E6 P2 f
  35.         while (1)
    5 s) b5 {# g) g9 S: o8 Z
  36.         {5 Q+ P$ M. B% W" F' Q6 C+ l
  37.                 printf("send: ");' U3 Q1 j! K1 u9 C1 s
  38.                 fgets(buf, sizeof(buf), stdin);
    ) {9 j9 u  Q$ Q! f
  39.                 ret = write(sockfd, buf, sizeof(buf));/ X6 w9 {( o$ O
  40.                 if (ret < 0)+ g! Y$ m8 j  y3 C% v
  41.                 {
    $ x' C8 L, J- @+ f+ r7 n
  42.                         perror("write");
    8 E7 v$ ]0 _* b$ s- s4 d
  43.                         break;
    2 s7 i- t' w0 @; Q, b# c
  44.                 }
    ( Y5 n! v( o  ]+ F) b
  45.                 if (strncmp(buf, "quit", 4) == 0)
    : }8 e* g2 n# Q4 M2 s7 e
  46.                         break;
      R" Y. ?7 P) o5 W* t  L2 j
  47.         }
    - k* G) q$ I( R
  48.         close(sockfd);! w+ k- {: a& }% u: X! y; q
  49.         return 0;$ S& \/ ^' u( s6 Z  k
  50. }
复制代码

5 h& p6 ^9 F3 }! g2 e$ T2 |/ P5 Z* q$ ?/ K
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-4-30 16:08 , Processed in 0.059224 second(s), 23 queries .

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