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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
5 o( K4 x/ R) I% x% P" }# [& W1 f
" x, g2 d$ x* v0 F# f/ u
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
2 Z: U5 N0 O/ b. |& ^& f  D8 ~' U& z. k/ X. g  a

8 h( ?3 y- ?0 sTCP协议8 q, r7 I+ O/ L; W
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。% S& F$ M' i. _

8 D- j& ~/ F1 a1 [: o9 @
* k9 w1 i2 N' h8 i) F
关键词:三次握手,可靠,基于字节流。2 K1 q9 @! ?: O7 B

/ a' C( r( I5 w+ o/ t* M* D; H

) R, Y6 M$ r1 a- I. v可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
% V8 @5 M6 v4 B. r% E
* R6 \( C$ v/ h- u' ZTCP服务器端和客户端的运行流程9 j+ h% b% Z1 Z) N9 @: k8 ?( Z( Y

$ }& l* b+ L* X' o% `& s

" Y8 b7 }- W+ w* G$ x/ d) N  T如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?; y7 e5 m, [# D6 e. k

0 }  ]  B4 }' ?2 {% Z& I

, M: M2 [3 p( A1 Y1.创建socket- a/ p- N) S7 J+ Y  @
socket是一个结构体,被创建在内核中
+ M& R, P# Z1 m3 i' E sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
2 g$ ?4 h* c7 Q4 t: I% W) \/ M1 g1 E0 Y% H$ f2 y
2 H5 q) j+ [$ B' \7 t
2.调用bind函数
( B0 G: I+ x/ i( }2 M 将socket和地址(包括ip、port)绑定。
; A5 x* w% F" V. |" O* s' {6 o) A' { 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序2 j+ h+ [% n1 r$ B' a
struct sockaddr_in myaddr; //地址结构体0 L, M5 G1 W# c4 x- ]+ W& V3 S
bind函数  [) S7 y( F; ]% S
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* Q7 P( }0 _; ~3 i1 t" b6 b2 B. @3 T/ \
7 V/ {, u' V1 |+ \2 ]6 |5 f6 C# [
3.listen监听,将接收到的客户端连接放入队列, w9 Y6 w8 Q# Z
listen(sockfd,8) //第二个参数是队列长度
) [6 f6 A  U" O) l- B5 y4 I1 C% \# p3 ]; b. [" X# [9 e

0 [) D/ D* N- `  h  R6 t5 e4.调用accept函数,从队列获取请求,返回socket描 述符$ l' C: _: c9 W* q- Z
  如果无请求,将会阻塞,直到获得连接
* W2 D" v5 @7 [* L5 O2 o2 U  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数+ ], M* u4 ~8 e0 e8 {/ J  C! V# N3 y4 H

, \& t2 @& n1 C0 a

4 `0 f: A( y/ s2 c: r% X5.调用read/write进行双向通信
% h& `/ y- b3 d) M: c: x& v, @6 Q3 }) J2 n) P& j

" b7 I1 g) ^4 `- O7 u$ ?: e, \6.关闭accept返回的socket9 I& P9 c( L0 D' `- Q
  close(scokfd);/ ^: O& P0 s4 K2 b! Z

4 W+ h+ g- |7 C  ]4 {$ A
# d9 A4 F: }4 x1 z

0 v& H5 z3 k+ N! s
$ V  T  Y" ^' u; `/ W
下面放出完整代码
( ~; R( M8 c7 b" S: V, J* O; O# Y( ?" B' g6 ]* b- O
  1. /*服务器*/
    1 B  d/ X$ Q- O- ?
  2. #include <stdio.h>1 k, ?( u  t- D" Y
  3. #include <string.h>: @: {6 n9 S. W. K9 z
  4. #include <stdlib.h>/ j$ d" v8 U: h2 V; H
  5. #include <strings.h>
    ' F& e5 S; }2 U% K! m8 _1 G# i
  6. #include <sys/types.h>  r4 d& Y- K8 @3 E' \! X7 M
  7. #include <sys/socket.h>9 W- u7 p( w; X# [5 A& J& ?, L4 i/ p
  8. #include <arpa/inet.h>/ t& B3 H, E, v- c7 t+ W
  9. #include <netinet/in.h>
    + O$ k7 s# {, ?* j6 r3 ~) G
  10. int main()
    6 W( f* A5 D; A
  11. {! r1 {4 O1 K3 ?) r; n6 |
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字# }+ m5 }" D# U. N7 Q# C1 X% q
  13.         if (sockfd < 0)
    5 C0 a, r6 ?0 D* J2 I
  14.         {( n- L/ I) D" I0 e" ^/ H
  15.                 perror("socket");
    % \; O6 X% h! J' o2 z' {4 t
  16.                 return -1;1 h8 n+ A$ o9 f
  17.         } //创建失败的错误处理' x" d2 v% C) u- \6 ]" m7 y
  18.          printf("socket..............
    1 ^5 g! V9 f0 O+ d% f
  19. "); //成功则打印“socket。。。。”
    $ k. c" X- f) F# Q3 }( P/ z
  20.          
    4 D( @. o( O  i! g1 O9 u$ `
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    0 r: j/ Q! y8 ~5 L$ y" y
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ! {# R' A  D5 l' V; \4 l
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型! X7 \- _4 r  S
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    : b) q8 l& x6 Y- N' [" C# T0 M
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址4 S* ]- T/ P; R  ^& P6 q
  26. . X- J; F7 u) m8 s$ l
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    2 J# M+ \4 ?, y$ S" C
  28.          {
    / k/ h# M7 _5 V6 s
  29.                  perror("bind");5 D0 k0 b( w4 [( P/ |/ R
  30.                  return -1;0 Y) p1 B; }1 Q5 O/ B
  31.          }( m9 y! q3 _+ W
  32.          printf("bind..........2 M) f2 o% }5 M) z0 V* c7 k" O7 x
  33. ");2 j" t7 d! T, n1 f0 q0 _
  34. 3 o8 W: f) J0 }
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    / X; u4 J, d  a2 O! Z$ J( X( k/ B4 D
  36.          {6 X/ Z4 R. i9 U! r6 v1 F0 U1 I
  37.                  perror("listen");
    1 y" d, b" L! `" W- ~
  38.                  return -1;  z2 l2 P' o) S/ h8 B
  39.          }
    0 E: Z! Z) z1 X
  40.          printf("listen............
    ' c% b* W+ D0 I
  41. ");
    0 f- [" H- [: v
  42.          3 h2 U( u' m1 n
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求' v# e& o/ V/ S* @* [) s
  44.         if (connfd < 0), N, B3 L$ B/ ], P, ?
  45.         {
    % }3 r' o$ M# w$ y5 V5 W. A7 L
  46.                 perror("accept");; |4 M* }* ^( [* W
  47.                 return -1;
    % W5 z! _, [* X" W6 @
  48.         }
    7 u  i. M6 {( v0 L3 ?; s
  49.         printf("accept..............
    6 m( {) b4 O: Q# k2 r+ Q
  50. ");
    7 M  R- ^& f7 r5 G* @# Z
  51.         char buf[100];//定义一个数组用来存储接收到的数据$ x  C; s9 h4 e
  52.         int ret;
    ; |7 `% I6 r2 O. y" \; Y2 x' C! l
  53.         while (1): Y; q: a$ ~" d, }0 H: t
  54.         {! W1 Q3 Q3 |, f9 ?2 d: ~2 D
  55.                 memset(buf, 0, sizeof(buf));$ Y0 i. m4 T# U! o8 J& V; b9 ?
  56.                 ret = read(connfd, buf, sizeof(buf));/ Z( M  V) i- [. }7 M0 a
  57.                 if (0 > ret)
    1 Y5 E# i  N$ h
  58.                 {
    ' X. O$ c1 g( p6 |1 V3 V" _
  59.                         perror("read");
    0 Z/ m, w; R  e. X7 }
  60.                         break;+ r# W2 y; }" K$ y( y
  61.                 }//执行while循环读取数据,当# E9 ?9 R6 g( n4 B3 R/ G5 p
  62.                 else if (0 == ret)
    0 @; ?% @7 `* c% c% p
  63.                 {1 W- Z" C9 u, z( o. \7 A4 m
  64.                         printf("write close!
    ( P$ M1 j/ u8 {' e& E( ]- ^( w
  65. ");
    . C& _4 t6 F. Z2 l8 X
  66.                         break;
    ) g- }+ r: g' B1 h; e9 l
  67.                 }
    8 |/ y- ~2 O9 o4 L/ d; U
  68.                 printf("recv: ");6 l# n# F/ U" ]( h; o
  69.                 fputs(buf, stdout);//打印接收到的数据
    3 w' r% z$ k2 W% d0 @% H, P$ c* |( g
  70.         }" H: j0 X; M' M( P, A
  71.         close(sockfd);//关闭套接字5 C; S7 o' s/ ^/ g* ]: B0 J4 q' m
  72.         close(connfd);//断开连接
    & _/ W9 U7 b2 \' H2 N- v
  73.         return 0;
    + y: f* U% z: p  K
  74. }
复制代码
( Z4 Y% Q1 s$ M% N: m( B, n

+ s  |; y; t5 C1 s
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    9 E8 @& G4 `2 l4 A
  2. #include <stdio.h>
    2 K$ E5 E1 m0 Q+ `
  3. #include <string.h>6 |! D& X* J" D/ u
  4. #include <stdlib.h>
    3 e0 R* D& |( Y. ?* f: d8 n
  5. #include <strings.h>" G  N; g" C' p0 m9 Q
  6. #include <sys/types.h>  X9 ?& o4 ?6 k; d
  7. #include <sys/socket.h>0 R  j2 h1 ~) I2 p$ y: ~/ P
  8. #include <netinet/in.h>
    : j5 v# h7 {: R5 X' J: ~3 A9 y
  9. #include <arpa/inet.h>
    , ~7 e% W: ]. U# x! d
  10. int main()
    ! f  X# j5 G9 y& j3 A
  11. {/ u9 `6 o6 Z, V/ Y$ U( x! m
  12. int sockfd;
    & G( c+ X8 w. ~! w/ P3 r9 |# f/ p
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    5 Y) b0 Y" o! t7 Q  V% Z
  14.         {
    ( t* Q9 x6 U0 j9 D
  15.                 perror("socket");% g: N9 D; C. _7 b/ b# X" T0 j
  16.                 return -1;/ @; q* n4 K- R7 i$ G8 U
  17.         }5 Q3 g$ c7 W" s/ K, j# {
  18.         printf("socket...........
    + p  t" ^9 G1 K' A( F
  19. ");
    , F3 [. H; ^0 z+ L, j' V( T% J
  20.         # F# E) E, Q' A
  21.         struct sockaddr_in srv_addr;  I! ^6 A4 I# N$ T
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    2 ~5 s. t# V: k
  23.         srv_addr.sin_family                 = AF_INET;
    . K8 @- X/ a* a( H
  24.         srv_addr.sin_port                         = htons(8888);: U0 N% ]; \* v; f2 `+ t8 U
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");" f% F. v7 b$ I9 C! x& c
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))3 x( @4 {. n7 H* a$ g# X
  27.         {- x) L1 D' k4 d5 x' n
  28.                 perror("connect");. D/ S. n  p* k% n. s8 x/ L
  29.                 return -1; //exit //pthread_exit
    & T( R9 p; X3 a/ {9 Q
  30.         }
    $ k, J% M7 v$ `1 ]/ Q
  31.         printf("connect..............9 g5 p) S( }$ p5 t$ C& a4 Y
  32. ");2 `( Y, t& u0 I- ?
  33.         char buf[100];2 o; D, G6 r% c* `
  34.         int ret;
    ' R) E) t# C+ Z+ I" ?9 }$ K7 s  W0 t
  35.         while (1)
    ; Q, m% b1 _& v" ?6 A
  36.         {; Z# [* V( ^# @
  37.                 printf("send: ");+ |& i8 Q2 [* d" B+ x: w/ i
  38.                 fgets(buf, sizeof(buf), stdin);' u9 r* N' F" V/ L" v
  39.                 ret = write(sockfd, buf, sizeof(buf));
    6 _  }, ^( M5 t+ R6 C$ J! {' s4 e
  40.                 if (ret < 0)  N% b! @" F! L; E4 o
  41.                 {
    # U' j6 f2 N& J, Q
  42.                         perror("write");5 V5 {0 \( W+ }0 e3 h
  43.                         break;
    - Y' J6 ~; e9 l- e' r
  44.                 }; _4 M) v2 B2 Q( ~4 u
  45.                 if (strncmp(buf, "quit", 4) == 0)
    9 Y9 r9 J9 w* C$ j
  46.                         break;: q9 N. p5 q* k1 P, v; `. z/ y
  47.         }
    . W  D; W2 u, M" ~" G* l$ W  g
  48.         close(sockfd);7 K  e- [4 A: z
  49.         return 0;
      a, Q% O* E5 _2 `
  50. }
复制代码
) j, y6 i+ L& k+ u$ z

5 N+ |8 G& G9 p, F, q  S# y! V6 X) V2 o0 Z
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 04:14 , Processed in 0.121720 second(s), 22 queries .

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