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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

9 p, \' `- ]& D

9 W! i4 d# U! U- L2 e& V( ]9 [socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
( U8 x: ?% o4 @# R" I7 k  K5 y  V, \: R2 v1 e0 E. u
# _" Q+ u6 P5 Z/ |& n( O. G
TCP协议
8 r* O/ u# b) KTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。4 ]0 n+ P9 u8 \, {2 ]( H1 w% g
" z9 ^3 P6 K, X; y0 Y

0 E* T3 f0 o" Q4 f关键词:三次握手,可靠,基于字节流。
( k! ^8 }3 s6 @. W
" l7 b$ b1 a* K3 ?& p1 R
8 P: l! ]3 L  c" n- @
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。# o  P8 ?* T% L- a' j; k7 B
, p8 {) N: H2 U% [3 T/ v
TCP服务器端和客户端的运行流程
( P: A) v9 h8 m- s4 S. t  [0 B- e2 |; o. G- x3 K
9 V2 }/ x5 S4 {) v* N9 B
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?; h. F9 z0 P% q/ @
' G/ Z: x4 S3 x9 M5 x3 a* _

) k( v9 `" S' e  U! x1.创建socket
" q: M$ o5 B* B- {1 S socket是一个结构体,被创建在内核中
% U) m0 S, o3 E sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议$ A3 w. S- K1 K% N1 Z( I

( `* ^( D6 l" I- H  |& g6 N

" c) O3 x- B, A6 i3 s6 k: g! {2.调用bind函数
* D9 l2 b6 \* ^7 l8 Z& d* d 将socket和地址(包括ip、port)绑定。
4 z, Z/ v: e8 _ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) x6 e8 G0 h9 p9 @7 S2 }9 ~/ o+ b" e
struct sockaddr_in myaddr; //地址结构体& G: P, N2 l! [" t9 g7 q% A
bind函数
7 Z' v9 X) B1 p3 ] bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
' z) u8 e- B& p9 Z* k2 a8 j( C* X
/ j4 F/ v6 E4 n5 b/ R
3.listen监听,将接收到的客户端连接放入队列
$ \7 z: i6 t' {; t$ N4 `. R listen(sockfd,8) //第二个参数是队列长度" Y0 `& ^1 C! j2 [

/ L: }$ D+ _! l0 b
, h! ?+ f- M5 N. ?
4.调用accept函数,从队列获取请求,返回socket描 述符! M5 w4 h% Z2 ]6 z' \7 z# C
  如果无请求,将会阻塞,直到获得连接6 a, o: F: U- F, ]( S% J
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数: V* [* ]  Y$ E, }, ~( F! y  }" ^7 j

: x7 B5 |* l# V4 t5 R  Q% X

! d, ?: p1 x* E, {* p5 B# }: |1 N) y5.调用read/write进行双向通信
9 w, u2 ^' ~/ j, w% |6 U! X7 a4 g- C8 u8 c9 J, z: A, R
5 ^  n; s0 p2 D0 m* r/ O
6.关闭accept返回的socket6 g, h* g9 m) t+ l- J8 ~
  close(scokfd);1 ~/ a" A+ W; x0 w# H( J( m

