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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。& p: o$ r# t6 `
+ m9 I0 d& R/ Q. e( Y- J" F4 i

% V- U3 F, Y  F0 w9 |' s- Lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 Z0 r/ u  }$ g; g+ n6 Z
$ @2 P$ ]! \$ V. H$ W# T
( z6 d) L& b; c" A4 R$ n
TCP协议
7 J! E) f+ ?) c) E( V5 wTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- E( ?0 d5 y7 C' r6 |6 M8 Q

7 h  Q) a1 f$ E% U, i4 U+ R

; R; c6 x5 j6 h5 Z4 @关键词:三次握手,可靠,基于字节流。- |. B. K3 |. C. a! A2 z

2 ^+ w1 g" }8 Q3 H

: J' _$ Q/ f$ S' q  C  O可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
$ j* G3 V! o6 w+ ], y( \6 Y! i 9 w8 n! t3 P, G5 ^) D/ f) A; o) H
TCP服务器端和客户端的运行流程
; c, x& b) C9 d; }) Q  I
" w. x  e6 x4 q9 ~9 _9 J# @
8 |( r8 E* W% B6 Q; S
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
; ]: }) g) y( G9 d$ t. w& ^6 Q
5 l; }  K% X9 N( R
+ t" {( }8 D! j
1.创建socket% {& T- L- x- E, K$ \+ x, `, ?
socket是一个结构体,被创建在内核中6 ^$ u9 l, O, l' @8 R$ W  B
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议9 x9 a3 \: x8 [

' ?( a$ y5 J7 J% m' O' W' N
# t5 |& ]8 G* S& E4 p" [# l4 Y' D
2.调用bind函数
/ A; ]1 t) }5 p/ L 将socket和地址(包括ip、port)绑定。
; r" @( L2 b4 M" o0 |  O5 z 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序: V. i6 p. G' ?1 u# D* G3 d
struct sockaddr_in myaddr; //地址结构体
5 H! j; g  I2 E. ~% q% V) e( q bind函数9 k5 t0 \4 z% b) x" o3 Q4 N* f
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* }. D2 r# ~1 I- H$ z

; M7 T# W8 u9 H2 r0 R. e

3 y- D  Z: W/ }' e& l/ F3.listen监听,将接收到的客户端连接放入队列
) H. L' C' ?( g! u8 `* @- r7 I2 k listen(sockfd,8) //第二个参数是队列长度
8 s/ \" g9 p8 i+ x% u. O% d% K! _: |, v( e; z. V7 ]
, O- `7 t; M7 g+ b# D, G
4.调用accept函数,从队列获取请求,返回socket描 述符
9 h1 ~& V. A4 H1 p3 a  如果无请求,将会阻塞,直到获得连接
8 t* e& i3 h7 d9 ]% V; f  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
4 s6 Q# e# X( M" n4 H( c; H
. F/ k1 v7 I( L- N" n0 K7 X
* T3 i7 C4 R+ ]9 G+ N; i1 Y
5.调用read/write进行双向通信
) S+ c" B# c; j, s* h! w* u) Z' f5 x6 S4 J6 g& h

- g2 }' e# M- h8 H1 N% t$ X: N6.关闭accept返回的socket4 ?* _& O+ ], c3 w
  close(scokfd);
: y% \$ i1 c+ @8 U: V1 e- g( q- Y( M' I6 e) Q: U: y6 t
3 ~! A, ~& l9 [7 @5 O) c
' h/ |4 T2 K- q. l+ f% \
5 ^( \0 W. i8 _& g5 ]
下面放出完整代码: P7 j/ T! c" @: h' H8 F! M
! ]3 l4 G8 V, ?
  1. /*服务器*/
    7 p: t8 o, I5 Z2 Z2 w: a3 n
  2. #include <stdio.h>
    6 i) B. F+ {$ p% e2 c
  3. #include <string.h>' E  y! h# ~* k
  4. #include <stdlib.h>
    / T9 N; g3 ~$ B) D1 P: _8 \
  5. #include <strings.h>
    8 x0 k, J: B( b0 _; l. _: t% K
  6. #include <sys/types.h>8 q. x1 K, c9 \$ L' G( O
  7. #include <sys/socket.h>6 t6 ?! l7 V- x, E* [# F/ u
  8. #include <arpa/inet.h>( Y6 i; M4 ^2 i* f1 s/ ]6 k
  9. #include <netinet/in.h>
    0 Z9 M2 A, n8 r& \9 w- Z! P9 y+ j8 @6 ^
  10. int main()% v* D; {% e; \" z; d
  11. {  ?) b! [- Y1 q' V, f. c
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    / I% V" j0 F7 e% ^
  13.         if (sockfd < 0)
    5 d5 K; i$ T7 n3 L. b. [( Z
  14.         {
    $ |0 w1 l, j0 h% W; H; y
  15.                 perror("socket");
    . J% m( o# j% z3 r: c1 ^. T
  16.                 return -1;
    6 s  s" t5 b" z1 l( g% |# X6 [) L# T
  17.         } //创建失败的错误处理
    $ ^; Y! r) |$ l: V  `
  18.          printf("socket..............
    # ]5 b2 s1 q1 U; ]7 F1 o
  19. "); //成功则打印“socket。。。。”
    # M5 w7 d! L1 T* g  t: u
  20.          0 h0 i% w: Z2 Y1 ?
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    8 u& ^  `" g' K" t9 U- A8 J
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)  X6 Q7 [8 G2 T9 v
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ( u% x% d9 g/ Y( O9 y
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    % T- C9 A2 d8 B8 d9 {0 B
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址6 Y/ e  S- e0 w3 |" j
  26. 7 _# M0 Z5 N/ }+ a' s* o
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    3 X  A3 f9 M, U# n5 ^' U: G
  28.          {6 b; g  W& |2 _- `
  29.                  perror("bind");
    8 @4 h8 d/ m5 J& k, h! W& ]
  30.                  return -1;, ^& `- N" ?7 A. Y# {( N7 k
  31.          }; S. M2 `3 A$ p
  32.          printf("bind..........9 Y6 _- ^) O3 P9 U  U( p% `
  33. ");
    " s2 H; [  X$ I
  34. ) H, a; p0 [; n7 |3 i" P
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    - Q# c& A1 r# v3 `: U
  36.          {7 d  ~- i* U( U
  37.                  perror("listen");
    8 J1 N6 Z4 h, l+ A
  38.                  return -1;
    4 b, A, M5 V  C' q" ?
  39.          }
    + Z# y( d! d3 n6 f* {- g* p
  40.          printf("listen............
    ( j9 [5 S  t9 t, _  J, q
  41. ");7 r/ @/ T& h6 ^- }! d
  42.          
    & ]1 O! b. y- i
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
      ]- z/ n* b. i2 a# i' f: B
  44.         if (connfd < 0)0 g9 a& e7 W9 p( H, D) J/ z: T
  45.         {
    / z; d  b1 U9 m9 b4 C
  46.                 perror("accept");6 Q2 k. ?0 _9 P/ ^) T! ~
  47.                 return -1;9 k# X- v- {4 `5 W
  48.         }) F6 a) `! H  |, R& v2 E
  49.         printf("accept..............7 [1 H  e# ?% l' z* m
  50. ");. F& a$ S' l! |8 c, }; R
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    ) x4 w$ D" S$ B
  52.         int ret;
    4 o9 ^8 z2 q3 M  V5 s
  53.         while (1)
    . K3 I0 N6 b' i( U4 z- ?' ^
  54.         {5 A9 _, e! U% K8 ^1 q
  55.                 memset(buf, 0, sizeof(buf));
    1 S# b  c* A2 x
  56.                 ret = read(connfd, buf, sizeof(buf));& z  w6 F& j8 L; \
  57.                 if (0 > ret)# h: X% ]8 {9 T* u
  58.                 {
    & |- W1 V& r# n, H
  59.                         perror("read");0 Q& h. I( l; _2 N( W7 K$ A
  60.                         break;/ T. C4 C6 X. M0 p9 e$ ~
  61.                 }//执行while循环读取数据,当
      y* o' k6 V/ u$ J
  62.                 else if (0 == ret)
    , m% c+ R2 C; Z: ^- x* [$ v" c4 t
  63.                 {! H$ ?. O# X- t/ Y' x
  64.                         printf("write close!
    1 b5 @( B, Q5 I- v1 p& H
  65. ");  r9 a7 R6 \% @
  66.                         break;6 F2 w3 A4 n! p
  67.                 }) \5 |) c9 V0 z9 v
  68.                 printf("recv: ");) X& O* n! I# B. J# {* C
  69.                 fputs(buf, stdout);//打印接收到的数据3 s; l- V! ~! \  _4 G4 I
  70.         }' j' k, ^8 ?( V; J
  71.         close(sockfd);//关闭套接字# R; X* q: e7 [! ~' P  {$ c
  72.         close(connfd);//断开连接: {$ t/ g4 R" P4 ?" i1 x& Y  s+ I; O
  73.         return 0;
    % {0 m% j1 [% c& H
  74. }
复制代码
4 _" f( }! c. c, i
6 H; w3 I6 G: Y
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    / B. ]8 Q7 }4 A4 ]
  2. #include <stdio.h>
    + G4 c4 p6 a7 E
  3. #include <string.h>
    ' p. }; J, j* ]; ]/ j+ H. N9 p
  4. #include <stdlib.h>* }7 h/ x- y+ W/ |; h
  5. #include <strings.h>
    9 T; M* I, N& T" ~, a+ \2 O, w
  6. #include <sys/types.h>
    9 F9 e) l1 u0 m" E, e1 i
  7. #include <sys/socket.h>
    & O6 B% @. I$ C; [! i
  8. #include <netinet/in.h>
    5 c% G0 l( y% l, i+ x, S: I- `
  9. #include <arpa/inet.h>
    4 L7 g2 O7 U* x/ b6 S+ |
  10. int main()
    0 c- f9 |6 B) H" p7 A
  11. {
    1 [7 I  q5 [0 n$ V' W  l* z
  12. int sockfd;6 B' s  `" L1 i; C
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
      t# ]: u0 i/ `# o: O
  14.         {$ m& g5 Z( k! p) y
  15.                 perror("socket");
    . x/ ~! u$ R1 M- n# D7 s
  16.                 return -1;
    " g$ i9 j8 E7 o8 M9 g
  17.         }
    5 V+ j) x! r, u1 f. i: n
  18.         printf("socket...........
    9 b! i9 B) U" m) N  L; o8 o
  19. ");
    + L) m* t' p% x) g# X! t
  20.         ' F- Z2 I/ O0 `* h
  21.         struct sockaddr_in srv_addr;
    0 B. T' R/ [3 _9 F4 ?
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    ! L2 M# Z; l' j2 i4 L9 v  A, N
  23.         srv_addr.sin_family                 = AF_INET;
    . H& F0 M4 R+ e1 [$ m
  24.         srv_addr.sin_port                         = htons(8888);+ p7 d. d  `% c1 r' U
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    2 u% \' n' f& p. @5 |& Q/ r0 A
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))( Q/ o8 B! B! h$ \8 W4 G# v
  27.         {
    . w9 C: V+ D- j8 z
  28.                 perror("connect");
    6 c# I6 U1 Q6 |7 h% v
  29.                 return -1; //exit //pthread_exit
    . D2 [0 Q: ?7 y0 e
  30.         }3 m' o% A6 B7 a6 L- g/ s
  31.         printf("connect..............( G% X& \# f3 @9 ^$ v' h2 L3 W
  32. ");
    2 y' e  {; ?2 J8 N8 s' O$ T% `
  33.         char buf[100];
    9 f8 [/ E! u* _$ y: |
  34.         int ret;
    * L  N- C0 o. s
  35.         while (1)
    * e& u0 Z+ h, E+ V. o9 X) }: p  |
  36.         {# z  }7 W) x1 B% k: }2 H
  37.                 printf("send: ");. k) W' w5 D9 p" p" _& e
  38.                 fgets(buf, sizeof(buf), stdin);
    , B$ g; k' j& I6 v  J( Z
  39.                 ret = write(sockfd, buf, sizeof(buf));
    % {. F3 s2 C1 p, k7 [6 }( N
  40.                 if (ret < 0)2 ~2 `5 Q: I! W  r4 n) t( H# P" n
  41.                 {( Y; J  e: O1 R8 A" B
  42.                         perror("write");, _5 p; w& J: _7 {- e( T. q2 l7 f
  43.                         break;. k) s) x, P9 V$ u4 X" P" z
  44.                 }
    / X2 O  v! o( X+ L# C
  45.                 if (strncmp(buf, "quit", 4) == 0)
    0 \3 X" c0 u' r
  46.                         break;
    6 u* Y( V& c6 u" k/ h+ }
  47.         }
    4 S# t! O) R+ s& k0 b+ u. T1 F
  48.         close(sockfd);; J0 I/ K5 c3 u; Y4 e! ~
  49.         return 0;" G1 d0 z0 C: u8 v
  50. }
复制代码

" k' {8 P0 Q  a' M
3 n  M- {0 r4 b1 ]+ H: r1 p
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-5-2 13:35 , Processed in 0.072913 second(s), 23 queries .

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