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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

9 U% |, C6 f5 j. E; M7 G

( D3 |8 r+ @. `- M) V" u: rsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。  _. _+ u0 @* c0 U- F% Z: M
' B/ K, b8 b7 a4 F
, G6 [5 O# }* D8 X
TCP协议$ U4 i1 ^1 T1 R2 n, v) ^2 a4 g
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
; [  r  {9 i3 O! `/ B& E, e4 w! Q9 J! |% _1 c; E8 a

4 M5 F1 M: ^) z; X关键词:三次握手,可靠,基于字节流。
: ~6 E' f( F% S6 r
2 L. k4 l' i  u" C
+ Z% [9 h* u; M. g7 q
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。/ T7 S$ s4 Y5 S* w4 F& {

" J2 I; X- [' B  ~) Q7 MTCP服务器端和客户端的运行流程
- T  y/ ~* M1 Y
, E+ w; S" V* w2 n2 U, [! R6 D
0 e) u' ?: G0 I
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
( ^% ]3 ]8 L1 q! q9 \, j' S  ]4 U+ ~8 ^, {: |

8 k  t  C# q) @1.创建socket
" ?  J; R- z+ n9 f* m3 m socket是一个结构体,被创建在内核中
! l$ F% Y7 j. u. Z) Z5 V6 q) E3 Z sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
$ J1 @: r- B* x  Q5 P7 q
- R" d0 |8 }5 s% ]0 L

; y& L0 E+ c" Q, K9 s& d2.调用bind函数" _, P* ?) b3 j2 z# U4 O4 {
将socket和地址(包括ip、port)绑定。9 n! i1 L# t6 f! P
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序8 P9 K% U& ?1 `% A5 E8 e9 |& G
struct sockaddr_in myaddr; //地址结构体
0 f8 J$ X. I; o* \ bind函数7 D# }. G- i+ ?+ B* X: L
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))6 K7 H+ E' ]; S) T( A

2 M6 Q5 l+ u' R! }

9 I. @+ v7 Z# C! S) |- U8 ?3.listen监听,将接收到的客户端连接放入队列
) R# j  z9 c4 z5 w listen(sockfd,8) //第二个参数是队列长度7 y& M0 D. O# r  O0 U8 |
4 n; Y" j' p9 \6 P* S
7 T0 V3 O9 _: i; h2 b
4.调用accept函数,从队列获取请求,返回socket描 述符
! ^" Q. o( O  m; @. n' M: ]  如果无请求,将会阻塞,直到获得连接
& C2 Z* A( l5 E& e" ^2 s2 F$ x" v  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
6 `4 c8 b! j; I1 ]0 |0 Y/ a
2 X  f- q6 l+ v7 Y

# h( `! d: g" \  T0 |5.调用read/write进行双向通信& p5 [. ^& d  j# o. E1 [
% x6 d, x& _% w" t9 Q0 ?& _