: ]( t  R2 D& A: `

- L7 Q2 q4 \2 B7 Z$ h
" I! P$ N5 Y4 R+ C% \! R9 |

! w% G2 E; H9 _$ @( I# O下面放出完整代码
3 n( r4 Q! q& u
( I( z7 @7 H1 K( i& H4 ^: P
  1. /*服务器*/
    ' e4 k9 m, }" r3 @4 l8 B* Z- X
  2. #include <stdio.h>, i6 l' U1 n1 n$ w6 B! H7 k
  3. #include <string.h>
      X: ^" I4 F& G, q6 E) V) ~
  4. #include <stdlib.h>
    & i3 K+ A1 Y. V8 }/ E
  5. #include <strings.h>- k( M/ b4 A% m& |, I& b+ ?4 a. I# ]  M
  6. #include <sys/types.h>
    2 O/ c+ n! S1 I7 y/ O' R
  7. #include <sys/socket.h>
    : z" a; @, e9 X
  8. #include <arpa/inet.h>( ^% r$ Z8 I4 @+ i
  9. #include <netinet/in.h>
    5 t- l  K6 g) f/ R( o2 O; k
  10. int main()6 c9 L6 p) V" j) ~* Q4 D
  11. {; `8 y1 Q0 V/ {" Y- C1 ~
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字5 s1 H, z& h. U
  13.         if (sockfd < 0)
    : G: q, `( `: J. {! `
  14.         {
      C6 e3 L& c, e- T7 O; f. ^
  15.                 perror("socket");; y& F4 J+ h7 W6 U7 t
  16.                 return -1;
    5 G3 Z: Y1 P/ X) t/ A3 D
  17.         } //创建失败的错误处理
    2 T9 y4 g8 d2 B3 r- O
  18.          printf("socket..............
    3 u1 W/ k3 B9 _' P, f
  19. "); //成功则打印“socket。。。。”# l, N$ X2 {4 M; P6 W. |
  20.          
    4 d  R+ e2 L/ n+ K' m  Y
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体, h4 Z+ E9 p2 u; q
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)8 g" E) b% M* {6 z. g' E+ m
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    2 h# L# G+ A9 t7 ~
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ! ^7 d/ Z3 `. c2 n5 T$ [" U( n1 B
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址. c8 R7 ^3 W% v. n% V; j# }

  26. ) L% M! k! f$ q, M+ n
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字2 y) o* q* R5 d
  28.          {
    9 w( z8 \3 l/ J# _4 v
  29.                  perror("bind");
    ( s) r* j' k+ R8 ^! A$ J
  30.                  return -1;" Y% O, w! F0 X! c7 i
  31.          }
    7 I# q& C7 c: ?$ V5 D( S
  32.          printf("bind..........: k, l! H9 ~7 B+ E5 p
  33. ");; q4 q1 f7 ~+ ]' ~8 `

  34. 8 B( a$ ]+ Y$ c' |! y4 x
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    0 s0 a5 z' s# O* C) o
  36.          {9 G" Y6 d8 |1 j, c. J1 G
  37.                  perror("listen");2 d4 n; ~1 |, k3 f" l7 R
  38.                  return -1;
    0 ~4 h3 D( f- L5 q) t; C/ p, A
  39.          }) t0 d' [4 [1 ~, e: \
  40.          printf("listen............
    ) q( L6 h  K0 O# S
  41. ");! f/ v: t; M$ `# h' {
  42.          
    ) D8 R7 N* y7 K! g  a/ y5 q; t- }! S8 E# v
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求7 _' ]4 t- o1 v) a7 p
  44.         if (connfd < 0)- k2 _- }0 D: Y( ]
  45.         {: A& b0 O* w% s5 w* z
  46.                 perror("accept");
    2 ]/ W" R8 y8 l3 o/ k
  47.                 return -1;8 h8 G) l; _# }# N" ^" g3 t
  48.         }: J, q0 _9 }0 C' [! [& M
  49.         printf("accept..............
    2 U) e, Q9 D& O* i! ~
  50. ");
    6 T% p6 r; o- m5 r7 ~# n6 l
  51.         char buf[100];//定义一个数组用来存储接收到的数据9 y# O$ P' b1 p2 t4 W  W8 ]/ C
  52.         int ret;6 J9 E* G6 t; V, ]6 d
  53.         while (1)
    # p1 E" |% }( b" x
  54.         {
    ! A* [6 G! A4 C
  55.                 memset(buf, 0, sizeof(buf));% x' M% Q) L$ O* q  T! X
  56.                 ret = read(connfd, buf, sizeof(buf));( \. f" R/ F/ L5 R- V
  57.                 if (0 > ret)% z1 E+ d% ~* k5 P0 q
  58.                 {" \- {- |& J& x. ^6 W% i# z/ @5 u
  59.                         perror("read");" O8 P) W9 U+ e4 ^/ J! j
  60.                         break;! f+ J9 o, Y, l) U
  61.                 }//执行while循环读取数据,当
    6 X. \! _0 U7 y. b5 k1 p
  62.                 else if (0 == ret)
    7 y' W! v4 `) I0 N$ F/ a$ n
  63.                 {$ _% t0 {# P: P" V3 S+ z
  64.                         printf("write close!
    . q' `7 U0 d) Q2 w$ V. q: y
  65. ");
    / z2 _$ D1 t3 e. P5 _+ K9 N" i
  66.                         break;
    " N* w& P4 @! L3 [
  67.                 }" _2 U  t* H; z$ v& P6 a7 c
  68.                 printf("recv: ");' z- o5 @3 m1 E: b; V+ u! L
  69.                 fputs(buf, stdout);//打印接收到的数据+ b' V- c$ ?2 D8 j$ b
  70.         }+ z, o& e- z9 Z, ]# o) i
  71.         close(sockfd);//关闭套接字
    5 Y2 @: N) C% G2 u1 H# S
  72.         close(connfd);//断开连接% e5 _1 Y  a& s) I4 ~; E4 k
  73.         return 0;; a0 S  U1 P- s3 A% w
  74. }
