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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

' k7 c/ Y6 G. ^6 n$ ^) b5 x

' M6 F8 ~# b2 o  l/ E! m. `socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
$ Q3 P) }# N; l& R
3 [) Z+ o. l' F# D5 g

1 v* ^) r  F. R3 y1 q/ qTCP协议
+ {6 T$ {0 R8 E. ?* QTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
8 ?  F' ~7 k& Y* U6 j* g: x& E4 L7 h8 a

2 Q8 u% S: w; d* _  W  p关键词:三次握手,可靠,基于字节流。
! O. Q/ c( v5 d1 u* m( ~6 J5 |4 ]) C6 W8 U

1 j, D# I: o5 r; Z  e% b4 |  q; w- I, B可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。: ~5 A+ Q, a+ A3 |
) u  ~9 _: U6 _' Q
TCP服务器端和客户端的运行流程' j2 l4 B: p! z& L3 l% W
) P+ h- q: |, _3 }0 m# _2 o
. y& c3 i* J" M& i1 ~2 @' t4 `
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?2 v) J& Q& G  O. W

& ]2 b& `1 c+ \7 W$ h# O8 S3 F
& N+ b; A0 _8 I
1.创建socket
4 f% x( ^; X' _  X% G  t7 f; V9 Y socket是一个结构体,被创建在内核中
5 F, }0 }2 }& l' O# } sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 v, a9 n; ], Y2 d4 [
4 {/ c7 ]( j* b
5 [% |8 N* j$ q: X4 P/ G7 p! }
2.调用bind函数
& o7 Z& q$ u" B8 [  F 将socket和地址(包括ip、port)绑定。
  ]* M3 t# O' b# q 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
: L$ W8 \. v+ @9 _' A struct sockaddr_in myaddr; //地址结构体
' b" ~4 \  D. g) F bind函数
1 M+ Z0 X0 r2 L( T! k. T  v bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
% L7 O1 ^1 E; n5 \! I7 y
/ O; m# j* T$ k

; _& I) y+ F8 E+ b! S3.listen监听,将接收到的客户端连接放入队列: @9 U. W8 k1 \- G
listen(sockfd,8) //第二个参数是队列长度: E0 Q: n, X3 \2 g
0 J3 s) H! x; `8 v" J  A
' F1 t3 t; @3 k" \, R  B7 l
4.调用accept函数,从队列获取请求,返回socket描 述符
) D6 l! `5 X' f  q/ ]! p2 ?2 w/ w  如果无请求,将会阻塞,直到获得连接
- L0 {: Z. X: ]$ K  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
2 o2 d% U, l, h% w+ V1 `* }6 _' u3 v6 n3 s% q6 `' h5 h
% ^1 \7 x: k& v2 p
5.调用read/write进行双向通信4 |" t4 X8 o/ a! t1 E2 F$ R

$ f% K  I2 D$ r3 `

3 Y* V3 L8 m7 ], i$ c) a; T: s* I6.关闭accept返回的socket4 \  o7 E% N8 u6 I; O; ?8 O+ r
  close(scokfd);- ^: _$ N7 A4 G) N8 `

! n& W% \5 S9 y9 a. c! i

