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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。. G& X' ~, i9 ]

+ j9 H) v: Y& Y& D0 r
  ]- g$ f2 {- f( t- \: c5 B2 J
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 y  B' s  e# J7 @1 g9 R+ |  C) h- U$ j) ^; w

7 n8 J4 R3 E3 R+ gTCP协议0 q$ Y3 C! E- U+ w  F+ @! n/ i
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。" J7 Q6 Y0 ~6 i, x2 G' {' j
& |5 S; ]' r1 p; ?6 T7 ^* }

7 M9 j' n/ H' S# _关键词:三次握手,可靠,基于字节流。
3 S! e2 m4 C9 q  y* |! g: W" c- M3 x  t6 j" D: k7 A, m2 s7 v( F( h

! b6 @" E5 i9 d" f" P可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
* |* G  r# `" N% M" K
( |0 U$ T" b4 r+ }6 UTCP服务器端和客户端的运行流程6 i  g) c% ?/ D

. T) ?- t7 g: Y
. \8 E9 w1 R" `. u5 p# }
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
- A; x" {6 }! \( a$ Q$ L
* P7 T1 p: s; s4 q
; _1 [# O& Q. D
1.创建socket5 H# Z. Q0 u7 S# u9 F
socket是一个结构体,被创建在内核中
' j" m& N6 D$ R+ c; v0 Z: E sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
' E& S% ?/ K7 f: r. C6 j$ N8 K" S1 p4 B8 G, o6 }$ M
( [; \5 D$ d* F' ?& `
2.调用bind函数% ~. |4 [7 R+ g$ [% O4 ]
将socket和地址(包括ip、port)绑定。- y4 c6 _! c2 j& c
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
; N, W; [/ n* q- _! m+ o3 s8 `6 Z struct sockaddr_in myaddr; //地址结构体
9 c1 A7 P! `2 H( U0 W6 j bind函数  j( v( S  @4 n! @8 B$ s
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
8 K: G4 y9 z) u; B' K, H5 n$ |" }) g/ Y" {, P# p

4 q2 e# g3 ~% c& d0 Q2 |6 A( Y$ J3.listen监听,将接收到的客户端连接放入队列( ?; Q, ?4 \1 U& O$ M
listen(sockfd,8) //第二个参数是队列长度
- A/ G7 S1 ]; O: l6 b* Y
' p9 f( Z9 W' a- z6 t
: w/ ~4 |! Y& U' g1 F% D
4.调用accept函数,从队列获取请求,返回socket描 述符
8 S$ y/ v. ]! G  如果无请求,将会阻塞,直到获得连接, W2 p) c: h* }/ ]0 d- f) k/ J) s
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" O& e0 h& x! b9 [0 u% v: j6 F, s( N: o! d* q

1 K$ p+ t% E0 V1 j" Q5.调用read/write进行双向通信  h, d  O  ?) w/ z, Y
. I$ B" I) j6 ?: r4 X; _3 F$ S; K
* a  o0 r5 L# Q
6.关闭accept返回的socket( `0 i( F, {/ G0 m
  close(scokfd);
2 L5 g: q: X+ x8 h5 p2 W! ~: j( S5 x
1 @; Y. ~: O- F
: i$ c9 l. [$ [% k6 j' I: h1 p% X
6 p& K- S, W/ t7 O

  P% Y" D+ a7 T# S+ H! L' ^下面放出完整代码
. C, ^/ k$ ?3 L8 g  g9 v8 s7 S- o- G: y% `' X6 K  c
  1. /*服务器*/
    3 z/ k' ]/ N( a; }! e; E6 x
  2. #include <stdio.h>+ T9 {! c5 I5 T7 b7 }
  3. #include <string.h>
    7 Z2 p* o% ?" q
  4. #include <stdlib.h>
    9 e" C8 q+ j. c
  5. #include <strings.h>
    ) M8 o  g' y4 C6 m& }
  6. #include <sys/types.h>4 [! l6 ?! c) {
  7. #include <sys/socket.h>5 @8 K4 c# W0 R+ o4 ^
  8. #include <arpa/inet.h>
    - H# }8 `9 C/ R5 S; h, L# Q) ~1 ^
  9. #include <netinet/in.h>
    . O5 ?- I7 [; k& V7 L
  10. int main()
    . q0 w$ y+ ]4 V1 k
  11. {# ^' w* A3 ~/ ]6 M
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字3 q% p. c: W; D: x. s1 i6 d# Q
  13.         if (sockfd < 0)' O8 Y! p0 U# }" k$ ?, E
  14.         {0 z! h  b+ @' N  b5 _3 J+ T
  15.                 perror("socket");  h% U% v! h' h# G* b; @6 w
  16.                 return -1;, ^+ W( T$ D3 m! L6 k
  17.         } //创建失败的错误处理& T8 Q0 V6 ~$ c/ V1 y
  18.          printf("socket..............5 g$ X3 t' B( i1 p+ C& p3 U
  19. "); //成功则打印“socket。。。。”# i# }! ?% _5 I& D" N0 B
  20.          
    ; N. I2 n# X0 y' _% E4 |
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    % D9 i) i. ]0 Z! K
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    + ], ]- a/ n5 r& `
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ! c/ J0 Z! I3 c
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    - u! U* _/ k! e" [' v3 |9 `. Y* |. c
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    % J7 H6 C# q; L

  26. * f. d3 n; ?. F6 N1 d' z; X" h8 H5 L
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    9 W2 {, g# f# K2 g
  28.          {7 m- q3 p& W$ Y; U$ K: {4 _
  29.                  perror("bind");1 E* M8 O; ?2 ?, _& \. O% i
  30.                  return -1;$ P$ P. t' O9 H" @; g: }9 a# J
  31.          }
    1 ~( t" }0 v2 k( ?! J" b# j0 l
  32.          printf("bind..........
    1 {& F" N' V& G0 O% j0 _. H# A
  33. ");4 i7 N6 J1 x  E* p8 h
  34. 5 O! M8 V+ D2 t6 |
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听2 S- T; }- c! T
  36.          {
    ) O$ n# J6 C0 T
  37.                  perror("listen");, l# `5 s4 I2 n- ~0 D+ H( P8 M
  38.                  return -1;
      p3 t) Z8 |& v, m/ w/ `
  39.          }
    9 R, U' {$ c# V2 u* t4 R3 ^7 z/ K
  40.          printf("listen............2 S. z# V& z/ l
  41. ");0 Q5 D9 N) E" ~% W% n- e! C+ w( K
  42.          1 F; _5 M0 P2 d. \+ C' Y
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求5 ~" D% U; Z/ E
  44.         if (connfd < 0)
    . ~" l5 l  ?3 Y' H% L/ U! c2 z
  45.         {1 ~# s" S4 r, X6 c5 L( e
  46.                 perror("accept");
    5 U( @0 H6 y( M
  47.                 return -1;. ~' v1 d( s" k/ W) n
  48.         }
    . N8 P8 d# n0 \3 G0 K% A
  49.         printf("accept............... H( g; H8 Z: s  u9 i# f- Q
  50. ");# k  l& H. G! p; `, C6 M
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    , N8 t0 h& X  r0 J* P1 Z; C5 @) L
  52.         int ret;
    & A4 Z7 Z# h$ o8 i: J: K6 ]8 s
  53.         while (1)7 S* U+ W- L( ~; I" z9 f1 t0 @# b
  54.         {
    ' J: S! i& |5 V* a! ^6 J
  55.                 memset(buf, 0, sizeof(buf));
    $ l6 O* q2 ~' o( H0 e
  56.                 ret = read(connfd, buf, sizeof(buf));7 [% D3 T+ A$ Z$ l
  57.                 if (0 > ret)
    % ]7 H1 w5 ?  G* C
  58.                 {
    ' p8 {7 z! v- v! N. k: ?
  59.                         perror("read");
    , a  C" K; f& b$ u6 t
  60.                         break;) Q9 B6 D% z' K# L1 X* i- g' x* X9 v
  61.                 }//执行while循环读取数据,当7 L! G& j$ ]! G
  62.                 else if (0 == ret)
    & C' C" R8 p! s1 V
  63.                 {
    / S! @$ e0 E) J3 n. A% y9 V9 Y
  64.                         printf("write close!4 Z" M! y. m( l6 s9 P' O& {) H. K: I
  65. ");& d+ Q# T8 n' S4 \" G
  66.                         break;+ f: ]- c: ~0 c8 b
  67.                 }  R6 ~2 w* _6 D$ V" R/ j
  68.                 printf("recv: ");4 x$ k) L) `( F! v; u" s
  69.                 fputs(buf, stdout);//打印接收到的数据
    4 Y0 B: p+ M1 @5 Q- Y
  70.         }0 E9 d- ^1 a, q7 t* B. r# h
  71.         close(sockfd);//关闭套接字
    ! w$ j; U# C) M: B
  72.         close(connfd);//断开连接, {& C; i7 B5 Z' p8 P
  73.         return 0;# p$ F# `, _% N6 Z. l
  74. }
复制代码

8 x* @& b# U* ?) }2 s4 H/ l; p$ V, ~+ k3 T
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)6 [2 A, i" g/ U
  2. #include <stdio.h>
    . Q* L# h# F7 t, ^$ x
  3. #include <string.h>
    % d5 R" S, P2 a
  4. #include <stdlib.h>7 m3 C4 p) x0 o+ Y2 `2 o
  5. #include <strings.h>8 o. ~( ^3 }  f- j
  6. #include <sys/types.h>
    , l1 s  c# V6 i
  7. #include <sys/socket.h># X7 ~" o4 P, V1 c  @. L
  8. #include <netinet/in.h>3 V' W8 d( \* ~. p, W3 C
  9. #include <arpa/inet.h>
    / \% }, K( @8 U2 T  M
  10. int main()1 n& T. }9 r1 A5 g" q3 _; b
  11. {
    ' }. O& _5 Q+ w. b) O
  12. int sockfd;+ z' o: T' e3 ]  E, x1 r
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    , H% L2 S$ s  R( i
  14.         {
    1 A8 v6 s0 f; @  ?, R$ m, l
  15.                 perror("socket");
    + {% ]" ^5 R8 d3 `; K4 X* w# _) F) \
  16.                 return -1;6 Y( q; v! J/ [8 j/ R+ n) N5 S1 N
  17.         }
    6 l! u! J4 q/ R" q4 X
  18.         printf("socket...........
    9 ]7 z& ?. Z' Y" H( G
  19. ");) s, n  c$ ]3 C0 d8 d/ V
  20.         
    3 W% R7 D4 [+ @# {
  21.         struct sockaddr_in srv_addr;
    0 Y3 _9 r! J! J& O; n
  22.         memset(&srv_addr, 0, sizeof(srv_addr));3 }& H' W. H2 K( t8 l  t
  23.         srv_addr.sin_family                 = AF_INET;
    . ]- e3 J+ ~; Y  M
  24.         srv_addr.sin_port                         = htons(8888);
    2 Z  o' D" Y+ q2 X# A5 A
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");' B# X( Z2 D( v, Y5 n) x2 j4 |
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    * i+ b+ A6 _1 @) g! c3 \* @
  27.         {
    5 y: I- e4 ^4 R3 B
  28.                 perror("connect");5 h9 Z" v% A1 z# L2 b/ K
  29.                 return -1; //exit //pthread_exit7 W" T, n9 b3 Z  w! }$ Y! Y' X# o
  30.         }
    ' R  e: s1 N+ C8 g: f
  31.         printf("connect..............
    0 P& N8 B6 R+ }9 O4 E+ f: ?4 B4 Q
  32. ");
    1 d2 T3 g7 q7 ?
  33.         char buf[100];" E1 b2 r: a) Y
  34.         int ret;
    . @' p2 o7 X+ w
  35.         while (1)- K8 C6 I7 j* r$ o1 @5 E; C3 d
  36.         {
      j; u: m  {& I/ P& E+ d
  37.                 printf("send: ");
    ' I- A/ @7 R, E! p4 z2 f
  38.                 fgets(buf, sizeof(buf), stdin);$ a, O- I; [6 G/ C' w+ v- P
  39.                 ret = write(sockfd, buf, sizeof(buf));
    & T6 p0 U# Y' S0 P& v( J
  40.                 if (ret < 0)2 g. G1 g" E: r  x
  41.                 {, B2 I4 z1 U+ @3 O
  42.                         perror("write");
    , Y+ u2 C# E0 t4 J( O* l
  43.                         break;
    ! L* V+ [8 W# R* h9 X7 F" N
  44.                 }
    4 x! U2 t* V" ^5 l  e+ R
  45.                 if (strncmp(buf, "quit", 4) == 0)
    : ]; n: L6 `4 J7 b/ o. R5 J
  46.                         break;
    ! q4 I0 H. M4 F
  47.         }
    # |% x) w9 C# Q( g( F8 y* y
  48.         close(sockfd);
    8 V) Y& k5 F* g3 k) d( a" ~
  49.         return 0;
    ! W: a. Z5 u& j* D3 ~- [' r( |
  50. }
复制代码

$ _! g$ i  Y  P8 v2 ^! e$ @8 Z8 |3 y3 n
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-5-2 13:31 , Processed in 0.068599 second(s), 22 queries .

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