复制代码
3 P/ u3 \0 b- r3 ^- Q  V; ?( L- w

4 r8 W2 M  Y5 {! n  {
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释), B# b, g; ?- K. k: x
  2. #include <stdio.h>' P- o5 O9 I( o0 p+ G
  3. #include <string.h>( n0 L& [5 p( @  E6 w4 Y
  4. #include <stdlib.h>& g# W( V( J5 f* A" [3 F
  5. #include <strings.h>
    0 D6 |! N9 E) `( s% Z3 m
  6. #include <sys/types.h>
    * t+ T+ c2 l& E: B- m
  7. #include <sys/socket.h>8 a$ M. v" w% C0 s$ d2 ^) h
  8. #include <netinet/in.h>
    5 z' f3 q! Y" H+ g
  9. #include <arpa/inet.h># f3 F, y/ y  A0 r% `: }! t
  10. int main()3 x2 l/ a: \1 c$ R
  11. {8 c8 k. C0 A/ I( ]) D/ `) D
  12. int sockfd;
    . M  \, b% E% V- e$ I5 S
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    4 E" y/ Z# }) x3 w, o
  14.         {6 w' z- _: `& Y. }" s9 v
  15.                 perror("socket");! H# Z0 b* q) A. q2 E4 b$ m
  16.                 return -1;
    6 w' f, A  Z  E6 _2 k
  17.         }
    $ b4 u7 W+ S, y7 ~" j8 _
  18.         printf("socket...........  s/ U" E$ Q' `8 h" y% t- W/ L
  19. ");
    8 U* k  c0 D1 y; X
  20.         $ k6 T1 @  p" s% y! d. `2 }9 q
  21.         struct sockaddr_in srv_addr;! \& A- C1 g. A' R. w& w
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    3 s" D0 a! J2 f
  23.         srv_addr.sin_family                 = AF_INET;
    : y1 |7 ?1 c# @3 I; \
  24.         srv_addr.sin_port                         = htons(8888);. r' n1 r; e5 i3 ~( j* z* H( q
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    5 n  _' A& m0 N9 z% P9 E: M0 y
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    , @1 a6 [7 }4 f$ I% W
  27.         {
    7 w; @+ N2 z- q4 [/ u
  28.                 perror("connect");
    " L: U1 E- r6 X" a, _$ m6 `
  29.                 return -1; //exit //pthread_exit$ J6 M% G, m, a" [- F
  30.         }
    % x; G2 {) M+ o9 S" Y
  31.         printf("connect..............
    * Q- C3 i4 G. R' i2 V# p
  32. ");1 y% ~5 n/ s) b
  33.         char buf[100];1 p/ g/ y" z& ?& q
  34.         int ret;; Q: {; R3 Y) X  Y
  35.         while (1)
    1 m; r& b  w: M6 |2 f
  36.         {
      `0 y! A0 p+ c
  37.                 printf("send: ");( F2 ^% e# @# S2 [9 [
  38.                 fgets(buf, sizeof(buf), stdin);
    7 E8 [1 c, y8 _: B3 b8 F/ Q
  39.                 ret = write(sockfd, buf, sizeof(buf));+ r1 h+ k) \7 Q; o9 ?& Z( C+ G
  40.                 if (ret < 0)" B: g& M( g, {! |$ G" Z! y
  41.                 {; C9 r" M& c" |' A1 U/ Y& F5 E  r
  42.                         perror("write");- |  l1 R) f: m
  43.                         break;
    1 R4 r' ^+ U) o
  44.                 }6 s( B+ [4 [% \0 {  H
  45.                 if (strncmp(buf, "quit", 4) == 0)2 C3 j7 l; N7 i' S. {
  46.                         break;6 Y* D) A6 _, v! X! e; T
  47.         }
    - C* K2 k" h: p5 W5 ?; ]
  48.         close(sockfd);0 u4 n- V+ @2 j5 I3 _
  49.         return 0;
    $ D0 O9 ]- K1 E' G1 E
  50. }
复制代码
+ |, S7 |1 D( a- u

' x: k+ h( O- o6 n7 o  W9 u8 m5 Q& U
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-8-4 07:04 , Processed in 0.059860 second(s), 22 queries .

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