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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

" K3 F( z0 I- Q1 i: Fsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
' S+ s+ C/ K* G; B- @$ b" i. @; N7 P0 f, F! |4 _) k

4 Z4 y* H0 x/ j; PTCP协议, u/ o: H5 s( `
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
: b! G& x, b+ a8 N" u+ R' v
& L$ z( W2 v& y, y
" s; Q3 `3 w7 w7 D2 o
关键词:三次握手,可靠,基于字节流。
7 e, c: d- Y% G6 D( E- I* N$ M) G- `
5 H; J( G3 k9 w. c$ ~9 b2 L
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。+ [; Q8 d. R$ g/ [! @

: s/ G4 N" N' w) Q0 ?  D9 Y; XTCP服务器端和客户端的运行流程
! k6 \# p" i; J* `4 D( @; D+ v
7 t# H+ K! h8 f$ Q* q/ A5 w

; t4 l0 T! O) G4 ^: ^% q1 @如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?4 _3 v7 o% _1 O
0 N& d, r8 q6 I
% k4 Y5 M7 u" i' k) G9 d
1.创建socket4 d& t& q1 ]" A; A
socket是一个结构体,被创建在内核中; R% ?2 H& t/ ?9 I9 n% ]
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议" w- ?- ?$ g) S! K; r0 q

# m& ^4 }: X4 j: E: @4 p6 q4 \

! x3 }* [- c, A4 ?2.调用bind函数
: n5 j' T# l4 M& n* E 将socket和地址(包括ip、port)绑定。
* q5 a) z7 s3 S6 _ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
) f( r$ y3 ~6 G$ r* H0 T: c struct sockaddr_in myaddr; //地址结构体
! l- m1 f& p( }4 n2 j5 ^$ E bind函数
$ I) C7 a! i3 p5 i  W5 ] bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))  @% D6 B" p2 a% h( U' s2 _
0 @- |4 l, B$ W/ n# \

