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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
# u, A  |& N4 _( @3 ~# S1 M% r/ \$ [" g  c
1 S2 |) e( M) R; f$ V# c" O
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
- V) z# u* H1 D3 o
/ y3 U: G* P5 |( B4 r/ [: Q
1 l7 J, i1 y6 a& H( x
TCP协议6 |6 l1 y3 a; D# _2 L
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。8 p* f$ G* [1 L- X7 D" D
; P4 c0 `( ~. ]! Q% Y" e
2 C  j" E$ Y% Q
关键词:三次握手,可靠,基于字节流。
; C. n$ @: }8 X/ x+ b
0 X( r$ T7 Y% `1 m% s

( _0 }' j  l. f& }( z可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
6 z" f$ ]5 d2 x* J! x
( P, r3 ?' Z; C+ [TCP服务器端和客户端的运行流程
- @% v; a* E# z4 I; }0 S6 s, W1 y% ?3 n5 N
, H4 _9 p, T+ X8 B% T+ y$ ~
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
1 a/ y- S" D% p5 K
4 q4 {& h6 u% X: g" Y
, L- T5 D1 {: W( Q, A; O
1.创建socket3 `. B* f) E- R3 e9 }7 l, w& q
socket是一个结构体,被创建在内核中" M! E1 C4 w6 N4 f0 M) D& D' S
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& i. S2 n( x" x# q* C" [0 G; ]' O8 y9 ~( w9 w6 v+ v

9 v5 g2 H7 h1 @# {  {2.调用bind函数" Q" V+ {( C0 V2 i
将socket和地址(包括ip、port)绑定。7 W7 [3 J- Y$ H0 \
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
, u9 P% P+ T, `" b1 P) m* k struct sockaddr_in myaddr; //地址结构体8 u  k$ T  _4 ?% c+ ?. I; v5 P
bind函数. E3 F+ i9 P7 P  V0 L4 H
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
. p) s' G2 ?2 M+ l/ N; o# [+ U# Z% [# J  Q
9 g8 n/ v6 s7 |( G
3.listen监听,将接收到的客户端连接放入队列! F+ C5 Q, G# c$ H, R9 {# O6 ^
listen(sockfd,8) //第二个参数是队列长度
, n& L  Y0 O, G/ F" @
0 \3 l" E6 A5 x3 u( \8 T& R" U
7 G4 `" q/ o- b
4.调用accept函数,从队列获取请求,返回socket描 述符
0 y6 W/ n: _' I  如果无请求,将会阻塞,直到获得连接
0 P/ Y5 |+ E% y* U* I  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数0 Y2 q) V# o) X% e# {- T7 \

5 _) ^* l( A6 _& h% [/ u

" t% R1 E$ G4 ?3 y& y5.调用read/write进行双向通信
+ _, h6 }# N5 R) `6 B7 V) c- d! Y! J$ P- G% u& W; U
, m- [  z7 @- @4 l5 E) M1 _5 K
6.关闭accept返回的socket
7 Q; h& H% I9 a9 F: V7 W4 B  close(scokfd);
( `4 a& T" {+ W' ?+ S. \# f" e' O. t5 J: ?. Z& v
9 q6 E: i3 V; I$ F( p( N
) o# ~8 q1 W0 X1 }( U5 D
# M! I) ?8 f$ n& s5 ^. w) H
下面放出完整代码
. y6 F' E% V5 z0 n% N/ b* z& U- V$ u. U
  1. /*服务器*/
    3 w* I- h& m% Q( C( D
  2. #include <stdio.h>
    5 S. T+ Y4 }) s* d' {: e
  3. #include <string.h>
    8 Q8 R" x* U& q! l
  4. #include <stdlib.h>4 X3 _* y3 g# `# G/ b8 V+ g/ i) m$ |
  5. #include <strings.h>8 Q( B( C. S5 T; B% h" y* Z# w
  6. #include <sys/types.h>5 R2 y$ w, R3 p7 m9 y+ X
  7. #include <sys/socket.h>
    6 I8 W: c9 D, |1 U9 _. }
  8. #include <arpa/inet.h>% t8 F/ x4 ~; A9 Z+ }" T! y
  9. #include <netinet/in.h>8 L8 s8 ]- M& O/ B3 W0 g# Z4 m
  10. int main()
    7 a- J6 ?$ Z# T7 ~  e0 A( x
  11. {
    2 t$ M) H) J0 _7 K2 T0 X& m
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ' b8 f6 Q8 c# J3 |
  13.         if (sockfd < 0)9 F0 R9 Z0 t9 H9 _2 ]) i
  14.         {0 q& j7 o# g& E2 |. ?
  15.                 perror("socket");
    1 t; f3 H8 Q$ O# H! K# p  c) ~
  16.                 return -1;& m8 ]/ G' K$ E7 U
  17.         } //创建失败的错误处理# Y- _$ M; h6 g% R$ |5 ]- G) o
  18.          printf("socket..............
    / }* ]% t  }$ S, `  h& v3 m
  19. "); //成功则打印“socket。。。。”7 V+ G; e: F/ i: l8 `8 _8 ~  g9 K
  20.          
      r* h( Z  v5 k- q
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    , }6 X$ o! {+ k1 x$ q# D
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)( e8 z, W$ I' K6 n
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    2 i4 ?, ?. `; F( Z& ~3 s( [# T
  24.          myaddr.sin_port                 = htons(8888); //选择端口号+ U3 b- g1 q& U3 l+ H
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址( r8 O6 u# @  v9 e9 A3 o: r

  26. 0 {: m) C$ _) @( [: l' I
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字/ A' T, Q( a7 o! g4 e# \  M, Y
  28.          {
    6 K- t/ s# z; L7 X- c% E
  29.                  perror("bind");
    " f7 x) h% O, w7 T2 \1 u. G
  30.                  return -1;
    % R, q& O# v, _% F
  31.          }
    - X6 N7 l* ]( e3 f
  32.          printf("bind..........3 X7 p% ~" f9 Z4 p, E4 h
  33. ");
    5 H3 _; g3 @1 \  b! M2 G/ h6 N2 k3 _
  34. . z" ?4 ^% V4 r3 t/ g: t9 K
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    , g  ?7 S" t2 _6 r- y% ?. \
  36.          {5 A8 c# \  ~; Q) _) N
  37.                  perror("listen");
    * o: m! w$ f3 h: T9 b6 J3 d  L! k
  38.                  return -1;& X  a1 q2 h% P, `
  39.          }
    7 s% G3 `1 o6 @- r1 M7 m( r
  40.          printf("listen............' |$ }: L. w+ m* o/ n! Z# v
  41. ");
    $ i4 H) G+ T: X& k* K( A8 l& M
  42.          0 `2 @2 s6 j' i/ N2 n  [
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求) N. C/ Y/ f% ~0 ]0 U- a6 f
  44.         if (connfd < 0)4 x; k3 F5 H) b- ?3 C) J
  45.         {' T% A  v! i: o5 `+ F) C5 h, I8 I' h
  46.                 perror("accept");
    * J5 Q) O+ l- ]8 r' ~7 f; O
  47.                 return -1;8 a$ s6 n: f8 S% E' j4 b, P
  48.         }% F7 O" f9 r. f( V1 u  [( R
  49.         printf("accept..............
    ! H! {0 C& ]8 k, t5 Y2 d
  50. ");
    . w$ U2 C  K2 c* P" G
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    " o$ e) s  z8 x+ |0 p6 z
  52.         int ret;" z) H5 C! b6 e( ~8 A' c2 I
  53.         while (1). p9 g4 x3 D9 P+ Q/ L
  54.         {
    % p# h8 w+ v, t8 k
  55.                 memset(buf, 0, sizeof(buf));+ ~) x' v% s8 _8 u+ h# |6 z
  56.                 ret = read(connfd, buf, sizeof(buf));1 Z) L* S' G+ E1 t( L
  57.                 if (0 > ret)
    , ?6 M5 x  n, I( ?+ G
  58.                 {
    ; U+ [9 o. P% A
  59.                         perror("read");
    4 a! j/ r9 s/ }9 s( C3 Q
  60.                         break;" E% l: S8 n; P% p9 x
  61.                 }//执行while循环读取数据,当
    2 j& }: j5 i  z# Q
  62.                 else if (0 == ret)
    / B- m: J+ ?1 h& y( s) E% j# D) _
  63.                 {
    * u) `# W; ?+ d( H! `6 j  r
  64.                         printf("write close!( m1 }; z  j: I
  65. ");
    ) F) A$ m: a& n: z
  66.                         break;8 p0 C$ c: Y7 m2 d0 i7 f8 U
  67.                 }
    # P3 P+ |9 l% B+ t1 f5 l/ _
  68.                 printf("recv: ");
    2 R& L& C! N' b! n9 B
  69.                 fputs(buf, stdout);//打印接收到的数据
    % j- i' m0 Q$ U' f
  70.         }0 {9 u* G6 p6 W% ^
  71.         close(sockfd);//关闭套接字
    ) ~$ V+ p; `0 w  M! L% p# w! y
  72.         close(connfd);//断开连接
    , ~. e) s4 D$ J2 ]. [% N! q
  73.         return 0;9 V, R0 u9 h! ~+ K# G  D/ B
  74. }
复制代码

" O! v8 ?% o$ p% x
( f" E  W$ a# v. V" u
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    : ]9 o# k, p6 U* A$ U2 N/ N/ w
  2. #include <stdio.h>2 C7 f1 l" N0 @
  3. #include <string.h>
    5 j; L$ x2 t! z! X: {- U
  4. #include <stdlib.h>
    + m) Z% p- p; O! n% S
  5. #include <strings.h>+ k6 |9 Y" \# j' r( k; h
  6. #include <sys/types.h>
    0 b2 |: W8 R6 a  E. ^
  7. #include <sys/socket.h>
    4 a8 x: T* T! m* f: M$ E
  8. #include <netinet/in.h>6 B; f. t% d) J3 `! S: G
  9. #include <arpa/inet.h>. Q9 D0 V8 i" V; D3 A/ j
  10. int main()
    " V. I6 F% C' P! m# L$ @
  11. {
    / u% ]# n2 _/ u6 J/ K
  12. int sockfd;/ n3 l5 \' p/ {) [
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))2 ?' Q" V/ C; Z1 f! Y( N/ K
  14.         {
    ; u7 T) N) S/ y9 }  p1 u
  15.                 perror("socket");) |5 s- X; u+ W, g
  16.                 return -1;( j, `+ X/ T9 K# e& M; S& ]
  17.         }
    . d5 o( a7 |  A: R
  18.         printf("socket...........
    , u& C) u/ q6 ^- u7 G( {( j
  19. ");& g5 }2 q' |8 V) G5 G2 N4 T, y% S8 B, W
  20.         ! E9 f$ [. a) s8 l! r
  21.         struct sockaddr_in srv_addr;0 E$ V4 @% h, |4 n: y. l
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    : j& ]4 V( \5 z! c  p' Q8 K" C) Z4 {3 f
  23.         srv_addr.sin_family                 = AF_INET;/ s3 j: k& ~8 W3 i7 R
  24.         srv_addr.sin_port                         = htons(8888);- |5 {0 o4 U# ?( S4 L7 h
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    3 f( i  e6 c8 e2 w
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))0 R( x9 \( e: ^& u5 r$ O
  27.         {1 Q& L* }/ j$ O7 y5 x5 Q$ v
  28.                 perror("connect");
    8 M% @; Q+ v( s2 f% g) T
  29.                 return -1; //exit //pthread_exit8 Y  J5 D  r' z. K) W* Z* m9 S
  30.         }, n. E( x% S2 c. v( B
  31.         printf("connect..............# k/ E# Y! N8 m# d9 w
  32. ");
    2 m( z% v0 L8 ~6 Q
  33.         char buf[100];. Y' `* N1 w! s5 }/ \7 X
  34.         int ret;
    9 ]+ u9 R; l! l* G
  35.         while (1)' U9 e2 f0 Z- Y. H5 M' ?# }7 V
  36.         {
    ) I" I2 I! l8 i$ }& R7 o1 B& _' e
  37.                 printf("send: ");4 [9 Z/ g! O/ C/ V6 D& r5 }
  38.                 fgets(buf, sizeof(buf), stdin);
    ' h3 [5 p: H4 W' K" }& {9 i0 n' D
  39.                 ret = write(sockfd, buf, sizeof(buf));
    $ m  g# @$ v8 b) Y
  40.                 if (ret < 0)
    , X. c$ L4 J  m; ^" r, x# j7 Y
  41.                 {8 P7 f0 N7 i# s7 e2 d* S7 u1 I( M0 ^: v
  42.                         perror("write");) G" X0 i* Y7 h: g" C5 X* }# O
  43.                         break;% N8 o8 p* _" y7 E
  44.                 }% h' o" P! G' P9 p$ p; X: B1 I
  45.                 if (strncmp(buf, "quit", 4) == 0)
    5 A; {1 k" B% b4 _; ]! j
  46.                         break;- D! e) p7 _; C' S: c
  47.         }
    1 i9 }1 E  A/ k' k6 T; [7 b
  48.         close(sockfd);
    + k+ E8 N1 [' \1 L0 R1 ?4 }5 N- \
  49.         return 0;
    * H5 H7 ^) c& D& X5 V& O
  50. }
复制代码
' ^. \. v- A7 N1 }0 a2 t' [5 W
- O. Z$ p( ?  H! |4 ]8 |! J
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-8-4 08:13 , Processed in 0.060700 second(s), 25 queries .

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