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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
" @8 e' m" \; n/ B$ {0 H; w/ ^: J7 o, m# p! W$ n4 q2 N
) r- ]6 E6 S% |0 Z; n, z. ~
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。4 T, d9 ?# n0 ]& P

/ R( ~0 G1 r. @3 K1 Q% v/ H
: d6 j/ }" @1 A% y! R: C# @
TCP协议9 Q4 {' I5 y, I1 h9 X  H
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。5 u' v" v3 b3 j! u" n

% Q1 M- _7 F! z6 [( w
5 z. v6 S. c( z
关键词:三次握手,可靠,基于字节流。
8 t! c; Z. A5 C6 E
, ?3 K0 V7 S7 _3 q. u/ _& i% T( c- x
) E8 Q6 ~* i" s( C7 A- T
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
! u4 Q' l6 ]3 e " g: ]- x7 y! p3 q3 @4 w$ b
TCP服务器端和客户端的运行流程
  ^8 v, q8 ?, y8 ]% `6 o9 H& E6 b- d/ Y' N! e$ v

, M, ?: t" _; I9 A# @2 p# m8 k如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?+ m5 z9 _+ Z- _+ f. J: A6 A. k' X
% ]; ~: o. d3 v) |
7 w4 L. n8 h3 M0 T+ Q9 b5 o
1.创建socket( J( J6 h+ F8 j2 r0 I. l
socket是一个结构体,被创建在内核中' k* i! P+ |9 S8 ~# W3 f3 L
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
) ?# b5 Z3 @- q2 m$ {' G+ a/ g$ r9 M

; ^# G: [: ~5 b) o! C. u1 l6 Q2.调用bind函数
  _4 s8 d4 ]  ]# q 将socket和地址(包括ip、port)绑定。; f0 P6 n7 M8 f# X
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序6 ^! e# y3 o( s  O; h
struct sockaddr_in myaddr; //地址结构体2 x& ^" ~( ^/ z' h! x- p$ J7 j, h8 N
bind函数" z; O0 Q* |1 {# {( L, N
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))6 D) m+ ^/ E5 x3 S5 c0 q

0 l2 n- @* R$ M  h% G) f4 \

+ q, L* L1 y. h: _' |+ Z4 F- M3.listen监听,将接收到的客户端连接放入队列. E9 B9 r0 g1 a4 s- H
listen(sockfd,8) //第二个参数是队列长度
- h, J6 m4 O8 B7 X# g8 g" h; c& L& e* k8 J% A

5 Q' B# c, e8 p5 Z4.调用accept函数,从队列获取请求,返回socket描 述符9 H' H0 d, x: \' V* j6 M
  如果无请求,将会阻塞,直到获得连接' B( p: _( ^& S5 C$ k* U! b4 E
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 L# d* K  P. W, K$ q
1 v2 _  H/ J" c/ h; g
( \/ x' J9 r! J9 n0 O
5.调用read/write进行双向通信+ `2 W( U5 [( B8 ~- W/ P

+ v% S  j! T0 \! a1 C
5 D2 z) I; L: L% D8 [
6.关闭accept返回的socket
  {8 [9 K( y  i  close(scokfd);
/ U- Z& P% Q9 T+ C
# ]9 }, [& F, }$ u3 M8 @, ]

5 E+ v2 D4 F" f: \2 ~, R. W9 W/ u- {- e

4 F" y: ]) u. I7 {- i' X; o下面放出完整代码& W; H( e* {. _1 v
% s1 w" K( q0 ~9 S
  1. /*服务器*/
    ! I0 J1 q+ @" H; }! V
  2. #include <stdio.h>8 |7 x7 X4 |, l* t
  3. #include <string.h>
      w. L- N* {! g. a
  4. #include <stdlib.h>. ?7 O8 L' C( z, `0 v
  5. #include <strings.h>6 E: w# }5 e7 M" Z( @$ U( G* q
  6. #include <sys/types.h>5 M9 b  l9 X5 m
  7. #include <sys/socket.h>
    - ~  q0 X2 s* y+ @% S1 K
  8. #include <arpa/inet.h>
    # S( m/ u! U- [4 @4 e2 a, t4 ]: `
  9. #include <netinet/in.h>
    - ^7 E8 R3 w, t$ |  K; M4 t
  10. int main()
      l" S6 R, ~8 R, x; c8 {  F
  11. {
    0 H/ J$ ]5 O6 ^
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字  |0 o* {' P. W0 `) V8 t
  13.         if (sockfd < 0)
    ' }# r( q. s  c$ y, ?
  14.         {
    5 i& q) J; r/ P" b: ]+ |
  15.                 perror("socket");
    2 ?* x% A7 b) N0 Y4 y! |! y! j; [
  16.                 return -1;; }% c( i& R2 t% z2 K. B- q1 c9 o9 g
  17.         } //创建失败的错误处理
    # g1 D0 W7 V" X4 J- I0 v
  18.          printf("socket..............& Y# M/ j: N# }4 E% i
  19. "); //成功则打印“socket。。。。”
    ' s; I2 \8 F2 r5 p
  20.          
    : j9 T4 i( M" U2 O0 }
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    : X3 i# f. w. w  V$ P1 l9 `8 }. q* L
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)5 M* f  z/ N4 Q9 ]9 \
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型1 E. Q1 Q8 @, O7 K
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    - U  n$ y6 b7 N# \$ ]. {
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ) ^/ V2 A) S6 `- y

  26. & Y9 N- j, Y4 a# f0 X6 S
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字+ k) C- \: P$ |9 l3 x
  28.          {
    " @9 x, g* z$ j; Z* d
  29.                  perror("bind");
    ' D: Q) I. X) M4 g  l# Y
  30.                  return -1;
    5 W% C" Z% J6 p/ d  y- O% i
  31.          }
    + A$ G; j4 j* M1 J5 C
  32.          printf("bind..........0 M+ b% \& }& A" `1 X0 ?& x
  33. ");6 g. |1 E3 J: G1 z) W1 v+ H, N
  34. + e  R9 c/ {/ E. T& n5 l# i# H0 p8 \
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听! b1 H. @! V- g6 X% @, z0 Z9 _
  36.          {
    4 q5 p2 d1 x7 g1 P) w
  37.                  perror("listen");
    ) J# n9 O  |/ W( c$ w' y3 \
  38.                  return -1;0 W: \& ~1 T' `3 J
  39.          }1 i& |* u! F% y5 o# G: m. [
  40.          printf("listen............5 @& K% ^+ C: V5 R! _& ~
  41. ");
    5 m/ {% J+ b- {8 b
  42.          
    . I( l/ i5 ^3 x) V- C$ H( y% x* F
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求. I8 M+ e0 r# A# o7 {* D
  44.         if (connfd < 0)- Q- B1 \7 F/ s* B
  45.         {: r8 p. t9 J7 x: `
  46.                 perror("accept");
    2 i( g. e' Y) ]  M, g. _% x
  47.                 return -1;- j: M0 _9 X  {2 z# a
  48.         }
    2 @1 w9 [  o) U8 S
  49.         printf("accept..............7 v, s( Y  a8 D4 G; s
  50. ");8 T6 ?% K8 m# t( o& s/ W* J: z+ _
  51.         char buf[100];//定义一个数组用来存储接收到的数据+ ~$ r6 X! f" ]/ t% X
  52.         int ret;
    4 p. [$ p# C% V
  53.         while (1)8 x% J( `% h" f, ^0 p
  54.         {
    5 V, u/ B4 V; [* Y, n0 _- v5 h
  55.                 memset(buf, 0, sizeof(buf));: D/ E3 f+ S# j' @" @
  56.                 ret = read(connfd, buf, sizeof(buf));
    8 f$ x4 o4 [2 p$ r5 b. y, O
  57.                 if (0 > ret)
    , |1 ?' u; u) K
  58.                 {. H: v% o. ]0 @( X9 `
  59.                         perror("read");
    , H- O# a2 [% J( k" \
  60.                         break;
    3 i$ K  p, t: L+ \3 g+ t
  61.                 }//执行while循环读取数据,当
    4 e- q' g; ]( Z' j1 \: r* `! G# n
  62.                 else if (0 == ret)- O4 `, n. k8 r2 |
  63.                 {- T9 r1 s3 D4 [  X) K* k5 R# a& o
  64.                         printf("write close!& v# T, b8 K$ e0 ]/ {& R4 z
  65. ");
    8 z) |4 ]2 A- J4 s
  66.                         break;
    ! R( x; u6 {/ Z2 H! W( ]; T% k
  67.                 }! W: @6 g# H2 E4 V( C5 l/ w
  68.                 printf("recv: ");
    3 Q, d9 a3 L% J- [* m6 t* V
  69.                 fputs(buf, stdout);//打印接收到的数据
    7 \  K2 Q0 w9 [
  70.         }
    / m% [1 v  s* d! k! v
  71.         close(sockfd);//关闭套接字- [2 k* e3 {1 A. t6 e
  72.         close(connfd);//断开连接0 V3 j, d( \; d
  73.         return 0;
    8 x# s' I/ K0 Z, T3 r7 i
  74. }
复制代码
! H3 G! t4 ^! B! N

, b1 F4 q7 P# k8 P
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    ; V" ~" L0 A; h6 Z
  2. #include <stdio.h>/ @* G- J  Z& `
  3. #include <string.h>. d7 T4 M. G9 N
  4. #include <stdlib.h>
    ! i1 F" p! w5 T. I5 M8 r4 M
  5. #include <strings.h>
    % z/ t7 K1 x% ?8 [4 X( A
  6. #include <sys/types.h>
    9 k0 G" \. p; v4 ?. n& E' @+ C
  7. #include <sys/socket.h>
    ' t  g# }  U2 |' y8 F3 P6 _; `
  8. #include <netinet/in.h>
    ; a( P+ i& J2 v
  9. #include <arpa/inet.h>6 L% I1 I2 [- _3 O: L/ `* ]
  10. int main()& d' m- F' d* ~* o5 q
  11. {
    ' ?0 h9 D. Y; a! h1 J
  12. int sockfd;# f1 M- J6 O. j! g; o3 @# e7 t! X
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))3 G3 f" y% I$ N% ^' U( D
  14.         {4 g! O4 y5 T) I/ r) U% c* W3 g
  15.                 perror("socket");% j8 g5 D0 s6 r+ x8 t( v5 h
  16.                 return -1;! Z* @5 S, i0 j5 k3 V( W. h5 b0 H
  17.         }7 `9 T2 I8 o7 m) Y( E1 ?0 R& x
  18.         printf("socket...........
    ) W" A( h' o- H( h3 c5 W! C2 Z8 a8 ~: l
  19. ");7 I3 T& l- D) [: r+ f0 w; k& q/ F0 F" @
  20.         
    3 H7 S( m* ]/ U! t4 P1 G, c
  21.         struct sockaddr_in srv_addr;4 Y" h) N1 s7 K
  22.         memset(&srv_addr, 0, sizeof(srv_addr));5 C. x0 k6 v' W0 q
  23.         srv_addr.sin_family                 = AF_INET;
    + M  \- U. M2 ]* z; c3 A4 _
  24.         srv_addr.sin_port                         = htons(8888);
      Q# ]: s( k  \+ K: i' j
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    % l3 ~" f6 j: g0 K
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))), X% i) \# m6 N! ~6 T5 z
  27.         {% K' ?' b4 v' o$ Q; ^# D
  28.                 perror("connect");
    7 e% `8 S  A# \% O
  29.                 return -1; //exit //pthread_exit
    ) p% q) J) I( Q4 [8 {
  30.         }4 Q4 P7 R! c  `9 L  b8 O& @7 v
  31.         printf("connect..............
    3 ^- o7 A4 N0 Z- N, N$ f0 T" `- w( Y
  32. ");
    # U* H! I0 E5 R5 v# O
  33.         char buf[100];
    6 q- Q+ `/ s5 J7 a9 l1 ]( ^
  34.         int ret;0 _( i! w  j% u5 T, k- b# p) L
  35.         while (1)2 L9 U2 I$ i7 o- _! M& N3 V
  36.         {2 A9 c5 V. q% V. T* b
  37.                 printf("send: ");3 h  }! O0 Q# j8 C" W4 _  Y
  38.                 fgets(buf, sizeof(buf), stdin);9 B! p- C6 {1 C+ R: ^* n
  39.                 ret = write(sockfd, buf, sizeof(buf));( a" _% ~( f' V+ {) h+ B7 x
  40.                 if (ret < 0)% g4 {. y  O# M. i
  41.                 {- D  S! ]+ X1 H/ m$ m9 h; h0 |' [
  42.                         perror("write");
    1 p% I+ _4 f/ \% m# h6 \5 U
  43.                         break;0 ]' u; }" i( ~& B7 V
  44.                 }: s+ o# W$ P% ?$ b% a& {7 F: x
  45.                 if (strncmp(buf, "quit", 4) == 0)
    2 D7 m, z' ~; o& l
  46.                         break;- U0 Z, [6 m0 C6 ?7 e
  47.         }
    " P# W. z# L! o, T" ~* ^9 ^
  48.         close(sockfd);
    # V& t0 W0 ~5 l# I2 U2 B" z$ x( O
  49.         return 0;( g" s' Y1 E4 O
  50. }
复制代码

( _) ^9 M+ |% n7 F5 T: [% e# r/ ?0 w
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-19 20:55 , Processed in 0.057137 second(s), 24 queries .

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