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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 显示全部楼层 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。+ b% w+ V6 L3 }8 j

/ }1 X. o3 W) h) m- n
0 c4 U, }1 c: }' J( m
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。& E$ [( y& H" S% D7 `6 C
0 f1 I6 q) q5 t9 R
  Y! d, S! X# \8 ^; I! i$ A  o. J  c
TCP协议
2 ^: r- t+ d$ k) E: O7 O( f% aTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 M; P" B+ S- h0 _% v
; I3 \  F5 K7 T( n, q
5 [5 W8 H8 }$ r6 \9 e2 R9 H
关键词:三次握手,可靠,基于字节流。/ N7 e& v* t; T: J- U$ H9 t- Q7 n
' }, W: Z# x1 f0 t* b

& s7 S' b# g0 o1 @# r8 y# w0 B可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。$ U! |* X% p8 J" e+ ~

1 w* `7 f2 e+ I; ]1 n; x4 ATCP服务器端和客户端的运行流程
- n6 c3 G9 m% \+ J' {& o" h* d! Q$ w9 `

: |! ?  V& r' t) B4 ?. E  R, E如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?: ?; c/ G7 F, M+ i  X

) K, w( }: X/ q
. [2 o- w7 H8 ^( m9 ?
1.创建socket
% c- V( I* K2 p- h1 ~1 m' } socket是一个结构体,被创建在内核中  x9 F7 b& N8 E1 |  M$ h$ b/ H) B, y% q
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议0 R- G* u) i% I2 O; N

3 n; W7 Y0 x$ S

  P1 i* [& |3 ^+ o1 d6 N2.调用bind函数0 W4 ?% F" H$ |( a- u
将socket和地址(包括ip、port)绑定。1 @0 d" D0 l( e' D% V
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
+ h3 U/ J5 K. {% e4 V; a0 y struct sockaddr_in myaddr; //地址结构体
2 b' P# y: O- P, X; e  A bind函数: r2 ?! Q) V# a2 H1 c  e" K( l
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
: Y( W  O- G  k4 t0 S$ C
3 ~! _# d: o/ I- _3 V7 T6 A
, N  @! |: h. v3 |/ x
3.listen监听,将接收到的客户端连接放入队列; f  ]( C0 x) G: v! G/ `
listen(sockfd,8) //第二个参数是队列长度3 s& W( l0 r, j. z

* i, {( m. V1 y2 Q
6 ~" l* {7 X2 w7 }7 }/ s9 w
4.调用accept函数,从队列获取请求,返回socket描 述符' M4 x9 y8 d1 a5 V# n3 }' h
  如果无请求,将会阻塞,直到获得连接% V9 A6 U" a; _* G9 n5 Y  U0 l
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
9 m" q4 t& x5 c8 a7 A' c4 }, ], S) ?- E/ w1 }) d

5 D# }( z# n( ]$ Y3 ^5.调用read/write进行双向通信. r* Q0 ]4 A; z# w8 p) Z, A

4 t9 T) _. d5 P" I$ o( ?
6 |/ a; P# Q1 G2 T& x  x6 x
6.关闭accept返回的socket
; p- F0 T- q4 P1 ?/ S* H  close(scokfd);
0 d: L: ~! ^7 c* F# z* Z6 o
' S+ M5 X4 \. H

! f* V4 d6 J( P/ x& |6 d8 ]
7 {' J$ z' a" C( L* g9 h$ v
% I  B. L; `3 u
下面放出完整代码/ l: t9 d# w# }8 [

- N7 S& G  H2 v; `
  1. /*服务器*/
    ( _5 u' o% B: `4 R- n
  2. #include <stdio.h>
    / K( S$ q" k& F: u  F. n
  3. #include <string.h>* D8 H( U' m. q
  4. #include <stdlib.h>- C3 v5 A' k% p! ?0 O6 k8 f" ?) T$ }3 D
  5. #include <strings.h>! N# ?: t  ^' m* G
  6. #include <sys/types.h>
    , J! L8 z, M/ t1 [3 S! t  ?
  7. #include <sys/socket.h>- K' n6 `( U4 L; D1 i
  8. #include <arpa/inet.h>& A6 {6 M, v  V& j, n$ A) n# m+ T
  9. #include <netinet/in.h>% M2 K$ c' I3 y8 D' |7 R8 J+ D6 ?
  10. int main(): }4 l( b3 g3 }
  11. {6 B( @+ l9 h4 O: O
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
      U( t+ B& m" }" a8 C' A! X
  13.         if (sockfd < 0)# ?7 r9 t  e" O1 |
  14.         {. y* q) w1 b1 F9 X  W. E2 ~: e# V
  15.                 perror("socket");1 m+ m7 i( Y# o. j
  16.                 return -1;. N6 @% m9 S& A- a2 i
  17.         } //创建失败的错误处理
      A4 o# @# b; D: A8 W7 o5 G$ `! A
  18.          printf("socket..............- Z4 {2 @7 ~. l
  19. "); //成功则打印“socket。。。。”! d0 l) P' n: ^& X1 g
  20.          " Z9 {* ~- G( ^* C5 c/ V% V
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    2 x/ X0 ^3 Q7 Z) h6 _" R
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    , A8 L' h# \: A8 K; Y8 D
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    & V! t  N+ M9 Q
  24.          myaddr.sin_port                 = htons(8888); //选择端口号* j# Y+ c( F0 f4 _$ f0 b% X/ _
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址6 a, q5 b+ W' U! o; E
  26. 6 p$ ?) {, s5 C% X, W: F8 w  K
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ! J" p& g: m* ~4 {* H: o
  28.          {
    & m+ F% {! [9 I$ r3 o+ z$ |
  29.                  perror("bind");( T! p' f  o* f* n3 e# d
  30.                  return -1;
    $ }& g/ p6 b! |# R7 r2 P3 f! I
  31.          }
    ( G/ G1 o3 ?6 M# {% O6 S) w
  32.          printf("bind..........
    8 A  F0 s, ~% E5 y4 Q$ s5 b* J3 {
  33. ");
    9 e9 [% z- R  H
  34. . \) D8 F9 J, M7 C; [
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    : I- v0 A( K8 J1 I% c
  36.          {( A2 \1 b% g+ H3 n0 Q
  37.                  perror("listen");
    & x; p9 Y. m! u
  38.                  return -1;- ?& f9 B; t* x! |2 }7 l! S# O
  39.          }
    2 i5 |: e# L& U; m
  40.          printf("listen............
    / t, ^% v. ^6 @9 t  N5 M% N
  41. ");
      K, U" R4 M* T  H3 E9 J, `; P" x
  42.          " W8 i: l, P1 \. o1 r
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    , A& q1 y$ c! F$ e3 @0 d6 ^
  44.         if (connfd < 0)1 d6 h+ M" m# W6 z4 h( }7 ?6 `7 t
  45.         {9 t' R! G5 `. i
  46.                 perror("accept");
    % z! g3 @6 M5 d. [, x2 j
  47.                 return -1;
    2 f1 F9 v/ J8 b% {# M/ \
  48.         }% J$ V" i- y" t
  49.         printf("accept..............
    & p+ ~8 e5 H. C+ |
  50. ");; C! {" G, t& T" M$ l8 A
  51.         char buf[100];//定义一个数组用来存储接收到的数据9 v  W2 V7 m% e' x
  52.         int ret;
    ) O' V+ ~- K! u3 l8 g
  53.         while (1)
    : y0 Y& {1 Q( y+ F
  54.         {
    8 u5 {: Y  Y6 m
  55.                 memset(buf, 0, sizeof(buf));3 D8 p" ^9 Z4 o4 J! K/ p5 R! V
  56.                 ret = read(connfd, buf, sizeof(buf));
    ) W7 C# f* T, }' ]) w
  57.                 if (0 > ret)
      Z- \. J! L0 @( o8 W. z8 ^
  58.                 {
    0 O6 J& p  r1 F" I0 M
  59.                         perror("read");
    : Z, L' [  e4 d) Q
  60.                         break;
    ; z4 U. B7 S: W3 C$ I2 i& p! I
  61.                 }//执行while循环读取数据,当. r( B( y( q. N2 o( _5 B
  62.                 else if (0 == ret)4 F. W. N+ L4 ^- i$ k
  63.                 {3 l$ [" Z; g$ @, y2 f
  64.                         printf("write close!
    6 X) F2 Z2 W7 H* y* E
  65. ");
      E2 n* I" O. |& u1 k9 [
  66.                         break;4 Q9 v3 M! ^% j
  67.                 }
    , X- X. n6 A2 |/ N, E) [* R" m
  68.                 printf("recv: ");
    9 ]) `. c8 b4 [7 n/ w4 l
  69.                 fputs(buf, stdout);//打印接收到的数据
    ! A, ]9 d: j; p/ e. O* J
  70.         }
    , k4 X- S1 D# `) Z/ `
  71.         close(sockfd);//关闭套接字
    ) \  K! R( N2 [- m
  72.         close(connfd);//断开连接+ r0 X5 B- K$ K! o
  73.         return 0;
    5 o& x! A# ^3 T- U- K
  74. }
复制代码

$ ]5 \2 {! a  y0 ^+ U  w& h& e! v- |: S, \$ Z1 Q
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)& w% l( S( F, x2 s' ?
  2. #include <stdio.h># t0 X( K+ }6 {. D2 n% W, d/ f3 P
  3. #include <string.h>5 i# D4 N7 }1 l, z/ I* H4 U
  4. #include <stdlib.h>
    7 n9 z+ [" C% ?7 R6 H. w# D, z
  5. #include <strings.h>/ Y& e5 p$ s6 [9 U' }: W* u% n( \2 F
  6. #include <sys/types.h>4 y$ B( T4 Q: d
  7. #include <sys/socket.h>8 h" B" N7 l2 A, t  M
  8. #include <netinet/in.h>7 q6 ?2 \' c2 x; X
  9. #include <arpa/inet.h>
    0 f2 u$ G, x7 `2 ]6 L1 n7 w8 ^, `9 L" L
  10. int main()
    , s7 S: p6 B. [, R9 m
  11. {' l. r- t$ F( k2 A0 i" r, N
  12. int sockfd;
    ' |( V+ [$ N8 G* |1 W& ~; Z6 \
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))' r- K: B, N0 ^  T7 K" A  N
  14.         {
    7 U3 b# S( q* W2 d* u( C; m
  15.                 perror("socket");2 ?& _/ L# r: ~  C2 I4 O3 Z5 r. T
  16.                 return -1;. o6 m" H/ i  M& }! O
  17.         }
    8 z5 \  V% e. b# j4 ^
  18.         printf("socket...........
    7 I9 Q' O7 ^6 T( h" J. p0 A, q
  19. ");
    . ?, H* i. L: f5 A% B
  20.         
    6 L9 c) V; r: Y6 m' i3 k
  21.         struct sockaddr_in srv_addr;
    ( S" I" H; C5 t
  22.         memset(&srv_addr, 0, sizeof(srv_addr));0 T' z9 _6 A9 I, N
  23.         srv_addr.sin_family                 = AF_INET;
    4 |% f/ E( i4 u& i3 t' x
  24.         srv_addr.sin_port                         = htons(8888);
    " p# F# p. C  i9 V! R& l( H
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");! ~6 |, p0 f0 Z' b
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    9 x" P9 d6 y3 a. Y' S, J1 T
  27.         {# E2 o5 B: z) b( t5 k: D
  28.                 perror("connect");
    , A7 g. Z: V4 V- S! c4 @
  29.                 return -1; //exit //pthread_exit
    5 ]) k- A7 e5 A2 g" W( }! x5 l
  30.         }$ A1 x6 J: s1 @; J5 a; G" m
  31.         printf("connect..............' |! `, f$ H$ L; Q
  32. ");
    9 a7 m7 s2 C( r$ g
  33.         char buf[100];& K; q7 t( c$ \8 R3 M' k% f
  34.         int ret;$ b" ?+ k& @  J3 _
  35.         while (1)
    . G2 a3 [. D) _! A3 v- H5 H
  36.         {0 F4 v6 E" j5 K/ X6 |8 c0 G- }
  37.                 printf("send: ");' I! w- p3 b6 l3 z; b
  38.                 fgets(buf, sizeof(buf), stdin);: ?  i) g; F& }' @3 q8 D
  39.                 ret = write(sockfd, buf, sizeof(buf));% W, D) i, T1 W1 t" U
  40.                 if (ret < 0)
    , Y; s) D; Y( X0 ?' Z
  41.                 {# y6 ]* G6 E  U
  42.                         perror("write");" @# [4 s; c. N* U  Y
  43.                         break;
    ( s4 \5 Z1 j" N2 h9 \( i9 p
  44.                 }
    8 I% n: L( o! \' H# J6 ?' {
  45.                 if (strncmp(buf, "quit", 4) == 0)
    : i4 m) I$ H( m1 D: C0 k! w4 l
  46.                         break;
    1 C4 h( a- ?, s7 D$ X
  47.         }
    3 l3 S3 p1 K% K. V1 u3 O
  48.         close(sockfd);
    $ e! ^# `7 N. }1 p4 J- Q6 J! g
  49.         return 0;% H  v4 ^5 Q  q
  50. }
复制代码
7 ~* |9 ]  Y1 j! Y7 Y& T2 \

1 t2 [- I: f$ s6 g1 H  w  D1 x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-7 16:08 , Processed in 0.153444 second(s), 22 queries .

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