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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

" E- X" P0 R, |

4 `% f; H' |& ?, Tsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。. M: O1 Y0 U9 {

- m8 p! d- @% {9 F$ A/ m0 n% O4 ^2 a

/ m: G" V# N; u$ J5 iTCP协议
0 F5 {. f9 @% w" R9 |, I8 O9 {8 PTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
, ~' i7 N% C+ v$ I$ W7 P& m" b
* e; M& e& Y4 C
. w6 @8 S/ E) z  r7 d5 T8 f. @6 T
关键词:三次握手,可靠,基于字节流。
. y' D# O( H6 v  U
: d5 S# m4 l5 D4 W$ a$ o# U* j6 N

! i) u5 U4 e+ D$ _1 G7 N可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
8 K6 ?- ]& U5 l/ C' q
% x  _2 W5 r) H  o' n5 v6 lTCP服务器端和客户端的运行流程; R0 M# k% W2 J* Y

# P( P: p* h, \9 C; Y

2 Y7 B/ p  y' M- W如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
: Y: J0 \+ u% H0 C3 n2 j+ J5 m# S- k7 E9 O3 m2 X, @- S

( R) M  x% R/ j; g# L6 [6 f1.创建socket
6 W% t  p! x1 _2 K5 e8 G" Z  m+ P1 W socket是一个结构体,被创建在内核中
7 D; j4 x4 A' r( V# L" R sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
8 S/ S" u2 X' m2 \6 p; ?& V/ y1 B2 E9 D2 q) e, h; M
! ?; f3 V- y' L/ q
2.调用bind函数
, ^' |" @! W  V3 s 将socket和地址(包括ip、port)绑定。
3 K5 b& F" ]+ r7 H% i8 T 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序% _+ _% s6 [( }1 Q+ U
struct sockaddr_in myaddr; //地址结构体
5 _4 C) s  @2 H bind函数
. @- b5 w& Y2 n0 z4 `. _1 i bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
; ^& v0 k4 t4 g2 }  Z! J" M
% E8 D! g& d1 x& c6 ?
+ s3 R. K: p# q: c
3.listen监听,将接收到的客户端连接放入队列/ _: B! T$ F* ~1 J7 g" H! [4 x
listen(sockfd,8) //第二个参数是队列长度2 H( T. T5 }1 l
" o% P0 G, r* Q0 d
$ x$ b! A: m9 T2 h) O3 E, U
4.调用accept函数,从队列获取请求,返回socket描 述符" e  [7 g3 l1 Z6 l9 @4 T1 \% W
  如果无请求,将会阻塞,直到获得连接! ^+ h, Y5 K9 f7 I" p/ N
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数3 R' Y( ]8 y. z, R: x" {& |( }
8 ~6 r# C% L/ T, ~/ V! f

: K- S3 T2 T3 H4 y; ~* n& h5.调用read/write进行双向通信' P4 P+ g6 j+ s* ?1 y7 K# O3 u- E

0 h" a1 d' Y; r" Y. W, W; k

4 ^" T/ q% g2 b6 ]* P' ^0 M6.关闭accept返回的socket. }  X  Y/ c: q* r& F4 `
  close(scokfd);5 a, p& d8 S% O0 F; R
9 i6 X; H% b0 p' p- V$ j
' ]# O! i3 b5 y