/ `$ D# g  s0 P  n7 f6.关闭accept返回的socket
" {5 l5 c  ^: z9 ]8 |  close(scokfd);" M4 n  P# Q3 m& d8 \

9 L: n, O2 ^- |0 |9 n5 q

& ^% l+ x8 n  }
( w; k* c  D0 D
+ d$ E% [4 d  D9 S$ e; c3 C0 O
下面放出完整代码8 e* A- x. F. g" P

) i1 w% w  R; D- h; v' ]* g
  1. /*服务器*/' v4 V" u0 w3 r& {/ m# p4 R
  2. #include <stdio.h>
    " L9 E* S! e; O0 Q: G6 W
  3. #include <string.h>) S6 T% k* J. T2 a) q
  4. #include <stdlib.h>% ~5 c/ }& e; f9 t( p
  5. #include <strings.h>
    - d, A4 R# v) \
  6. #include <sys/types.h>7 E4 b9 o- c+ `+ v% {. n  H
  7. #include <sys/socket.h>
    3 n8 r" r) v# G7 G7 b2 P& T2 z5 c
  8. #include <arpa/inet.h>
    : _5 g3 e4 n" H) L+ z9 ~
  9. #include <netinet/in.h>
    7 Q' L" g: P2 j+ e0 t
  10. int main()3 R; t3 Y. W: v: z7 h  }
  11. {3 I+ e4 H) w- u9 L2 ~- t0 Y
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字" [6 V4 e( p6 z# i/ I: J
  13.         if (sockfd < 0)
    * ^0 ]/ k/ ~' R6 \2 ~
  14.         {9 y6 ]; `: \% Q7 m2 }
  15.                 perror("socket");
    6 r& O! ?7 d6 ?* q% ^
  16.                 return -1;5 E8 }6 |7 V; q0 W  Y2 n, Q: M
  17.         } //创建失败的错误处理
    & z5 b% E9 n( p  a' C% @0 `
  18.          printf("socket..............
    ! P* m* C: ~: S
  19. "); //成功则打印“socket。。。。”% B4 Z- [* _4 F6 S% |
  20.          0 P* h$ A, P: L
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    8 `5 a8 S: [3 M0 ^/ q6 j& f0 N
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    1 p) V1 q7 C6 V+ \$ R3 W
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型! _7 J" _* r5 a
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ! y. U; V+ z( N
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址; _, @  x/ q7 A% U3 ?; t' x

  26. & P* b! c: b+ g" z5 N; Z
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    : x1 Q9 w) T' R; Z1 y4 G
  28.          {0 x  T2 y1 B1 x( P; Q
  29.                  perror("bind");
    . r6 Y. q4 A2 |. s1 n
  30.                  return -1;# |8 T& \0 i- G7 g, o# I5 y
  31.          }1 L4 z8 G+ z: m; Z, ?+ H3 X
  32.          printf("bind..........
    ; k5 ^% P" Q4 w8 M  X
  33. ");5 L$ ]5 }$ w# d: @

  34. 2 Y7 u/ J1 N9 s
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    5 G* l: I7 y0 i7 h5 Z% P
  36.          {1 y$ K; O- U' Q8 P& F
  37.                  perror("listen");" {) C6 B+ u; T# u7 l
  38.                  return -1;
    ) ^3 G& d% }" J3 r" d' L% p: U
  39.          }" e# h' U7 `8 `) k. s
  40.          printf("listen............# M# M7 k' Q  w
  41. ");+ k. z& f6 W& M, {
  42.          . o8 I& P( L6 x
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    # e$ l5 r. n, ]3 Q
  44.         if (connfd < 0)
    8 N4 o' m0 X. f, n. ?# L- @
  45.         {
    + B) E  x6 M( Y9 u
  46.                 perror("accept");6 ~5 d! D) Q# @' [+ ]3 [- |
  47.                 return -1;
    1 R1 S/ Z& g/ a/ _; [9 I# n' n
  48.         }8 j: o. w" L) V$ p) S( h$ I1 _
  49.         printf("accept..............0 Z  P, {% `  o! P* G9 |9 P( C
  50. ");
    * K+ s0 h. P2 f% E/ ~# a( y, j
  51.         char buf[100];//定义一个数组用来存储接收到的数据
      b* V6 ^$ Z- O  l$ ]
  52.         int ret;; E$ h- M, \3 A( L' t
  53.         while (1)
    3 Q7 r/ Y3 I; m3 l
  54.         {. m8 [/ _/ X2 s+ y3 T; b' _; l
  55.                 memset(buf, 0, sizeof(buf));
    % d* k5 V8 j7 A# F# Q, L$ P; i
  56.                 ret = read(connfd, buf, sizeof(buf));
    4 P" N3 G/ |* d# U3 a0 f! Z" }
  57.                 if (0 > ret), s/ V8 R7 A9 C3 L7 a
  58.                 {. m# ?/ x/ q& @7 n' [) {
  59.                         perror("read");
    $ r9 a$ F/ @- P. j0 R
  60.                         break;6 B0 j' k+ Y1 a* [* v' e- k5 r6 h
  61.                 }//执行while循环读取数据,当
    / k3 y, X+ Q9 J/ [
  62.                 else if (0 == ret)
    : j' _  h9 {( k8 K) b0 ~4 `) Y, m
  63.                 {
    4 X+ q' _$ G2 D; V) n1 [* r  }2 x4 ^9 B
  64.                         printf("write close!0 u& k4 d! I& M$ L! |4 }
  65. ");
    4 v! }  J9 P, h4 L" N) T% q
  66.                         break;. x" R; X- j5 R0 f" c, l0 k
  67.                 }1 n3 ~" r* W+ L, T
  68.                 printf("recv: ");% J' t8 p% Y+ ]- H) |
  69.                 fputs(buf, stdout);//打印接收到的数据3 c( N4 H, O. k: ~# d
  70.         }! i, q, S! s3 h8 L5 C
  71.         close(sockfd);//关闭套接字2 v5 Y+ D# ^- ^6 L0 d
  72.         close(connfd);//断开连接6 i) N, `6 d5 Q
  73.         return 0;
    - j3 K* Z+ H- Q; J
  74. }
复制代码
6 R6 D$ K" r% g" s
. C( Z$ {0 u! F0 G) a
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    5 b; L. a' b" E5 g$ C
  2. #include <stdio.h>8 R6 D6 B" W1 n
  3. #include <string.h>
    0 W8 }7 z$ y) F- |
  4. #include <stdlib.h>
    . g" D, |/ b* _4 L2 J) `3 N
  5. #include <strings.h>
      _  ]" B+ t* I" r+ W% j, l, n  G
  6. #include <sys/types.h>4 i4 c1 T' i' C% h3 v/ E
  7. #include <sys/socket.h>
    : b  a, {  @: p, t# w  A
  8. #include <netinet/in.h>
    " u$ R4 q' o. J# H$ Y* j( ]
  9. #include <arpa/inet.h>
    0 }/ {: S/ q5 }( |% p, o2 O. P$ j) D
  10. int main()* e- v: C6 F/ S7 l/ r
  11. {, _' T+ {9 j7 t: l/ V" {
  12. int sockfd;+ V7 e9 `9 E& V
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ) C* M, Y' }5 @3 M
  14.         {6 k' k) e, q  S2 ~; N: T; [
  15.                 perror("socket");! C8 n# u1 ]7 M/ H0 s4 }' M
  16.                 return -1;
    " a2 r1 m% D- W1 s6 @+ G, A
  17.         }" w( V6 m9 q' y+ O
  18.         printf("socket...........7 K4 K5 v1 t* W/ X
  19. ");
    ! J6 t- M5 o+ B' r" G1 o4 r8 o
  20.         0 E1 M4 K) x( p: `/ T
  21.         struct sockaddr_in srv_addr;
    " h2 e' F9 O/ V; L8 I+ ^
  22.         memset(&srv_addr, 0, sizeof(srv_addr));4 q9 _- x0 K4 G' x& P
  23.         srv_addr.sin_family                 = AF_INET;
    : \+ {1 @$ g2 S2 b9 ?
  24.         srv_addr.sin_port                         = htons(8888);" n+ f: w6 L* B% v' d# k3 z$ ?2 ~
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");$ K. G( c. z' \6 T
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    / E! A( E7 g/ B% C
  27.         {
    ( V! j" x7 J( |1 `& T
  28.                 perror("connect");9 }, {- v3 v& u
  29.                 return -1; //exit //pthread_exit
    1 P7 k+ b- b' ^, y
  30.         }
    - C& ]$ a+ Y# F  N: j6 c7 e
  31.         printf("connect..............3 ^' i8 m( _8 V! V( d
  32. ");
    + E4 {+ C: M. y2 E7 L5 e
  33.         char buf[100];
    $ M: N3 e2 b: v1 R4 i' S, {
  34.         int ret;$ {! P% Z" }7 n+ J" p
  35.         while (1)+ y' X% x) t% D' ~, U
  36.         {
    3 e9 k2 C! K! `* h7 J
  37.                 printf("send: ");
    ' [7 x6 _7 l- ?- D6 \, ~0 ~
  38.                 fgets(buf, sizeof(buf), stdin);/ S" c( I) j3 q7 `" }
  39.                 ret = write(sockfd, buf, sizeof(buf));" _! Q  D) k8 h! }
  40.                 if (ret < 0)4 I( y+ c/ d0 I
  41.                 {
    + x6 \0 b$ T- ]3 B: v3 G( Q! V2 M% m
  42.                         perror("write");
    # i9 C; Q6 n7 G, H5 m. z
  43.                         break;
    ' Y0 N2 o, A% h7 T& G9 W1 B
  44.                 }
    ! W3 e' J% S* V- Z  w  b- P
  45.                 if (strncmp(buf, "quit", 4) == 0)9 a5 ^1 n7 O) j/ \$ `
  46.                         break;# C4 t( Q  V) R
  47.         }: c) a! s2 m1 d' x0 T, j
  48.         close(sockfd);
    & z& V8 l, ~2 L0 @: J2 |* U
  49.         return 0;4 g! h9 a2 S& `% h7 b' t; E/ |9 z' n
  50. }
复制代码
  ~( y0 f+ |5 q6 V: x- A0 y. H( h
2 v8 E9 `7 h3 j, i
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-16 15:21 , Processed in 0.066998 second(s), 22 queries .

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