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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

# K& v' ^% G9 f7 K' ^, _

; r% I7 W# ]) Bsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。1 W% w  R1 _" c6 l5 W, G
2 |& L& }4 o+ B( J' |/ B( Q2 o4 b
+ `% c" _" `2 J( Q' y! x2 C9 ~, M7 D
TCP协议
; u4 o  k! `  ~/ x6 e- K# G# aTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。' r; V( ^, l8 \% C6 k( E" c+ B

5 P- J4 K, ~1 L9 I
, _& @: E2 i  N# _
关键词:三次握手,可靠,基于字节流。
* f4 i: h* W5 H2 n" \9 B
' {! W0 ~: c1 o: g, H

1 }: L* G6 w$ {1 g4 F! k+ q/ A可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
# a9 n$ e, p; K- c0 ? 8 O: b. m3 a/ f, z& r0 o
TCP服务器端和客户端的运行流程7 D; v# w8 O, c% E

$ {4 `, [+ V9 e
" \3 g. y1 s, L
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ V0 q% u) ]6 {- B* |* D' Z3 w0 q* w5 ]6 d' O
9 Z/ C% `4 F0 H5 g% h# u7 l
1.创建socket" g0 X% j* J9 ?
socket是一个结构体,被创建在内核中
+ m' Z5 r$ ?8 A+ x7 ^. { sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议4 W+ c; g4 y% \, R+ K) Q) ]

, j# H& x/ d/ @% y, |2 u# [
4 ^$ B  p/ \2 V  f* l0 ~9 n
2.调用bind函数$ s! \3 W$ J( E: E
将socket和地址(包括ip、port)绑定。
3 l& {2 p6 l7 ~- A 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序. f# k: p; T& U) q: u" D  @
struct sockaddr_in myaddr; //地址结构体
2 N, r( c2 G  y  P+ ^+ K bind函数4 m& I7 l9 {3 s* }. u! J  g4 i  L2 q
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
  }$ a/ x% J/ _" e- \/ f
4 b* v  \% ~# x! y' k

/ U9 n5 y, Z& A! o8 N3.listen监听,将接收到的客户端连接放入队列, h, V9 V  H" a
listen(sockfd,8) //第二个参数是队列长度
3 o( g  i, }, Y$ Y
) Y& g, C5 `: h: x9 X) V* c

4 u. t& X) c/ @0 z; M8 @1 Z6 v4.调用accept函数,从队列获取请求,返回socket描 述符" a9 F, ^- C' U, @: o
  如果无请求,将会阻塞,直到获得连接
