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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

, C+ p& S% [2 J$ {4 r0 M
; L; N9 i& p* @: Y' F7 ?  Q
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。# ^/ s" n1 z$ f" h) ?4 \7 |' K
  I5 I8 N* }5 U0 D3 J# d

1 }% _$ y3 g& y5 k# G1 K/ a+ ^TCP协议6 ]6 ]! `6 x& ?3 [
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- V6 p! p0 u: I6 B

: p. J  X6 M( y5 @! p- D
, U2 s3 O2 |2 m- X. v3 ~
关键词:三次握手,可靠,基于字节流。8 V1 `  g4 u& p! |
! A0 e. Z; S0 {( i4 C% I
4 C# H6 v$ x9 R5 `; {
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。# x: x) p+ p( n, z  z$ ^
& q* U% b. W; ?5 N$ ~
TCP服务器端和客户端的运行流程
9 O, m3 {5 K. f3 s* l6 E( g( _
" @" h: F1 `& j/ X% c
; ?0 R' P# O/ h$ W8 p/ _& _1 q4 [' N
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
, e4 R* E% B% d8 U5 U- Y0 l% j3 Z; O; U- K7 h' y6 t

5 v0 I+ Z5 I; I; @3 c0 Q$ s1.创建socket4 P5 A: D# ]5 a( L! w, |
socket是一个结构体,被创建在内核中/ R: C- o5 [1 u3 O8 b  f. O
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
6 j1 @- r2 {8 m
* d# z( g0 k+ x5 t2 F+ q
1 s& g% Y. p1 i; j
2.调用bind函数
/ y: L8 f( ?' a4 T, c7 q 将socket和地址(包括ip、port)绑定。
2 V9 C' Y1 k; b2 c: [: v- x+ F 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
3 w# @- R. V$ t9 {; t  V* k) [+ j, C struct sockaddr_in myaddr; //地址结构体3 u; B8 I# j8 \/ D. a% J
bind函数
% f* z+ J, n( J4 U1 Z bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))0 V+ I9 \7 z4 v  [9 C7 v# O
! A; @5 F: @! U) ?6 c6 r

7 l2 _( y0 J  m" `0 S3 ~' t& Q7 M3.listen监听,将接收到的客户端连接放入队列0 @- j% Z3 @4 d2 @2 ^" v
listen(sockfd,8) //第二个参数是队列长度
$ p) z, A: K: J$ R
: s4 t) `% O9 U7 |
( x5 F7 \1 `0 O! q& F8 y8 U
4.调用accept函数,从队列获取请求,返回socket描 述符
5 Y; b4 S6 y* ^2 u+ _6 P9 t  如果无请求,将会阻塞,直到获得连接
. ~# V9 P: S" ]" p  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 d4 z) t  L) f# f3 Y) f+ Q% F  u+ v  {% C
/ ~1 w6 O2 ]) p- w+ y, n
5.调用read/write进行双向通信- Y0 r  ?% U, x2 m  m

2 {( E% @$ r% l

- b7 I! {! e. E6.关闭accept返回的socket
9 F6 l2 {! `- K: K" Z  close(scokfd);' o0 q4 K/ Z6 f- t1 {$ r7 J! N4 k$ S
$ u! d! M8 O! i3 U# s
& t& A6 a6 M. M% B& O6 \$ r

* }/ {1 L0 J+ d4 a' n  z

( b7 {3 L2 \: y3 Y下面放出完整代码
, @$ C3 x; H* M/ k
; L# Y: h7 Q0 k- ~, y$ J) Y
  1. /*服务器*/' [3 U: Y, [* G' O, p1 P  U
  2. #include <stdio.h>4 I* A% L* Z8 i8 `/ q1 _
  3. #include <string.h>/ B: ^; o, Q7 Z( M
  4. #include <stdlib.h>
    * w- N7 R& Q8 N: d8 _1 T
  5. #include <strings.h>7 `7 w, p4 ^* H( }
  6. #include <sys/types.h>0 I) `6 `3 h* F$ r5 \6 n
  7. #include <sys/socket.h>0 D, h  q& d$ g% K7 i3 ^: P! v. B( D
  8. #include <arpa/inet.h>
    $ C8 B  y( z2 |  M) A! r5 e8 L
  9. #include <netinet/in.h>
    % R6 V1 C" C% b! Y/ m; b$ Z  I
  10. int main()
    . {% r; d0 k  W) j  i9 {) l
  11. {  a3 t% F, _, s# u% H( s
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字1 |3 A7 Q7 [) w- o
  13.         if (sockfd < 0)& u# A! w+ [  o% a% C! v1 Y" c
  14.         {+ g! {9 f+ W2 h4 X# y2 H* D
  15.                 perror("socket");
    : V( e  q" P+ z( S1 R
  16.                 return -1;, M# B/ }* T' K
  17.         } //创建失败的错误处理- E3 r+ ?: C/ @- p/ v; c
  18.          printf("socket..............$ X! P3 f- \) q- ?
  19. "); //成功则打印“socket。。。。”
    ; u  G$ {& G' H  j2 J- [2 V$ S; `
  20.          
    ' p! t5 h* i7 u$ ?* w+ `
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    / o9 a6 B% l9 N! f* g. r
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)5 Y9 g. d# t, n% i+ W6 d' J
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    4 y- k5 X& C' r) {  l& U
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    - y& `( B: w$ n: @$ w
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    $ {- G5 [7 c$ `- O, P

  26. ! `: Z8 U' k' Q  m4 l
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字" \" A$ `$ ~5 q1 O7 ]  D; v% D
  28.          {, q+ U: v2 `0 X, C% v6 t3 S
  29.                  perror("bind");2 @! Q% W. i* [- k- y0 E- H3 f
  30.                  return -1;# l( F, Z1 T* i/ R$ h
  31.          }
    * s+ B" P7 Q. x$ b) b- u/ r0 a! Q% [
  32.          printf("bind..........
    8 Q$ Y! y5 h/ O6 W- {, V3 d; c
  33. ");; c# ?! J) d, u6 t, R) d6 v

  34. + G% M; y; i. ~: i7 \8 T" }/ a" T
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ' |3 \7 S6 P) V1 ]  S
  36.          {
    3 V  S* o( `; O/ j" u, O" v
  37.                  perror("listen");
    * ^& k0 \( R4 w/ k. {3 M9 x+ f
  38.                  return -1;
    - E& K3 l6 \% [( Y1 O8 ?: @+ X
  39.          }
    * {' @* Q' Y# h7 i
  40.          printf("listen............
    , O2 n( W8 z5 q. a# `
  41. ");5 n  v# P0 }1 _# j) E3 S
  42.          
    5 f- P+ M3 X4 X, H
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求5 w5 Z9 `9 T7 E2 G
  44.         if (connfd < 0)
    , }7 v" \4 |* S7 \; M  o9 G# m% x2 m
  45.         {
    8 S6 Y& T1 r) r4 u- B' l) r' X
  46.                 perror("accept");5 v6 L, V. X5 u- z' ^- N7 m
  47.                 return -1;9 c- Z1 Z& }; Z* V- I
  48.         }
    + t* c/ k3 {7 {* s  _8 R6 u
  49.         printf("accept..............
    1 v1 P2 }, o9 ]0 I, O/ y+ D
  50. ");
    ' n( o* R: U1 n: v5 |
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    / D  W/ K& V; j" P1 a
  52.         int ret;" c4 O" a6 F6 l) p4 K5 \, c
  53.         while (1)
    & C: E) e0 l# |! C/ _
  54.         {
    # t. _6 J$ ^' Z# N% E4 z0 a2 G
  55.                 memset(buf, 0, sizeof(buf));, X% ?3 I% B0 A3 i: ]& D' ~
  56.                 ret = read(connfd, buf, sizeof(buf));! W0 I; @: d0 ~! ]$ e% a2 V
  57.                 if (0 > ret): t+ E* ~1 Z  `2 e6 H: \
  58.                 {! r( a) [! L# o* x4 D; l2 A
  59.                         perror("read");
    ! w0 D' B" X* l0 g7 t$ X
  60.                         break;, @2 F# P/ f$ e# z) S( x0 k
  61.                 }//执行while循环读取数据,当  Q( U) v: E/ i0 v2 E
  62.                 else if (0 == ret)
    9 U* l0 G& s* W2 p
  63.                 {
    & }; ^# o4 d8 c: M
  64.                         printf("write close!, d' G5 F: z5 c$ U
  65. ");
    8 r" q8 b, a  k9 @
  66.                         break;
    8 R8 B  y1 u! |8 k
  67.                 }8 n5 w2 g/ F* d5 O( w" s" Y" `: I. l( M
  68.                 printf("recv: ");
    1 U  n+ I9 {0 m6 \
  69.                 fputs(buf, stdout);//打印接收到的数据
    ; Y) L5 {; X) F
  70.         }
    0 i& Y. c+ @8 S  p& X2 J1 \. G$ [
  71.         close(sockfd);//关闭套接字
      x+ c& A: E& M& T$ W
  72.         close(connfd);//断开连接
    : ]7 W- ]7 K9 Y6 ^4 C
  73.         return 0;
    ) F9 S) F3 n- A
  74. }
复制代码
5 [6 ]. Y% x0 Y5 e
$ _+ N! r0 S( y
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    * w# g, k2 N0 x2 X
  2. #include <stdio.h>7 E6 F" G# V& n+ F8 H
  3. #include <string.h>4 T; M2 u/ c- y% F& h$ x& M! p
  4. #include <stdlib.h>
    ! M4 y& [/ ]4 m% }# p: }
  5. #include <strings.h>% n& t9 x' G2 b9 t1 i
  6. #include <sys/types.h>
    0 i0 L# L/ N5 ~0 c; R+ I! X2 c# e+ j3 o
  7. #include <sys/socket.h>/ b, m! F  @! g- J
  8. #include <netinet/in.h>
    # L8 w  E$ n% Z
  9. #include <arpa/inet.h>
    ; f3 W  L! N" j4 J& [
  10. int main()6 S; i5 G! ]1 P7 E! p, w" O
  11. {, T% l1 G3 [5 O8 X# O6 V& x
  12. int sockfd;
    ; H# u6 f& L1 [
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    - g1 [% V* t- E5 K) d2 h% f; @
  14.         {, U8 ], V- h" S5 G
  15.                 perror("socket");
    ( ~2 F7 o6 Z, p9 S1 `
  16.                 return -1;! S, V6 V( \8 r2 s; `5 k# x
  17.         }
    3 O7 d3 E9 b2 r+ t: B2 S# L
  18.         printf("socket...........
    ' [/ i% l" ~; F# \8 X5 y
  19. ");) \9 h, a* v; R% o8 s  f
  20.         1 L7 y! O' ^8 W& R: m
  21.         struct sockaddr_in srv_addr;
    3 h/ X/ R7 d  E  o/ Z
  22.         memset(&srv_addr, 0, sizeof(srv_addr));6 C& z) t# W7 V3 a! U
  23.         srv_addr.sin_family                 = AF_INET;
    ) }) u% L9 L* m! d8 L
  24.         srv_addr.sin_port                         = htons(8888);
    - _$ j# l( D" M0 R$ E
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    3 I: D6 _; f$ ]5 A- D
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
      F2 Y( L" w, M; K) O0 a
  27.         {, k3 ?% n/ k/ _& y; M4 \( G3 S* m
  28.                 perror("connect");& Q0 `% c. [4 N/ o2 d4 s' s- i
  29.                 return -1; //exit //pthread_exit& u8 L  T2 D% p
  30.         }
    , _  i2 `. E0 X. V7 [6 ]: R1 O/ A; _
  31.         printf("connect..............2 d  E- K4 W6 Q1 {2 R
  32. ");+ R+ _5 F+ }9 }+ O
  33.         char buf[100];
    9 C3 ]/ M, X3 Z: l% Z6 g/ D/ @6 y
  34.         int ret;( L7 F, R% E4 L0 \" ]) M7 R4 R- z
  35.         while (1)
    2 H$ `9 A( h) u* B/ j  R9 |+ q
  36.         {
    0 v- J! U, ?0 s/ J( @. P
  37.                 printf("send: ");
    ; M0 H( p  L- b
  38.                 fgets(buf, sizeof(buf), stdin);- Y3 [9 s% p0 \5 M: q
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ; R) i$ {5 U( {4 [
  40.                 if (ret < 0)
    / j& e2 h) u: |7 Z% v; q
  41.                 {: s- {- z' [) L! f- }9 t+ w$ h4 V- o. P
  42.                         perror("write");
    / U; s0 Y$ `% r7 d
  43.                         break;# n$ d2 y0 w2 J0 G4 |) n
  44.                 }  y* T1 k  w/ i8 v# W& L% U
  45.                 if (strncmp(buf, "quit", 4) == 0)3 v4 B1 y3 v9 x  w! J" F- R0 s
  46.                         break;7 ^9 @# z3 S5 L/ N/ Y" u
  47.         }
    . v- z, J0 {$ ], s3 W7 d. k
  48.         close(sockfd);2 Y4 M; B7 L; J6 ~- O
  49.         return 0;2 w7 O- ?2 {* o6 e+ j: b* A
  50. }
复制代码

/ q2 G9 O: v0 ]% U/ r4 P6 f! _- J3 s% p: g0 ]9 P7 f
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 03:18 , Processed in 0.103441 second(s), 22 queries .

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