9 G2 Z( e: E! {8 i

# f/ Z1 c  J% T! J8 O( m+ }' P下面放出完整代码
& t8 C1 M: [6 g/ j
7 m5 C$ b& `. B' p. s9 D& Z3 L
  1. /*服务器*/
    6 K3 U1 T& ^( R# O. d
  2. #include <stdio.h>
    8 o( E; x( [7 m6 [2 J& `: [8 x
  3. #include <string.h>
    6 a$ _  L+ Y' O& T1 n
  4. #include <stdlib.h>0 g1 D# [5 Y; L  n
  5. #include <strings.h>/ X" u& S/ x& [  ]2 z
  6. #include <sys/types.h>6 E2 }, \# L9 {( E+ _/ B, i
  7. #include <sys/socket.h>
    ' A3 A, u' o3 @1 X! y% Y1 R' h! v& F
  8. #include <arpa/inet.h>+ l: N! q9 l* l9 I8 h
  9. #include <netinet/in.h>8 b- ?- F4 ]( o. L$ M
  10. int main()
    3 b/ I  \6 j, W' G# @3 f
  11. {) S# M& ^4 G5 J' ?
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    6 d& a/ J; X7 X
  13.         if (sockfd < 0)
    # O- g8 Z3 Y. c
  14.         {1 _/ F! W5 ?0 t" k
  15.                 perror("socket");  ]0 B# M5 M' S7 Z* |5 @
  16.                 return -1;
    # P6 v. I, Y/ E- m2 L  J
  17.         } //创建失败的错误处理" x& F# J! K! y6 k
  18.          printf("socket..............
    / }4 h0 S9 C5 Y$ b; O; S& K3 T4 R! p0 K& F
  19. "); //成功则打印“socket。。。。”  H" H/ R* s( P( l+ j" p3 T
  20.          
    8 s4 Z8 E4 L; G# k# r, v0 D- E
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体0 v6 q8 K* Z# [6 w; |. W  a4 t
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    % I5 P" e* [& E- f1 V0 j
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型- o, k; ^7 S3 N% O4 x5 x3 ^
  24.          myaddr.sin_port                 = htons(8888); //选择端口号, t) k5 C. B" k
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ( E2 q2 R( c3 X& L3 \1 a

  26. ; _. F7 t+ o' g( w7 Z
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字0 I) a. g( S  u8 d/ I  M
  28.          {
    ' p3 k# h  R2 `+ a
  29.                  perror("bind");
    & F% Y* |/ }( k: k. N2 J
  30.                  return -1;+ F4 o% L! h( p5 K5 ?$ [3 ?9 ?% a
  31.          }
    & f  l- ]0 T& E, o/ M
  32.          printf("bind..........
    9 b8 [# r( K5 C! P- ~
  33. ");
    ( b4 ?# o8 V: r" e
  34. 3 e- y8 t. O- r" k7 q
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    , ~8 ?  V$ K  a" A  e( L! e4 T# l
  36.          {
    0 ~8 M8 A) }4 j# k0 Z
  37.                  perror("listen");% r) Q3 ^$ t" `: M
  38.                  return -1;7 X) e* y  h9 p
  39.          }, J. Z2 X. `4 W& S
  40.          printf("listen............
    & K% v9 P5 b, u5 [3 o  G0 `
  41. ");% B5 F* k* x  z9 `6 {- a
  42.          
    3 L# b9 t% k& y4 K
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    / L+ g. f6 {; E5 I; k$ @
  44.         if (connfd < 0)4 \# w6 g8 t" k, F
  45.         {
    ' D- R% Q, h9 i1 p! f4 B2 G
  46.                 perror("accept");$ D0 m  p: n. A
  47.                 return -1;9 q( r0 O0 x. k9 I9 P1 \4 m/ ], X
  48.         }9 F# u/ t( Q0 P1 q6 Y
  49.         printf("accept..............
    ; i+ K/ f7 C3 I. t& o3 J
  50. ");
    ) ~" D1 O9 k) D$ B& Y0 E7 Q
  51.         char buf[100];//定义一个数组用来存储接收到的数据- M9 A5 S/ J0 M! R$ O
  52.         int ret;) w, c" ^  _5 {3 ~' i
  53.         while (1)0 @8 Q# T2 {/ _( m- s4 T# p
  54.         {: u/ Q) l! {) A" U  J# w. Q
  55.                 memset(buf, 0, sizeof(buf));* X$ N2 s: _' ~
  56.                 ret = read(connfd, buf, sizeof(buf));
    " w! `. j# g1 A# K; F+ t
  57.                 if (0 > ret)
    9 I6 a8 @: [1 v9 @( u& }
  58.                 {. P6 N+ |& ?; ?
  59.                         perror("read");
    9 Y! N* B+ e/ B1 i4 D' x4 a
  60.                         break;4 `, \- r- v  p% V2 k
  61.                 }//执行while循环读取数据,当
    + g& J. i: w% t0 y# [! i  @
  62.                 else if (0 == ret)  h6 {  p; M! ?& Z5 j8 S* z
  63.                 {
    9 A& F6 E! u7 e2 C' K: x3 }' c
  64.                         printf("write close!6 E: X/ c' j8 d& b  L$ L
  65. ");
    . x, x% w4 k, t5 {8 D- C% K% D
  66.                         break;
    5 Z$ ]6 k9 {  f- K; y& \* J
  67.                 }, t: E) r6 j. P# v/ z' u/ t+ l0 p
  68.                 printf("recv: ");) Q3 Y( Z$ k) F" Z5 U' P, @
  69.                 fputs(buf, stdout);//打印接收到的数据3 }$ x7 x3 n  g  S
  70.         }2 ~* S# ]1 z1 X/ H. r( Q4 L
  71.         close(sockfd);//关闭套接字1 A/ O3 m: D; ^+ m, j" {
  72.         close(connfd);//断开连接! V0 f6 q  {/ V; K+ P. n
  73.         return 0;! B' D1 S) M$ k7 O* Q1 q
  74. }
复制代码

! V1 |3 i6 d% f* i# ]* N  M5 S9 H% G  l$ q+ _
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    & Z% _: C' S* Q
  2. #include <stdio.h>
    ' C. M) t' Q! x' j3 ?
  3. #include <string.h>( }& \5 s5 K3 T& ~4 M- g. R
  4. #include <stdlib.h>! p) ^/ N8 Q  ^! N6 E( d$ n- e) a
  5. #include <strings.h>+ E0 d* [( G7 t/ P5 h
  6. #include <sys/types.h>
    ) W. ^. {. s! x5 b
  7. #include <sys/socket.h>
    # j5 E' M& J, C2 C
  8. #include <netinet/in.h>
    ) v$ I0 j) {  W* U+ }+ T. z
  9. #include <arpa/inet.h># T( C  _3 R" K( ^/ s* {
  10. int main()
    5 R4 Y4 ^5 S  A6 _5 b7 {5 X8 ^, p" a
  11. {7 a$ }; ?, u) B( h, f4 r
  12. int sockfd;
    . W. H4 r1 Z2 i" s6 C4 j& o
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))); y7 E, A5 t" T) \) H; V  v& N
  14.         {
    ; o5 T2 k: e: q: [; w
  15.                 perror("socket");
    + |7 P# V/ y, q
  16.                 return -1;, d/ Q( a: h1 P# k# D! T
  17.         }
    ( d8 P5 v$ E- l! f
  18.         printf("socket...........
    1 J2 B1 |* P* k$ p  }" N: K$ [
  19. ");
    + b2 ~; B( C' V5 V- Y
  20.         % d4 {7 P2 D: M; o
  21.         struct sockaddr_in srv_addr;
    3 C( v* H: X* _, h# E/ W( T/ V
  22.         memset(&srv_addr, 0, sizeof(srv_addr));: E, x) O  L5 ^: y& W! v) ?
  23.         srv_addr.sin_family                 = AF_INET;1 m' m& i/ ?( j  j7 o
  24.         srv_addr.sin_port                         = htons(8888);/ i5 H1 N2 {3 l5 ^9 u
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");' o$ j+ H/ i2 s$ X0 f8 m5 Q
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))! n) b5 l! z( A, w) r" p8 Z$ d
  27.         {
    8 \5 z4 w9 u6 m8 J( C
  28.                 perror("connect");
    8 b6 Q  f" W6 G( K% l2 Z
  29.                 return -1; //exit //pthread_exit
    8 ?3 a5 ^+ i7 m2 u3 D, A
  30.         }
    6 r; ^# M2 l2 E- x2 ]! E' ]3 v
  31.         printf("connect..............+ L, D8 Z! \7 d
  32. ");5 ~0 `& D* O7 f7 c  Z
  33.         char buf[100];
    6 v' C' p# u# a1 N; T  V
  34.         int ret;7 e6 a3 i; M4 U6 l: ~# [( X# \
  35.         while (1)
    # T$ o9 g9 I7 G& ]2 e( K: Z+ \
  36.         {# K" j. E8 G# A) j; B, R
  37.                 printf("send: ");
    . Q) V2 Y, F7 j, ^& L; z! R
  38.                 fgets(buf, sizeof(buf), stdin);) k3 T2 e/ B& ^0 z; _* r
  39.                 ret = write(sockfd, buf, sizeof(buf));* q& E, C6 W& ]' d, q. Z; P
  40.                 if (ret < 0)
    ) l# m, T+ ]& {$ O  J
  41.                 {
    3 o4 }9 K: n  o% T0 m3 H
  42.                         perror("write");7 v: U. a7 |2 G  [
  43.                         break;
    1 B7 I( [1 s: {2 T; p
  44.                 }5 M) O2 u$ d' |/ }0 O$ F& [3 l8 m. {
  45.                 if (strncmp(buf, "quit", 4) == 0)7 A9 a7 z+ M- C3 H: [+ o6 `7 d
  46.                         break;
    ( I/ X2 y; b1 _$ o% h
  47.         }* s/ c8 T' `3 Z6 j( d% ]
  48.         close(sockfd);
    9 s3 H$ }, V- ^
  49.         return 0;) C. V" J, `7 p* z; g
  50. }
复制代码

- @& j$ {* z. |) C" C1 U5 |0 q3 P" G+ V( X+ r6 q$ L3 k
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 00:43 , Processed in 0.135777 second(s), 23 queries .

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