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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
- i" r  h  u2 R, c4 S0 j8 r( b9 {0 ^/ A% [1 S7 s7 {+ |/ H4 V

0 y- l* \0 S$ u4 d  r7 Lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。9 U4 t7 C3 A7 M3 E
+ c5 {' J/ D3 j2 W4 M" Y" m8 h6 O

( t! J& Y" M* r$ ]9 s: q! oTCP协议8 W$ w4 \' N2 _
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
8 d. V6 B+ e! ?' k7 n$ \1 k) ^! v$ q. ^# f- [+ w
, ~; b  X2 g% z3 g4 S
关键词:三次握手,可靠,基于字节流。
& Q, C8 y& J3 }: ]" }: N( i) t" m# ?" t' ?- G
. S0 X% ^5 U) k
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。0 o( N3 g6 C" E. `1 x7 O
% t& t7 e% e! @3 Z4 x4 p% }5 |
TCP服务器端和客户端的运行流程4 Z' e" K4 F' C1 t* J$ k

' q! r& ~% ^( R4 m- |

! s8 F) |" O7 h* m8 z6 Z如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?, y" w$ ?& n/ h, L% f4 n
  s3 w4 q0 Z: o1 y$ ?. s

& ], v' f* y4 y* E3 p& s/ a( B1.创建socket% J4 ~% d8 E% p
socket是一个结构体,被创建在内核中
! ~. U% M  {  b6 d* e sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议8 S% z. a( ]& Z6 f# \

* P% v! L7 d2 V5 V  f3 G3 C
+ U, {% ~4 P, y$ ?- M' ~7 `# H
2.调用bind函数
( Y* X% m1 R+ ^. m3 Q. H 将socket和地址(包括ip、port)绑定。( Q* N8 D, [: H9 R: u, S; y
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序: E6 O+ L0 @: |0 }( I
struct sockaddr_in myaddr; //地址结构体
  m1 `  R1 }8 i! `, U4 K1 c1 d bind函数
3 N, [& f, Q) S9 P bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))5 Z0 a5 S$ e, U% z: o7 Q

5 T5 o/ Z" B! {" }3 x

1 H% q  O" x& {: e3.listen监听,将接收到的客户端连接放入队列
% f2 a$ Z9 ?+ @ listen(sockfd,8) //第二个参数是队列长度
' t9 S: f2 W- a' n/ R' {. a& d3 \$ |+ c* z5 ^+ m% f
3 R- ]3 R& x5 k
4.调用accept函数,从队列获取请求,返回socket描 述符
4 ]6 D+ U$ T6 @1 k0 D2 ~  如果无请求,将会阻塞,直到获得连接
" V1 |1 l' P, Y/ I. Q  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数$ @+ V' G4 q# h( M