. r+ g6 }5 U! O3 w  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
6 R1 {. P& G; ?/ z/ H% O) J% N! S! P2 P( R
9 W/ b+ b& Q) i0 s5 X: U
5.调用read/write进行双向通信
  y( O7 @2 `9 t" [+ T( ]9 d+ Z( i- v9 q" W; a/ ]" R
! c* V- _8 ^' k% k2 j8 ?; i- e- }% R; K
6.关闭accept返回的socket
1 @2 B- z3 F! m, u$ Y4 Y  close(scokfd);8 k, v- E  @: u
5 ]0 q, z1 F5 B* V
0 j7 n& G: z3 x8 L
5 Y2 I" G; ^7 f2 z9 F+ A

  m. Q* ~+ j/ H/ E- E下面放出完整代码& b$ y. T- `4 I# Z. W% m! [

$ q, w- U+ d( Q, U
  1. /*服务器*/
    & m* N! W" E+ T5 m+ F( {, C7 I
  2. #include <stdio.h>! C1 w) H8 k2 Q, s
  3. #include <string.h>5 F/ F% l& m( W5 P8 C
  4. #include <stdlib.h>
    ) C  A& O  }+ m+ Z5 t0 d4 i9 U7 P
  5. #include <strings.h>
    0 v; d2 Z7 \5 E  b9 ]0 f; c
  6. #include <sys/types.h>
    + B0 x: E$ ~0 e' R
  7. #include <sys/socket.h>( u( s5 R0 D/ A# J3 f  W
  8. #include <arpa/inet.h>
    , j" c4 ?9 a0 j4 g4 i2 U
  9. #include <netinet/in.h># R" {+ r# m% F5 A9 H! X
  10. int main()
    . w  M( c- _4 O+ s8 U, S
  11. {2 y9 s; k% e& A/ `" X
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ' C5 s4 ]  O' y; o$ E1 Y
  13.         if (sockfd < 0)! m. }) o( Q; P5 Z
  14.         {5 C7 k6 A, T" e' _3 I0 N- Z
  15.                 perror("socket");
    2 d- u  D* {, l7 e/ r
  16.                 return -1;
    $ \+ E2 K. B6 j3 s& U  N# ^: X
  17.         } //创建失败的错误处理
    5 E* U4 \& e7 _2 {+ U/ U
  18.          printf("socket..............
    2 t) ^$ ]. b6 k, _/ o: y
  19. "); //成功则打印“socket。。。。”( t0 k' }' Y1 {8 w: @
  20.          - `8 C8 o/ }3 R: ?( s% o& z5 N- D
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    0 {- J% E( n# L3 B1 c. j1 c
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    3 M2 F3 E1 z" \7 v8 E4 `
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    + B4 O, {+ n# @  J+ |3 g/ h9 w
  24.          myaddr.sin_port                 = htons(8888); //选择端口号3 v- c& w2 {. `: t3 V
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    , z$ |$ H# H1 G+ K
  26. . W0 U4 i( e1 k
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    : `/ y4 ]* y. f- z2 x' B3 y2 E
  28.          {3 f. \; F4 n: i, J( {
  29.                  perror("bind");; H* N/ k. X, m! N% t
  30.                  return -1;
    " Y- R% Y; b0 [" Z4 i4 M
  31.          }8 f( q5 q: Z3 }  f" X+ t7 q
  32.          printf("bind..........
    4 z+ ~/ Q* k% M, Q
  33. ");. G! O  E9 k- q, F
  34. : w& R- i+ z& `9 P# t
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    5 t) F# O! M( o! U# x
  36.          {: |0 g4 _( V$ w
  37.                  perror("listen");
    % j! N; \; }" v, G/ T1 ^; b
  38.                  return -1;3 ?, A+ Q& A4 d7 V' Q
  39.          }
    3 r9 s7 {, R9 ~7 k4 n* l! t
  40.          printf("listen............6 {( d* y) N' \' X2 x- z
  41. ");
    , e# l) p2 T  c' |  C' l
  42.          
    . ~+ O' z( p; f& b$ q
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    " B- ?$ l# z# a4 G
  44.         if (connfd < 0). s0 R2 s! j( \: h0 O5 d  P9 m) Q
  45.         {0 j( B) y" t. C7 v7 Q: [
  46.                 perror("accept");* R1 f1 D! t0 ^% l, c
  47.                 return -1;
    9 w& E) b. W' d! y/ @
  48.         }
    7 a# _6 O- L) M1 d& s
  49.         printf("accept..............
    " `7 W/ l4 l: v# C( ?, C$ C
  50. ");+ d1 c+ }; Q! X- C4 u+ X5 @- _
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    7 ]# t: m( O% o# T4 X- {
  52.         int ret;
    7 x, l* J7 f. @& ]7 V
  53.         while (1)0 J, \" ~* Q5 t0 A" C
  54.         {
      r+ B# e+ ^8 |  o
  55.                 memset(buf, 0, sizeof(buf));; }: y  M6 O, }# W9 x
  56.                 ret = read(connfd, buf, sizeof(buf));
    ! t3 u+ w$ S' K! Q' R
  57.                 if (0 > ret)  D# d& C& b- D, d; u' H/ H9 d$ p* l0 g7 c
  58.                 {
    4 m7 b' U: c9 ^: S2 z7 S# t# {3 ~
  59.                         perror("read");
    % b1 ^4 N% _* _& O8 P9 F
  60.                         break;
    8 G8 R: q8 l4 }- e! O' Z
  61.                 }//执行while循环读取数据,当
    - N$ R3 Y. ]) z5 A9 s6 ]7 F: L4 J
  62.                 else if (0 == ret)
    * Q0 E6 y* ?7 o+ u
  63.                 {
    * `; \5 }5 u7 Z* u5 l
  64.                         printf("write close!7 m5 e( h$ E+ \2 d" B% y* W) l& M
  65. ");6 |( A# S2 F6 t/ T# ]7 {" S' v3 o
  66.                         break;, r/ F0 A/ F3 z, f/ j9 E# P8 B
  67.                 }+ w% @7 n: s3 q; N
  68.                 printf("recv: ");
    * b6 g, P! F  s  q. E" Z
  69.                 fputs(buf, stdout);//打印接收到的数据8 ]' W: m& z6 a: Q  {3 v; {& h
  70.         }1 r$ B: l. F) W5 \
  71.         close(sockfd);//关闭套接字) d# a7 E& z: F
  72.         close(connfd);//断开连接# Y6 W! I3 w# F" s4 ^  i$ w5 Q* l
  73.         return 0;6 O4 j- g: S. i7 G4 _4 x9 g
  74. }
复制代码
( J# {1 Y* l# W* q3 F
; o' @. F- S5 V/ ]4 {$ |& D
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    0 _+ i3 c0 E& O$ p" _0 L0 {2 p9 N
  2. #include <stdio.h>4 @2 L. K* K+ v
  3. #include <string.h>" f' P, l' K9 b. [6 X
  4. #include <stdlib.h>
    4 z# d0 ]0 r# h# T
  5. #include <strings.h>
    0 v" w: ^; P* Z* A& F  M
  6. #include <sys/types.h>
    : S5 j. |: I' j. q% d' y6 g5 I' z
  7. #include <sys/socket.h>% o* K$ F- a; b7 E
  8. #include <netinet/in.h>
    ' _0 D2 X  _- }+ @# z6 k* X
  9. #include <arpa/inet.h>
    ) [+ ^) w6 L! x" h$ ~# z. B
  10. int main()
    + _' g0 m( _0 [# X
  11. {
    * _; R5 w+ t) p# U7 }
  12. int sockfd;
    / J9 ], }* x' a3 e( Y+ L$ v
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    4 H4 Z% S/ Y8 K+ H; m6 N
  14.         {
    % v1 ~5 I: a/ _5 n, w( b3 c
  15.                 perror("socket");8 d5 f$ `. F1 m/ x& x7 U
  16.                 return -1;
    % u8 |8 H8 m. @9 N1 u& X
  17.         }
    . D7 H5 S% p, I: [) ]# @& a
  18.         printf("socket...........
    6 W( m1 t$ C. v0 K
  19. ");+ ?7 P/ R  ?+ N) y! J
  20.         
      o1 H& L% k" O% X% y' t# V; }: L
  21.         struct sockaddr_in srv_addr;
      U/ [5 \) p$ }! y: j( {5 q+ s
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    : a$ u  [! e/ m' V
  23.         srv_addr.sin_family                 = AF_INET;$ H% k' L. V: V0 ^
  24.         srv_addr.sin_port                         = htons(8888);+ T- `2 L5 X" T: x, O" A9 X3 b( {
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    5 ?( T  T' I2 |9 ^# B/ @
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ! H& v" c* F3 Y- m* N
  27.         {
    5 G) n% _; _2 i2 n* ?2 w/ x1 O
  28.                 perror("connect");9 ^- l* d" A4 I0 Y8 i# o- A
  29.                 return -1; //exit //pthread_exit
    6 a+ E$ K5 P* b* f5 v
  30.         }' c; u' V. d' H- b/ S
  31.         printf("connect..............5 L" x0 x0 d) I6 q
  32. ");( }4 O/ X' K4 c/ x1 h" ^3 |! q- V
  33.         char buf[100];
    : g* Y- k! v  L( r$ H
  34.         int ret;
    8 o( e' |6 V! ^9 ~+ E8 @
  35.         while (1)
    + {4 K9 @3 X( a6 ?# y4 b" h" ?. K
  36.         {
    0 x% J) b1 W- N8 a# Z! m" g  E
  37.                 printf("send: ");* a) [8 ]& e* p2 Q
  38.                 fgets(buf, sizeof(buf), stdin);
    5 t, y8 t& c; H5 B
  39.                 ret = write(sockfd, buf, sizeof(buf));! }/ i7 t, C8 j
  40.                 if (ret < 0)
    3 a; H, e' S9 j9 |1 ]! b
  41.                 {
    & n" V2 W$ r( j6 p
  42.                         perror("write");
    ( Z  D9 S( Q; A
  43.                         break;+ W" N( ?( r# O/ [2 \# N- d
  44.                 }
    & y) D' {6 j5 ?2 b, `  M
  45.                 if (strncmp(buf, "quit", 4) == 0)  `' d( K, n6 [! P$ B
  46.                         break;
    8 P5 h5 U: W; H6 M+ l0 h6 c$ h
  47.         }) Z% @8 W) [1 u  u
  48.         close(sockfd);" Q6 ^5 \5 }8 b# ~
  49.         return 0;
    6 ]( y7 L* I. B/ {8 ^0 s. z% O
  50. }
复制代码

7 t( X8 c3 [. k9 z* u8 k! P& u7 |7 l/ u
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-16 15:06 , Processed in 0.065079 second(s), 23 queries .

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