- X' \' V% N" s" z3.listen监听,将接收到的客户端连接放入队列
: {) G& G/ d: z- N+ ]% l5 E listen(sockfd,8) //第二个参数是队列长度
5 D0 N% K+ s. P9 }
  J. {& q' m! u, N
$ {& f6 H# D" A! N2 D  G
4.调用accept函数,从队列获取请求,返回socket描 述符
5 {' q0 y: R* J* {6 |( _  如果无请求,将会阻塞,直到获得连接
# |8 o- t8 I8 C/ v( O  d  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
* S. k6 y6 f9 y. k) U& }2 h7 C! C" R) |" n6 i- p; v: M, }4 V- W' X, d3 L1 A
" [3 h; ^2 z8 @: L1 G* D) R
5.调用read/write进行双向通信
/ t- b( q# B1 C- Z
9 R0 I' m) F/ ~+ ?

* l, \* L, w$ a% V. o6.关闭accept返回的socket
. m2 V3 |) f3 V" N' g. R4 X8 m  close(scokfd);
9 L9 K) |$ {- I- Y8 s4 m* w3 w+ @: A6 F  u, ~$ X
8 K2 a! E0 n7 x, S7 _, b

( |. h1 p) f" Z/ [9 H

- g0 l$ l! W! Z; ^1 C  f* h6 v下面放出完整代码
2 h/ v) R% J) `6 R6 a
4 ?* B2 Q  k% k* ]( I% m& y
  1. /*服务器*/: v, x5 q/ D" V( N' N
  2. #include <stdio.h>5 A* e4 T/ {3 y1 Y( f
  3. #include <string.h>
    ; k1 u; y" ^  i" p! Q+ s
  4. #include <stdlib.h>9 u/ c9 Z( n# g% `
  5. #include <strings.h>/ x5 T4 B, i- w. u2 }
  6. #include <sys/types.h>
    * q" V/ S  k+ S0 b
  7. #include <sys/socket.h>+ J3 Y9 L' w+ E6 g
  8. #include <arpa/inet.h>& V- _: e6 H& c; _+ i
  9. #include <netinet/in.h>: q  ^6 K) l& ]( k
  10. int main()
    - r1 Q$ w1 @: S, `3 D9 d- `
  11. {/ q  T/ l4 X" v$ L7 {" X& s6 H
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字3 G* ^7 R. Z. d; o2 f
  13.         if (sockfd < 0)
    4 o* K+ j4 h9 v
  14.         {
    . o# ?) @2 j# e7 l! H/ k
  15.                 perror("socket");# |/ k/ R- m$ H  F" s9 p, A
  16.                 return -1;
    8 E2 g0 W% v& h2 `- A' a. T4 x
  17.         } //创建失败的错误处理$ b9 ^. [9 `( D
  18.          printf("socket..............
    8 b7 H2 Y  V% V& y& t) ?6 y
  19. "); //成功则打印“socket。。。。”
    & C, H; d( i( }; g4 A* D
  20.          1 @, q; [3 J( e! Q2 B2 p, L% t
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    3 a" j: M( a) D! N: \0 p) h
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    - g; D& o  ]% {! ~
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型/ A( H1 Y* L. k4 h0 ]
  24.          myaddr.sin_port                 = htons(8888); //选择端口号& l2 l- J' ~; X8 ^* f. J4 ?
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    5 W7 d; U/ i. a" u" k! }& h
  26. ! z# D/ |; w2 E3 o1 @% Q
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字- `- _  _" [. Y: c& n" K
  28.          {
    % M8 H, V. ?, w! C
  29.                  perror("bind");- \5 Q2 O# ~" A6 ~$ N+ t
  30.                  return -1;
    4 B" N. j9 a7 N, M  r
  31.          }
    7 j7 {$ F$ d# c9 l6 [9 {! M
  32.          printf("bind..........
    ( ^3 a; O$ U0 {1 K& U: c4 ]
  33. ");
    " Y; g/ ]( H$ s$ w

  34. ( L, |5 Y6 N2 I& t2 L( [& M
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听( p9 c, c3 r1 ?* t9 {6 R7 R9 _7 f
  36.          {
    - w7 F8 G0 m* i" i) `4 m0 A: [, K8 }
  37.                  perror("listen");
    / M2 V& n! G; c
  38.                  return -1;
    # y- _& I0 Y2 B& A' p
  39.          }) W. x" m/ c3 D: ?: G: B. w1 }9 @
  40.          printf("listen............
    9 v( e) m1 V0 n. H
  41. ");& T& J  [6 u1 p# E+ P' j: D
  42.          : r& G( f2 j9 G4 \  u) G" A
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求, f1 r5 ?, X1 n" Q" j! F
  44.         if (connfd < 0), t6 K1 ~- o! S4 _2 R  ]
  45.         {2 A+ C/ O& U: S6 ?
  46.                 perror("accept");! |2 X3 K' B% x+ w( `
  47.                 return -1;" f+ l# r- s8 Q% L% _
  48.         }& [: B5 d# x1 N: _8 M8 h+ s
  49.         printf("accept..............
    ! [  Y& F% v5 W* j- T- H! e
  50. ");
    5 f, ?( _9 [) _# B/ A# @
  51.         char buf[100];//定义一个数组用来存储接收到的数据+ V/ b+ f. W2 i! _' o
  52.         int ret;
    & m: }. [! o7 i  D4 E) l& v
  53.         while (1)3 ^! t3 l* r3 ~' m! _" q2 f; |
  54.         {
    ! _! o! X* P: S: b4 S
  55.                 memset(buf, 0, sizeof(buf));& `6 m- i7 S# h1 A/ `5 k( g1 v1 Z
  56.                 ret = read(connfd, buf, sizeof(buf));
    ; c$ O9 V" b! j0 q4 e" @
  57.                 if (0 > ret), c' ^+ [4 G2 x  ^4 f/ w& a
  58.                 {
    8 I4 M/ {" D. Y& u% @) w. U  G! q6 O
  59.                         perror("read");
    7 t! g" G: l2 I: q
  60.                         break;1 L9 M3 I3 }' c" L) W
  61.                 }//执行while循环读取数据,当" z* l2 S' K6 g7 p" l# Y3 d
  62.                 else if (0 == ret)
    9 U$ E+ d6 G0 V7 O) @3 J
  63.                 {5 \$ w5 u/ i* i2 |) o, c( s
  64.                         printf("write close!
    6 R1 e- f7 T, O6 ~1 P! [
  65. ");) B1 p3 m8 F, @7 H# T
  66.                         break;
    / ~; [# ?7 ]! s' M6 k
  67.                 }
    5 D! J2 H8 S) `
  68.                 printf("recv: ");. W( W% ?( {, q# Q; f! E( a: F
  69.                 fputs(buf, stdout);//打印接收到的数据8 o: T: e/ s3 z
  70.         }6 {6 @" o. }) v2 N1 P4 \9 w
  71.         close(sockfd);//关闭套接字# b2 v2 v' [8 L! _& A* s2 x' d, F1 X* s9 ^
  72.         close(connfd);//断开连接
    " h+ i6 ^% N, c' k1 ]  g! j- {
  73.         return 0;
    / V* R) x6 x& [
  74. }
复制代码
8 Z! U, m! T' V/ ?1 {# U+ L- M

6 j8 B( I: j- p. J
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    " d0 f" W% {' b# s
  2. #include <stdio.h>
    ( K% b! _4 }/ H4 t) Q
  3. #include <string.h>( I" d! m) P" ^' ]8 m: d! w4 M1 ?
  4. #include <stdlib.h>. N: g7 q4 S7 t6 L7 }( y
  5. #include <strings.h>
    & [( H( D+ ]5 e2 ~" |. M
  6. #include <sys/types.h>
    ( N& ^9 h: a: y  I, h+ L
  7. #include <sys/socket.h>6 {; c# C+ `# p0 c# {9 i
  8. #include <netinet/in.h>
    * U5 A* R3 B7 q' Z1 f7 e
  9. #include <arpa/inet.h>) U6 J9 ]& |% w6 E9 C- l; g' r
  10. int main()7 t4 e1 N0 E: _% K# s% m3 a
  11. {
    & O) z; P2 |2 J) W- u& J
  12. int sockfd;
    3 l% X. w; W3 d
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))' Q1 J8 }. F. o, b
  14.         {
    4 h% k% T2 y0 J6 @2 q  j0 X! l+ J2 u
  15.                 perror("socket");. Z! C+ K8 g7 ]6 U
  16.                 return -1;- o  a. C4 I+ F2 }  }/ }9 g
  17.         }& L. q, {8 o$ r+ P, b' e8 n4 R4 ?
  18.         printf("socket...........
    ( y$ m' W$ ~2 Q5 X: P3 S5 z" k
  19. ");# X$ i5 h+ `5 x/ _4 J, H
  20.         2 ]7 c2 F  Z* @6 k5 w+ l2 s
  21.         struct sockaddr_in srv_addr;
    . u4 F- g" Y' }3 D# |5 r
  22.         memset(&srv_addr, 0, sizeof(srv_addr));; r$ Z$ a. L4 E! p
  23.         srv_addr.sin_family                 = AF_INET;; I; G: v4 E# C
  24.         srv_addr.sin_port                         = htons(8888);4 `  t( ^* P: _  W  b
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    + d, u6 }% c# `& I- e$ d8 u+ c
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))' R6 o# L+ k9 Z% q) Z7 z+ p" m2 _
  27.         {2 K$ `% c: W: _
  28.                 perror("connect");
    + G" G6 ^7 s/ D3 K3 F- N: y8 K6 G! T
  29.                 return -1; //exit //pthread_exit
    % X0 b1 E  M: O6 b7 S
  30.         }3 K! T+ M  |: R7 K4 Q! t8 G% `
  31.         printf("connect..............- e- ]! r7 W) ~* h+ A5 Z
  32. ");- |0 ?5 @5 m7 K
  33.         char buf[100];
    * k( _+ [+ l' w" q
  34.         int ret;3 X( D: h% j/ U
  35.         while (1)+ G: C0 W: h' @' b
  36.         {
    ' L, J/ e3 U+ z" k
  37.                 printf("send: ");: l: a7 k# Y* {, u- v
  38.                 fgets(buf, sizeof(buf), stdin);
    , |2 M2 ^7 B$ N" l( @
  39.                 ret = write(sockfd, buf, sizeof(buf));
    / s. ^3 C2 b! x
  40.                 if (ret < 0). y8 N6 t1 V6 Y9 u6 t
  41.                 {+ `! R, w3 w0 Y" b9 D# m* m# p6 G
  42.                         perror("write");
    6 o2 l! v- e" L" U% k
  43.                         break;# m  a# Z# Z$ @, g
  44.                 }/ K0 n4 t% @7 q/ J5 V2 V+ s
  45.                 if (strncmp(buf, "quit", 4) == 0)
    9 Z3 |5 r, K/ k3 P  p7 n  |
  46.                         break;- c7 Q; @7 u: b+ l) l; g
  47.         }
    & M7 O5 {" f: T6 j
  48.         close(sockfd);
    # b8 M' Q1 U6 E0 y5 a- y; ]
  49.         return 0;
    9 ?- W9 i( r0 O. ?) z% G( ^
  50. }
复制代码

; D& k7 o5 G1 `  ^2 u+ g1 q$ i2 _, F# R) l( k$ S- G6 S3 p
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 07:02 , Processed in 0.122366 second(s), 24 queries .

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