' N7 _- b8 |( ?1 G/ ~9 `+ g2 V, T, ?' h" a" v- F
4 K5 W. x. O2 \7 e$ f
下面放出完整代码
- C/ _( f' a4 X0 Z( h! u
$ R' G4 r8 c9 J9 S, L  M% t
  1. /*服务器*/5 S: }( l+ x1 n" K: f  F. q
  2. #include <stdio.h>& M" Z, R+ j0 p
  3. #include <string.h>. o( s# b8 g0 Z. _5 i: \
  4. #include <stdlib.h>" A3 t6 o; g( p# D4 ~' a, n
  5. #include <strings.h>
    - f; I: ~/ w% S* y' x% e; l
  6. #include <sys/types.h>
    # O7 ]6 V- C8 V8 E1 R( H
  7. #include <sys/socket.h>; J7 l/ a: n' A5 h$ c
  8. #include <arpa/inet.h># T8 S6 ~0 a4 k9 u3 \0 l" J$ V0 K; |
  9. #include <netinet/in.h>3 W! |) j1 v4 O! Z) N
  10. int main(); p: Z1 P0 L: g% y( }0 ~8 X
  11. {
    * C' M6 D% t: B) Z
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字) ^: ^0 C- G6 T8 k& w
  13.         if (sockfd < 0)
    6 L( ^* C$ N- m5 f
  14.         {; n: a: w  O$ C7 m) ?9 a
  15.                 perror("socket");# y# L2 Z( `$ X! C3 i
  16.                 return -1;
    / }, A) V8 }( j, v. i$ Z' B9 z
  17.         } //创建失败的错误处理8 N& ^$ N! l, c( k) u3 L
  18.          printf("socket..............
    # m( t# a. L  f& p5 }5 _7 K
  19. "); //成功则打印“socket。。。。”, x5 e% D8 S5 ]" W' i% t, F
  20.            m! \/ X6 K; I) m" ]
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    " c& C; L% M8 ~, s) K  H* \
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)) A/ N4 W9 f7 V3 U& r
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型! k) i! x4 `" `( h  p( D
  24.          myaddr.sin_port                 = htons(8888); //选择端口号' l( \, P+ P; o
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址, L( l( G) B) [6 }' ^% p, g
  26. 4 s. ]8 A* N5 Q2 U; d
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    . f: u5 D$ i" t* X
  28.          {
    7 P$ y. W+ @# I+ r
  29.                  perror("bind");0 p, Z9 D' @2 E# B
  30.                  return -1;
    ( z8 ^5 ^- g& p. B- B( i8 M
  31.          }
    ' y" g% x9 }* E6 W
  32.          printf("bind..........
    7 T6 ?9 ^0 b9 f3 g! c" q, S  D
  33. ");% W$ a' ]( h4 S% O  U4 C, I
  34. 7 }9 O' P! t( x# F! y+ i
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    & _+ r& K) X- Y
  36.          {
    3 [; z; l5 D/ o& O2 X! L
  37.                  perror("listen");, O4 T7 v. D% Q, i3 u. Q
  38.                  return -1;: t( v/ A4 l' d$ c/ u. I8 u% V
  39.          }4 e5 P' O7 H3 c( F( g# Z
  40.          printf("listen............
    8 a6 b3 J* W) V  X6 }
  41. ");
    4 \+ k  t4 o  @% F! P7 ^1 Y# H
  42.          : e+ E6 d0 j4 D
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求. _: [1 [5 ~+ f
  44.         if (connfd < 0)6 P- ~) P6 `) e- h5 [: y9 c
  45.         {
    7 c/ h2 M/ a) }. G! H, n6 m
  46.                 perror("accept");( a% A8 r  z' X6 n. O2 Q( o8 Q
  47.                 return -1;2 p1 C* o" M/ w6 @
  48.         }
    & j# R2 `# _) X  B# p9 }
  49.         printf("accept..............5 ]' |/ ]* f% U/ A$ P1 A$ V( L. e
  50. ");
    9 Y( `. [7 G0 y
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    4 y) T' O8 y; B0 j
  52.         int ret;
    ' v" i: g) n7 W/ `
  53.         while (1)
    % o5 a3 L' d) [
  54.         {1 K5 K- X. Z0 }5 y7 s
  55.                 memset(buf, 0, sizeof(buf));
    ; r2 i- g1 B7 k; M4 D8 k
  56.                 ret = read(connfd, buf, sizeof(buf));" L' k$ R9 _  i8 `
  57.                 if (0 > ret)
    . J3 U8 i6 }! W$ r3 R6 Y
  58.                 {! _* C/ [* L  _
  59.                         perror("read");6 n6 W$ Y6 ^$ N8 k
  60.                         break;3 I0 R. z8 v9 H% {
  61.                 }//执行while循环读取数据,当/ X( R' y2 q) V
  62.                 else if (0 == ret)
    $ o# D/ l1 }' @) J+ F' z5 [/ o
  63.                 {1 `! k5 y$ k6 I+ G
  64.                         printf("write close!9 ^# y1 f, A6 Y0 _# w9 w& c
  65. ");
    ! J5 \* \; D( B3 q" p1 c8 V
  66.                         break;
    - y; c  c. ?5 k# `
  67.                 }
      x# G: K$ x2 D9 l  v
  68.                 printf("recv: ");
    7 n- X7 V! R7 p. f; S1 }0 k
  69.                 fputs(buf, stdout);//打印接收到的数据3 Y3 D! M6 z) p4 y: c0 r2 w* }
  70.         }$ }& s8 c% T) K- Z
  71.         close(sockfd);//关闭套接字
    ! u8 X7 S* z4 ~0 S
  72.         close(connfd);//断开连接' C$ z( N0 O& A* Z
  73.         return 0;, F7 u9 w# U; y1 k5 ?7 H- g
  74. }
复制代码

: ^& Y4 s; _5 m1 C" _; g
& _% `2 F+ u6 l
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)4 ]# }! B; q* j. m6 i
  2. #include <stdio.h>
    , V* v- b" l3 p2 F; o2 }4 m2 B
  3. #include <string.h>
    : n  C# J' U, Y: u; B
  4. #include <stdlib.h>) s, ?6 `' D* e) ]' n' f- F  T
  5. #include <strings.h>' G0 O: d7 Z. o/ a
  6. #include <sys/types.h>
    & Q; [2 c# M3 x/ i
  7. #include <sys/socket.h>
    + Z* T; k/ W( D5 J+ q1 Z7 z/ {
  8. #include <netinet/in.h>: C4 h# O$ X# Y! T/ H
  9. #include <arpa/inet.h>
      A. u6 \+ g7 V* J2 ]1 o. u
  10. int main()9 r1 E( I' y7 M) c
  11. {
    $ y. s8 a/ D3 c# A9 f
  12. int sockfd;: N+ t) u  E( b! }0 ?/ U
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))1 q$ {8 B8 D1 g/ n% P
  14.         {; `. {. Q0 {) o1 [" \
  15.                 perror("socket");
    + P' U7 P0 N8 o. m2 p4 i, V1 {
  16.                 return -1;
    - `2 R% P1 C3 ?; `* q- n2 B. m3 ]
  17.         }& T* Z" {  u4 z' ~4 T, K
  18.         printf("socket...........
    1 s" e, F# T8 J
  19. ");) m2 i8 z' Q2 [$ _. A- @
  20.         * Z7 j  T' W( b6 j- O0 S
  21.         struct sockaddr_in srv_addr;2 S/ z7 F9 |4 K) M
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    / Z1 ]! ]$ ], x* P; j7 V
  23.         srv_addr.sin_family                 = AF_INET;
    2 p6 q0 x, O2 X/ X
  24.         srv_addr.sin_port                         = htons(8888);% k7 X0 {: S8 U/ r7 h+ T( w
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ! s, p# W& @2 m& g. @( a, L( h
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    , y- @# c/ X6 Z+ l# z% j+ E
  27.         {& {( v* m  o0 r( @( h9 Z1 y' X
  28.                 perror("connect");6 ^4 d9 @% U5 m2 G- E5 D
  29.                 return -1; //exit //pthread_exit5 [% ^  g/ y/ a
  30.         }2 {8 b! r3 |0 h7 Q7 ^7 S5 d
  31.         printf("connect..............& Q* t+ v# L' N/ m- q* D
  32. ");
    2 M& K1 H+ `- p: ^2 D2 ~
  33.         char buf[100];
    % S; f* w% a5 m% x* G- U; d, @
  34.         int ret;
    # k! \; m) z& W* @
  35.         while (1)# w% M- `: Y! N+ l; L
  36.         {
    ( |# d! @1 v4 r: V9 O1 J2 L1 ]
  37.                 printf("send: ");
    + P' i9 X* O2 m9 J. l7 c9 l
  38.                 fgets(buf, sizeof(buf), stdin);
    / s; m) u1 S7 v2 c& U
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ) v$ \8 a( P$ n" f. S4 C9 _
  40.                 if (ret < 0); ~. p; S5 W4 z1 P9 n- ~# Z
  41.                 {# X4 o) @. F! L; Q* N9 l, E; q
  42.                         perror("write");
    ; E7 U0 c! g( F
  43.                         break;
    / I; p2 d& C* ^( S
  44.                 }
    - R! z3 n3 h& U! t; [& ]
  45.                 if (strncmp(buf, "quit", 4) == 0)6 p4 A+ F6 G( B% B- u4 F0 R
  46.                         break;
    * n) {3 K, t5 W; n. N
  47.         }* z6 z( G, h& d2 P; a
  48.         close(sockfd);
    + {& ~* V" P3 F* x: l4 t
  49.         return 0;, p$ P' j3 n& w8 q" \( D3 F
  50. }
复制代码

: N% i4 }6 ]! i0 T+ Y
' o. \# Y! F) ]+ Y. }# j
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 11:54 , Processed in 0.131279 second(s), 23 queries .

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