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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
* \+ B3 u6 i, d1 g
; J# C# S. d/ @8 x; G; P

/ s8 R' |1 Q; X! Q3 V$ t# Q; qsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
8 \- v& c; W8 i! J) C: b: i& _4 Q) S# b0 K1 C* L
6 _8 P2 |" C& X* p
TCP协议( _4 A: E1 S- {8 e* R1 p- L0 m% F
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
( ~( @7 E+ f. |& U# l% C
3 l% n; P+ \9 F+ ~! h1 ]
6 E1 b0 O$ J. C* u* I
关键词:三次握手,可靠,基于字节流。
3 b" y, O  W5 A$ g! N! Q" Z$ c2 Y1 B9 C2 K: T6 o3 M7 c

5 u6 o, {  |) M# k  E% Y" x可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。" Q; s  T0 o; y8 l) h* F: Y

* D2 P5 }; I' f# T: M" zTCP服务器端和客户端的运行流程
' c/ S0 R7 [9 B( T( W6 _
% I" c7 Y* O# u

8 g7 }, W2 l# L如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?- h4 I) }6 _( U( [8 G
2 _7 Y( x# \7 P

  |  F4 o+ m, ]8 a. ~2 ~/ Q1.创建socket6 P' A) `: V6 @$ J! @' O
socket是一个结构体,被创建在内核中! B  k( h9 K% y9 X5 z
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
' r0 R* t4 z* a" @4 j5 X6 i
$ C. W8 y6 c, ^% X
: t3 e, {& H) n, g( j
2.调用bind函数
; x. y8 I$ N4 Y  }8 d' S6 Q( ^ 将socket和地址(包括ip、port)绑定。
8 ^! y* ~; w, R" i0 M 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
1 y7 E9 F* X8 a4 e struct sockaddr_in myaddr; //地址结构体
! g6 j' ]. A( y0 Z6 d  h/ C8 J+ i& _ bind函数
3 a3 b7 k7 [$ s8 [9 m! \% V bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
; z- I# e" q7 J' ?+ T* \# V' `/ S5 I# B7 `0 j* [! Q7 Y( x$ E

( w- w: A+ `& v3.listen监听,将接收到的客户端连接放入队列5 W9 U& P2 ^( e& \
listen(sockfd,8) //第二个参数是队列长度
. l9 {, ]' [  H2 j1 \
& F- V! H9 r+ w
2 s6 E2 z" l+ I" [0 S' H
4.调用accept函数,从队列获取请求,返回socket描 述符! M% w" d" B8 ^% x; A
  如果无请求,将会阻塞,直到获得连接% ?( n0 b7 l# x( d0 d+ t) u
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 w9 j+ y' j6 z. B4 ]/ q3 {% ?
5 v: R: ^2 |1 N2 Z# H- k: T: L
. E- y" j" a  `7 S7 K9 _$ t
5.调用read/write进行双向通信  M  x3 ~" x; Q' ^- m
4 }# D1 a5 x7 q7 I* E/ f
! f1 Z1 ~* A5 A) q. A. l
6.关闭accept返回的socket
! ?4 D: d% L) e7 i5 D" r% O$ B5 c  close(scokfd);; f. R; e/ g& e# [0 H* z4 d

. k5 y+ e* W( U; Y: Z
9 u2 u! Z  a3 |+ d* Z! C

0 M2 [- g& M% \

: @# t: e+ f6 T- {  W  J下面放出完整代码
" K) L. V2 j5 D, M8 w" F. k9 {: O3 X5 C) D; [. J
  1. /*服务器*/# T' A; V2 O) \5 v( z0 W
  2. #include <stdio.h>+ L. C. g4 }9 `3 T+ H
  3. #include <string.h>
    8 T1 Z7 {6 q+ f. n4 P
  4. #include <stdlib.h>
    9 j' v/ R/ n# Q' E- ~* ?) j
  5. #include <strings.h>: ]. z3 N% n% _
  6. #include <sys/types.h>
    4 K5 v) Y! Z- l$ c7 _
  7. #include <sys/socket.h>9 i# Q1 h/ b$ Q6 ^
  8. #include <arpa/inet.h>% _" k1 S+ j! n! `9 {% g, Z2 X
  9. #include <netinet/in.h>
    . B# k; ?$ z/ [- z  S4 P
  10. int main()7 ~3 X0 S9 l1 f* m, Q! N5 ?, B+ g! H2 C
  11. {
    * H) J* a+ u- a5 k, Y
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字  D# X1 S. X4 U4 {" @: l
  13.         if (sockfd < 0)6 k/ ?* R% ?* z- k
  14.         {
    + q$ ?7 d# ~# I9 M  i) N
  15.                 perror("socket");
    4 D/ s. y( R6 p* e$ u* h/ L. W
  16.                 return -1;
    4 d& V/ n. N) F4 t/ r
  17.         } //创建失败的错误处理
    - N: W/ `5 g/ s, |5 R3 w  B' x
  18.          printf("socket..............: i' |" E) ]6 r8 P" f" h( G
  19. "); //成功则打印“socket。。。。”
    . Q! I0 y1 t7 }  A
  20.          
    7 H# A' h6 W" S# x
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体. s* y$ u2 t9 S) S: k" C5 e: o
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ! I0 g: k2 X- K7 n) [$ ]
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    8 }4 ]# t/ C! Y: r3 N; s0 @
  24.          myaddr.sin_port                 = htons(8888); //选择端口号, _4 s- f0 b% f3 F. W0 t$ D! g& \
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址' o! _0 m& Q7 x& u% m5 F

  26. # }$ `; O1 i/ J; Z, N
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字& c* F9 \7 X) w4 s
  28.          {+ f) y. u; ?2 t: Z
  29.                  perror("bind");
    + \9 U" J5 {& c+ S, M7 H# r
  30.                  return -1;
    % c+ S/ J1 j" J- \9 K* C, i3 C# D
  31.          }" |$ k4 j+ S4 f+ [2 E* }
  32.          printf("bind..........: x! N7 ^" u% p; h
  33. ");2 y- C) F0 s1 C. L1 q) f9 r

  34. ) L; p9 n4 W+ d4 w" g
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听% x, g: S. N/ V% _& w8 s
  36.          {
    & l3 Z0 v( ?* A) _; B1 I7 Z6 y
  37.                  perror("listen");  @2 x% M0 v* Q, ]
  38.                  return -1;
    * _  E! a/ c. |8 R) j
  39.          }  ^* w( d6 H9 s. Y# n& r5 A  G# k
  40.          printf("listen............
    ; Z: x9 [2 o3 [* |4 ^
  41. ");, `# G9 C- [0 ~, {
  42.          
    4 v% R' X2 X8 S& I) X
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求: ~# ?$ c) x* {5 [' @$ |
  44.         if (connfd < 0)1 P: [" f* S% k- x2 y- h/ c; l4 @
  45.         {
    " Y% Z  E/ {4 h, ?
  46.                 perror("accept");, }  T$ `7 x; ^6 _# g/ i1 T
  47.                 return -1;1 c7 p# L2 ~6 D3 H( w
  48.         }8 e+ {% O/ G; W" k
  49.         printf("accept..............
    ( |/ O: O/ W( V' G* J
  50. ");4 G# R4 K3 [2 ?$ @; E  |3 Y; W
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    6 K9 p- G# V% F% S" W  c
  52.         int ret;
    + J; @" Y0 Z$ [; `/ }( g8 f2 s
  53.         while (1)
    ) ~# n8 U1 Q* P1 p$ S3 h8 [
  54.         {
    " s$ Z! h. R0 z
  55.                 memset(buf, 0, sizeof(buf));  D/ @7 F7 M4 x" h5 l  @; F  n
  56.                 ret = read(connfd, buf, sizeof(buf));& Y- {& Y! S0 ^
  57.                 if (0 > ret)- X( R& b" ^- G% A
  58.                 {+ n& c' a" t, _7 T5 R7 {1 V0 z6 x
  59.                         perror("read");
    ' j+ \) O& E+ q; [# x7 T. _! B7 I; M
  60.                         break;
    . {9 u: r( S0 i; l% Q
  61.                 }//执行while循环读取数据,当% A) W+ y( ?& c7 C8 @
  62.                 else if (0 == ret)
    8 }* z) q* ]' N
  63.                 {
    0 L: A) X5 Z& p% A4 E  ]
  64.                         printf("write close!
    2 O; t) t' a$ R
  65. ");
    0 g8 K; V$ F( i
  66.                         break;
    - x( \7 d$ M* p# a; ?7 C  ?9 ^" [
  67.                 }
    * ?0 V1 l3 A' N" _; K1 V
  68.                 printf("recv: ");2 P! I: [3 G. A6 f/ j8 ?0 Y
  69.                 fputs(buf, stdout);//打印接收到的数据6 r' m9 r8 N& ?+ }& m  C
  70.         }+ d9 a- e4 k$ F: _9 o) c8 f
  71.         close(sockfd);//关闭套接字6 Y1 d, n% Q) M  d* ~
  72.         close(connfd);//断开连接
    * `. m/ Z7 E( s" S
  73.         return 0;
    % b& I' X+ |: q" L
  74. }
复制代码

. }! \, s( N5 [& S' g0 @; u- M+ K9 S  H# Z
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释); F2 e% A/ C8 F/ y; e- r
  2. #include <stdio.h>+ |1 T  K3 G2 `' [
  3. #include <string.h>
    , ^3 q- N& D. W
  4. #include <stdlib.h>
    : l! N& v6 O) b/ K: N; a
  5. #include <strings.h>
    / R, t) N: g! c' ~
  6. #include <sys/types.h>9 N; I% t% X) d% M$ Z! L8 ]7 Q; d1 k
  7. #include <sys/socket.h>. h$ |1 f/ d5 E: E7 ]8 j
  8. #include <netinet/in.h>2 b0 r# P8 X- f  r
  9. #include <arpa/inet.h>
    % E$ u- v% Z( s  F2 a2 K
  10. int main()
    ( R% O7 [, \0 q' L: C& F
  11. {' R4 b4 a, I* D9 x: s* B
  12. int sockfd;) B0 S) Q5 t% m% ]1 K! Z5 D5 |' Y
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))- |  h) M4 R% W/ m2 `8 e4 g
  14.         {
    0 }9 L/ q3 U& ^* M( g
  15.                 perror("socket");- w7 z2 e, o0 j4 Y# j9 K" w
  16.                 return -1;
    $ |; m  M. O0 I3 ]0 H
  17.         }5 U# A: {  a7 K. U
  18.         printf("socket...........0 [* t: m4 q( _( H
  19. ");# ~% Z  e% ?/ e" Q
  20.         
    ; z" S6 ]% E& x/ g5 C, w
  21.         struct sockaddr_in srv_addr;
    $ k3 _1 F# z2 J/ R
  22.         memset(&srv_addr, 0, sizeof(srv_addr));9 _" X% b8 [2 R8 \2 A6 ^
  23.         srv_addr.sin_family                 = AF_INET;
    + A3 T9 k: C( \2 t9 x. W
  24.         srv_addr.sin_port                         = htons(8888);4 ^7 a) Y( S1 Z( l' s6 K
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");/ Q1 I0 m/ ?* P6 X
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))). ~6 g3 y3 M  [6 P- ]
  27.         {( m9 v( I# [- x% {
  28.                 perror("connect");
    + ?9 D" C5 ]/ _) B1 X$ `3 G) Z! T
  29.                 return -1; //exit //pthread_exit4 d! C3 m8 l3 k* W
  30.         }
    5 Q8 i; M3 R6 Q6 M8 J
  31.         printf("connect..............% Q6 s1 e: P2 _. v
  32. ");2 h4 B) s+ |$ Z% i- s/ G
  33.         char buf[100];
    ( r" z7 B* Q- ~' b0 L
  34.         int ret;+ x: V3 `) i8 u0 S1 t
  35.         while (1)+ w' r5 E; m% o" U( V
  36.         {" E  O* o& _1 S$ C5 F" j
  37.                 printf("send: ");0 @) J% }+ d& Z
  38.                 fgets(buf, sizeof(buf), stdin);; V8 l  M# t1 O& m: a
  39.                 ret = write(sockfd, buf, sizeof(buf));
    & I1 U6 t5 A7 b6 E
  40.                 if (ret < 0)1 B: y/ g. B$ y4 Y/ D( ~
  41.                 {8 |$ W8 f8 s) n# w8 Y. M) V+ D
  42.                         perror("write");
      T( G6 {( |, c+ B  ?3 k) p
  43.                         break;6 t0 q6 d7 H/ y# ~7 L5 {; K
  44.                 }* a% Z& X2 }- o# X7 C9 z
  45.                 if (strncmp(buf, "quit", 4) == 0)  W' `( Q- z# Z. J* [4 }
  46.                         break;
    ( q  v0 P% K( t; Q4 r
  47.         }
    1 v; e6 N# ]. v4 F5 ~% d4 f
  48.         close(sockfd);/ N$ H. i5 g7 u9 v/ D' R
  49.         return 0;
    9 v$ e, E( ?4 q
  50. }
复制代码
8 q/ S3 a/ ~! U* E3 w1 ^  \

# y4 Y* ~& a7 H2 p) r& V
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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