2 }# F* |) ?( y* @9 |
& A# T% u  Z% P, i) h" _0 z
5.调用read/write进行双向通信
8 j4 l4 l/ ^8 {) X6 L; H  {/ C8 U7 Y( e( x2 C0 Y

8 }! j) {3 t. K7 Z8 R9 Z0 B6.关闭accept返回的socket
2 m$ N$ ^% L7 k4 c  c; K4 {  close(scokfd);7 Q" T9 \8 I  @: v9 T0 F

' h7 z/ Q0 f7 J+ q+ a
' M5 f: G4 J2 @: ~2 L! c( Q; b
7 O. t% b! P' w* G0 J) k

, q. z$ u1 R7 t下面放出完整代码% o% g1 C- ]" L& i

, e1 {" M, ?. |8 G
  1. /*服务器*/
    ! J! `% d8 p9 f8 \$ L( ]5 y5 ?
  2. #include <stdio.h>
    : o6 R. g0 S$ S) K8 w, i
  3. #include <string.h>. t" P( y# g' R* W* ^
  4. #include <stdlib.h>
    / q5 I4 m8 G: A$ J4 a6 @  g" X' \
  5. #include <strings.h>; M( P3 x; {, g! ?5 Z
  6. #include <sys/types.h>/ G$ P4 R* A- S# ?! e6 {( Y* u9 D
  7. #include <sys/socket.h>
    % W1 Q: l5 }* h6 p; }* o/ C
  8. #include <arpa/inet.h>
    # |) A, G) R, Y/ t. n9 e
  9. #include <netinet/in.h>
    # e! X' ~7 a0 L0 e* E1 k9 O
  10. int main()
    4 N0 D, A- T( J/ N- r+ z+ o# {
  11. {* E" K0 e  J: [
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    4 V1 n2 c+ {7 Y: Q
  13.         if (sockfd < 0)* x5 {, p: R8 g
  14.         {
    1 n9 V& J- P6 v& B
  15.                 perror("socket");9 P5 p  y5 H6 \
  16.                 return -1;
    & D+ }5 s: `9 c, q8 V, q
  17.         } //创建失败的错误处理! W6 p  Z* {: s* a& O. U
  18.          printf("socket..............
      M% r' {, t$ k9 ]
  19. "); //成功则打印“socket。。。。”3 ]) I3 S" x; A' F
  20.          ( ?$ U, g. S  s9 h* E) M
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体$ G3 P- a8 L$ Y. j. e
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)" d4 |1 ?5 j. N/ l$ e4 A2 V
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型" ]! E" j( ]) n+ J5 l1 ?0 N% d
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
      S% u1 y  m+ m1 N0 A3 g0 F: x
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    2 q' A- P2 S9 \* Y5 c( T1 ~
  26. 7 R) t- a; d# Y8 |" r
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字+ z' I& X8 l" `7 i, s' R
  28.          {1 S; a& \! e  Y" i4 {
  29.                  perror("bind");
    , O' h& V, A* H% `9 T
  30.                  return -1;
    % N- i2 @  M! K3 P
  31.          }
    ) a6 {, Q) t9 B8 H5 R9 J; ~! p4 m2 f4 c
  32.          printf("bind..........( l! |' B4 a5 i9 Q
  33. ");
    , P! `! Z" l7 Q& K0 K$ ~  H
  34.   X8 I4 t" d5 O
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听, R* y% G7 M0 r, u8 d
  36.          {
    - V3 C) c; o. x- P0 S( s+ |' S
  37.                  perror("listen");
    ) g! V4 p  F) W2 [
  38.                  return -1;
    ' m. z) p# P$ x1 ?
  39.          }# R7 [! ]& R) L& F- y
  40.          printf("listen............
      e8 i3 i; b2 _  d1 _* G
  41. ");
    6 G/ P6 q( U& g' c
  42.          
    : G' \3 u2 x" l1 a
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    $ w( I) x7 h6 r
  44.         if (connfd < 0); H- X! S' B- ^+ ~7 l: Z9 P
  45.         {
    & P4 Z; ~$ r: L9 B* C
  46.                 perror("accept");& S+ v* p- k8 O( |2 {
  47.                 return -1;
    . D& u7 O7 o$ U! j' Q
  48.         }
    3 s8 f/ w" b( N; [
  49.         printf("accept..............0 y" {/ |5 [* U5 |$ Q* V( a5 D! o
  50. ");7 K7 z8 R. x. l+ C; ?3 K7 _/ i8 C
  51.         char buf[100];//定义一个数组用来存储接收到的数据. Q0 `* e$ Q# d1 X0 q) U. ]2 Y
  52.         int ret;5 Y$ m: M5 X$ \! ^% S: @
  53.         while (1)' o& A; q0 k& ^- p! }0 {
  54.         {& L! p2 n* `4 I) e9 i! A
  55.                 memset(buf, 0, sizeof(buf));9 h% Z5 X3 O5 e4 }
  56.                 ret = read(connfd, buf, sizeof(buf));
    ' c5 g" e' _. O) k; `% ~3 {
  57.                 if (0 > ret)
    & j3 u4 o6 s' r3 w5 _2 n; r
  58.                 {
    # }  V) |, m- V5 |6 Z7 d# D
  59.                         perror("read");
    $ c7 F' s  _. E" @1 P0 E5 o5 P
  60.                         break;
    . g7 k  r( A" x- U9 ^3 n
  61.                 }//执行while循环读取数据,当) G, t2 C, N' \/ z* Z
  62.                 else if (0 == ret)* e$ @: c1 _' m3 `; B  L" a
  63.                 {8 O2 U! @2 H. Q2 Z& s7 M6 R' e+ y
  64.                         printf("write close!
      P& r1 D" H3 ~3 |) ]; G
  65. ");
    * `- m% x$ Q  c! p7 O
  66.                         break;: T& X& v" v- X( W9 z% r
  67.                 }
    5 M" H$ J' F# S
  68.                 printf("recv: ");# N* V( b4 y$ k) D3 X9 [
  69.                 fputs(buf, stdout);//打印接收到的数据. _  r* F% J' ^6 V2 @% z8 m: v
  70.         }
    3 a3 T* @0 w5 _% T0 o1 J; {9 |
  71.         close(sockfd);//关闭套接字
    " z* Z- A' a# r$ b/ C
  72.         close(connfd);//断开连接6 X, }" n; C& d/ f
  73.         return 0;
    / c% u3 ?  u: |& \
  74. }
复制代码
  P# p" e3 R( O0 ^

( p2 u! T( \2 b6 o4 Y
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释): T) ?$ C5 M. ~
  2. #include <stdio.h>
    9 [: s* ]* p+ |. X7 F& d
  3. #include <string.h>$ \  T' h* o' x" X+ r1 o
  4. #include <stdlib.h>
    6 O" E# h4 L# o8 ]" S4 W" i; w
  5. #include <strings.h>! ?4 t2 J" G) z
  6. #include <sys/types.h>
    % S7 q4 ~/ d. ~, |& N, t6 t
  7. #include <sys/socket.h>
    7 r; c4 p- U$ ?" l6 x3 {; {
  8. #include <netinet/in.h>
      l, y- e: b' y( i
  9. #include <arpa/inet.h>
    ) e. I7 j. D5 W8 E+ H& B  t8 [' x
  10. int main()
    3 t) k' h2 k$ L/ E0 u0 a
  11. {
    1 X; x5 P2 M6 [  ^
  12. int sockfd;/ E- q) V* h8 e& x5 t8 }* E
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    : P3 n; [1 q$ t" q) s$ s
  14.         {
      G$ _7 V- I; P% [- o6 r; i5 G5 A
  15.                 perror("socket");7 F; o" R, ~  D2 i
  16.                 return -1;
    + F6 A/ E9 T9 c! Y( T/ k
  17.         }( X7 o2 l, F" c' O6 X
  18.         printf("socket...........0 o/ b$ ~6 |8 G, I+ k, ?
  19. ");
    ( s0 _1 N% z% f% Q: G
  20.         1 T& \6 X2 E$ [$ r2 T3 T) T/ Y9 w& a6 _
  21.         struct sockaddr_in srv_addr;
    * Q( j4 ~- |+ c. M4 i# e; p
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    # c* X% }3 V! m$ A
  23.         srv_addr.sin_family                 = AF_INET;
    ) y% O, S  u0 Q9 j9 M; `
  24.         srv_addr.sin_port                         = htons(8888);4 U7 ~8 \; M0 q
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");5 J0 n& A4 j5 G0 b2 {' z, G2 D  ~& ~
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    : G+ m0 @8 Q: o: e4 _+ L
  27.         {3 V4 Q1 O- B+ w$ X
  28.                 perror("connect");. Z5 J1 J/ @) Y- G9 I& r
  29.                 return -1; //exit //pthread_exit
    # Q* k( j3 O. J5 f/ T' s
  30.         }& t% l* T* N0 F: T: w
  31.         printf("connect..............
    0 m4 ]5 t5 [6 \, c1 M
  32. ");; v+ w4 `5 k, E4 H& L7 b) R
  33.         char buf[100];) w! y1 U( ?, }4 q
  34.         int ret;. z( N' J4 u5 K6 W  D
  35.         while (1)
    # b" E9 w- w! j, X
  36.         {: k/ M' D% Z/ N/ d& f% O: `
  37.                 printf("send: ");7 ]8 C, c9 C! j
  38.                 fgets(buf, sizeof(buf), stdin);, ?. ?- D& b6 \9 m' q) n
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ) d8 ]1 a2 B" b
  40.                 if (ret < 0)6 v; c" @  ]3 D: w, n9 z
  41.                 {5 x' g2 t$ v" D! U
  42.                         perror("write");
    & s' ^9 B  D- Q/ S9 i8 q
  43.                         break;
    " J8 _) v" g1 n& n+ L
  44.                 }/ O+ L0 M0 c8 V; J
  45.                 if (strncmp(buf, "quit", 4) == 0)
    9 s9 J' g2 ]; l% t' Z
  46.                         break;
    . ]" v' J$ S# @6 V& i( X; e
  47.         }
    ) Q% I$ p9 e" @* t! c8 A
  48.         close(sockfd);; C+ I8 H& F/ n3 J9 B4 }7 p
  49.         return 0;
    5 t. r$ Q# [/ n" F
  50. }
复制代码
2 O$ _/ V. G4 d3 a+ N8 m

, K& }# a7 Q/ e) z! P& j  ^
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 01:16 , Processed in 0.120210 second(s), 22 queries .

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