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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

, _; f0 ?" r) X* Esocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
( `' U; |3 N( t: n5 c; Z' e3 g7 J( U( `* ^4 m

% X" i6 ~) ?  |* g% ?TCP协议7 r+ @; p) f+ n& z1 \: p9 S
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。& u( {  F* p' {: g" c! F8 h
: F2 d, r0 w5 T* s$ G# K2 k

( m0 q2 Z: F' X( {$ A: H! j关键词:三次握手,可靠,基于字节流。
% y& E0 B; P$ w% t8 y& B3 M( |
! Q) ~( `' \7 ^" _' V
) z5 f9 P) E) m/ F6 {/ t) m
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
& j, a( F; P3 n" D' |( A
. L* m4 p7 ~- m. xTCP服务器端和客户端的运行流程
2 N# x; @; ~! x2 C0 b( ]% X& W: B* w; b! o4 H
5 s. Y% P* s: _
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?5 w- B! I# R8 t; i8 _

5 }( L- {* T, \+ x1 K8 n# ^  P6 O# J8 t
9 Q8 h7 x% s4 d$ d( M" b
1.创建socket' f% }( a2 G8 d. |3 w0 _: E
socket是一个结构体,被创建在内核中
# ^+ I5 O# [, G$ F) b sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议+ D. i: S2 Z; m2 s

! q* @2 O4 s# P3 S4 W

0 b  N) Q2 h4 S0 S, O( |2.调用bind函数
  k: i; M* `  x5 _ 将socket和地址(包括ip、port)绑定。
  J, n. B& i. j. v3 Y: |4 P 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
3 g/ p$ d, h. R6 T5 o0 R1 @ struct sockaddr_in myaddr; //地址结构体
' j% p+ g! p% z  D bind函数/ V" Q1 X5 H" s( z/ v
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))) f/ T# w/ `# J& F

  }2 e$ N  {8 ?5 K4 X+ G
, ^. I  {) B& D4 w' l# n
3.listen监听,将接收到的客户端连接放入队列: q' |& @& s! n
listen(sockfd,8) //第二个参数是队列长度! N! A* H- y9 k* J  {1 h

7 [2 |, J. y/ D/ p. A! s

- z% s/ H1 ^8 u$ y; F! D4 Z4.调用accept函数,从队列获取请求,返回socket描 述符/ L6 ?5 j: Z/ S+ X( M
  如果无请求,将会阻塞,直到获得连接
* w# k1 i; P' x  g- v  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
! f0 V5 U5 ?* F$ d0 t$ F6 D" b. S8 d: h  E! a# C
, v* f! _% E8 M7 G- `2 u
5.调用read/write进行双向通信5 s7 x1 P4 ^# d/ ]' n6 d  N% R

* _7 w& F9 I& S% _2 g+ c
7 |3 J# K. Q' C
6.关闭accept返回的socket
. a0 b. g6 x6 d6 w  close(scokfd);
! E; b' E7 m( \  |. C$ `5 U
: ^" W5 Q5 t3 m  X- Z8 ~
% s! Y6 q5 l5 o, ?: W( H) y

# M4 ~, r6 E+ X+ L" x& X! z3 M, X
3 c$ l- R9 u( O6 @
下面放出完整代码
) L7 B  N1 S' R, {( V8 n! }* s: Y% o4 P$ E7 R
  1. /*服务器*/4 v( {3 z+ E% o. F" b% G- ]
  2. #include <stdio.h>6 L& r2 E/ A: A* b  B0 P/ N
  3. #include <string.h>
    % a- ~' G  h) i' J  n. Y
  4. #include <stdlib.h>2 k! ]$ {$ u$ Y
  5. #include <strings.h>( Z# u' K" l8 l. n
  6. #include <sys/types.h>; P, }# J$ N4 j9 H3 Y2 @7 }
  7. #include <sys/socket.h>! M6 g8 n: }6 n- T) B$ T
  8. #include <arpa/inet.h>; R  m5 Y! Q, x
  9. #include <netinet/in.h>6 e/ {& `7 @& X( r: T2 Y
  10. int main()7 f" {7 k8 W4 W5 N
  11. {
    % x: ]7 G9 b* k# g/ R/ h
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    & _9 K; }5 k7 D1 N3 p# N- V
  13.         if (sockfd < 0)
    6 e8 V2 E  S" _: c! H# @
  14.         {0 M4 a* ~0 _' y( b3 d0 M
  15.                 perror("socket");) f6 d9 M' K" n6 M1 t+ V8 F* s
  16.                 return -1;% ?' ]' Z: ?. l: [. S$ ]& {2 U
  17.         } //创建失败的错误处理% U8 I: Z& a7 v$ W
  18.          printf("socket..............
    $ ^6 y& z$ K) H  {7 j" S
  19. "); //成功则打印“socket。。。。”; {9 c3 P& Y* o2 U" _$ F3 Y' c
  20.          4 Q& A: F0 R  C$ s
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体+ A5 g2 t0 J. {
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)3 b! q9 o$ ~1 \' \8 R4 ]# J
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型. b4 d) ~7 m8 Q* ^  V' c2 E
  24.          myaddr.sin_port                 = htons(8888); //选择端口号/ \6 M$ O$ M1 G1 w
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    / W  y- s- _0 z. `; d+ b
  26. - m1 N9 }3 k+ j
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    8 t4 j, Z% n& B9 a
  28.          {6 X4 x& x% R, U3 [" Q( L8 z7 n
  29.                  perror("bind");
    " c. O  u0 y9 M
  30.                  return -1;
    5 T/ ], _% B4 {! \& @2 H: K! C
  31.          }3 F, K+ e3 \( P6 x. n
  32.          printf("bind..........
    ; F2 ?9 p1 \, f
  33. ");* o5 p+ J  Y2 r0 A0 `$ O2 }6 c) f

  34. ; a/ q4 a) e- Z' F
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听+ B- I7 R0 f/ r3 l9 X0 S: z
  36.          {
    * S3 L5 N/ w3 B7 y6 J* V( b% [
  37.                  perror("listen");
    ; ]9 O; x1 L5 H; K( l8 H
  38.                  return -1;
    ) L: V, s9 i$ J7 J
  39.          }
    ) s+ }- X% J0 c$ S9 D. O! b
  40.          printf("listen............* J& y9 S4 i; p2 c/ o; Q$ i/ g% `
  41. ");: y0 v, A( J) {' W1 ?7 T
  42.          
    & v0 s- |1 @9 U& z# s+ k- b- d0 R' _
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    5 P0 X" f' m0 a& U1 U2 ~
  44.         if (connfd < 0)
    ! D9 Z4 f8 F# z) N. l$ q  \6 }3 T
  45.         {$ R# \1 a6 Z9 ]; W6 c1 U
  46.                 perror("accept");
    + S5 j) k! B9 h2 G: K
  47.                 return -1;5 V; B; @: s3 r  R( q; ~% w! i
  48.         }
    - W* {5 W: w( \2 [. R+ v
  49.         printf("accept..............  p) B# L, m5 K2 l7 j7 `
  50. ");' k' K0 x: M, \& c% K+ S4 B. f
  51.         char buf[100];//定义一个数组用来存储接收到的数据1 U, H/ |. p, f- {
  52.         int ret;
    % w! b* G6 `" b1 {
  53.         while (1)7 L2 X+ Q. d6 S+ k8 A- S3 b
  54.         {; t1 \. l  W% L" z/ R3 j8 v7 s
  55.                 memset(buf, 0, sizeof(buf));0 ]& u6 I/ X8 B( J5 ]" I6 u
  56.                 ret = read(connfd, buf, sizeof(buf));
    ; ]4 f& @5 R7 g' a+ q. Z' x
  57.                 if (0 > ret)* B2 T" @& S0 a5 D  A
  58.                 {
    ' _+ D6 i7 e$ ?$ X- x: V
  59.                         perror("read");2 J% [9 k) j  y9 `' e6 H/ z
  60.                         break;
    $ g  ~. ]" Y4 i; p9 `/ r6 ^; X
  61.                 }//执行while循环读取数据,当
    , ^: S7 [( B2 M6 ?
  62.                 else if (0 == ret)1 v$ x$ k1 P/ @8 U9 }; a
  63.                 {
    # }' J# R3 T2 t4 q
  64.                         printf("write close!3 ^) p; h! b4 m0 ^9 d  n! i3 A
  65. ");
    ! j% ]- S7 v% R8 r6 i8 N
  66.                         break;
    : [& z; c* A1 m
  67.                 }
    3 p6 e, `! P  e* b# O- R
  68.                 printf("recv: ");
    - l, Y" C+ j  Z, ]! s
  69.                 fputs(buf, stdout);//打印接收到的数据" s! }1 `6 Q+ R/ F* O
  70.         }
    : H( l+ c  @1 r  i8 \0 [+ C
  71.         close(sockfd);//关闭套接字4 P. ~; w9 p. ]0 E6 f
  72.         close(connfd);//断开连接
    8 \+ `) c: ^& w* F" A
  73.         return 0;
    - w6 }- p1 _; D5 C/ ^& @7 N9 {
  74. }
复制代码
/ _' D* ^3 y9 z1 g
  y* Q" H$ @# T$ s4 s
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)# p+ ^* p5 P5 u
  2. #include <stdio.h>
    8 o  B8 n- h- s( `% \8 ?1 S
  3. #include <string.h>
    # `: y1 X5 v& j& ]7 [
  4. #include <stdlib.h>9 K' Y4 M2 o. i1 J; D
  5. #include <strings.h>
    5 i# I6 s) f; }" x3 ?
  6. #include <sys/types.h>7 }+ i5 l3 ?8 b
  7. #include <sys/socket.h>
      p! \1 c5 o- k1 ]
  8. #include <netinet/in.h>
    : M3 D  M# V3 P: `4 D8 j
  9. #include <arpa/inet.h>
    5 T2 B9 n, l9 L5 I( F
  10. int main()
    / K/ |4 B9 v. |+ U& I! r
  11. {: b8 a/ o% a; T- m, t# `
  12. int sockfd;0 j! \0 Z1 Q* ]( M) T
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
      ?  f" Y# B2 u' J& [
  14.         {. P; S! d! F6 Z* _
  15.                 perror("socket");0 q2 V7 ?/ L$ a  G) E; l3 F2 u* k
  16.                 return -1;
    ) s% P- d; O. o7 q8 L/ y; t2 t3 L: o
  17.         }
    - E- l0 S7 `8 b1 M7 Z, c3 C
  18.         printf("socket...........& A/ t  y' v1 q7 s
  19. ");! ]# F) w2 a+ Y8 \/ m4 [
  20.         ( R; t  l: G* C6 \8 P# D
  21.         struct sockaddr_in srv_addr;. M( o3 {$ B7 v2 r& E7 }" o
  22.         memset(&srv_addr, 0, sizeof(srv_addr));0 \3 ^8 Q2 P0 x# b8 @/ x4 _
  23.         srv_addr.sin_family                 = AF_INET;. _, z; ]7 n2 T$ d3 R0 w, h5 J* Z
  24.         srv_addr.sin_port                         = htons(8888);3 r5 p4 T. x: p
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    0 x1 j) M9 M( Y; W
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    : L- y. j5 I/ H/ d4 F
  27.         {4 Z# ?+ g2 q; ?: O; L2 b: C
  28.                 perror("connect");8 p2 a$ L  r3 ?  F
  29.                 return -1; //exit //pthread_exit
    $ V+ [9 S) G+ C8 j- F# x6 C
  30.         }" f" L. D% I1 N7 G* x# ?* j
  31.         printf("connect..............5 k- V; }" s: i
  32. ");
    % P8 T1 Q8 z# O0 I8 s$ O) {
  33.         char buf[100];3 x# S1 y5 `- o9 v7 B+ o4 ~* d
  34.         int ret;. X9 k6 e* d+ b. F8 T$ K
  35.         while (1)- |- Q4 `$ ]. V6 Q, K
  36.         {; D7 [3 ]. k+ V; |4 n8 f2 {
  37.                 printf("send: ");
    # D: V* d- s. N$ D. l
  38.                 fgets(buf, sizeof(buf), stdin);
    % J$ `  r' w; t0 x% D3 @
  39.                 ret = write(sockfd, buf, sizeof(buf));* x2 W4 B+ {  y" W7 V5 S
  40.                 if (ret < 0)
      o  U. {& X$ a  S4 k
  41.                 {. q* t% p& ]6 A2 b7 Q
  42.                         perror("write");
    ' U, w" P+ a+ Z7 J4 u+ o% ]2 x" R
  43.                         break;% N4 G- V, V/ a! N( O
  44.                 }- W$ {5 b8 L# q5 g  n
  45.                 if (strncmp(buf, "quit", 4) == 0)1 m5 O6 `& D# B" G  S
  46.                         break;
    - Z% _& k/ z7 D  t4 t
  47.         }
    * m" l* L, h' e9 n1 L! v
  48.         close(sockfd);
    ; t5 e! [( D% `# r
  49.         return 0;, ~# r$ M, U! q7 v  ~5 h# `9 I, R
  50. }
复制代码
# k. Y8 f% e  V: r2 ^
; x1 F( m: u  m' m8 H( M
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 13:20 , Processed in 0.066792 second(s), 23 queries .

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