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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
' }4 s! ~% |1 k9 ?5 S" x7 L) S4 P2 @/ ?$ l; R! e& d/ z
! p8 o+ k  I0 N: T0 l6 \  S7 d5 r
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 N% f% _, W! @4 y4 ^5 T) @
' h+ S) i8 |6 Q$ h, ^) T$ d

( s; f3 A3 |+ t+ V6 qTCP协议
% D: }7 N+ ]8 S5 n6 yTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
8 G  @, l" I) F% K( o; w; \% o# ~/ z/ r% N% {& w$ }
  o/ @: K% C  R0 w5 G+ O& Z# A# H. G
关键词:三次握手,可靠,基于字节流。- f: W! O7 @6 Q/ w+ ?

! t) r( I7 ?7 B4 h4 R1 h
8 s5 V: j' {/ F  ~( ?6 X- w
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 i0 z  T, q/ c6 a& l0 D4 |/ @! G

8 }+ w7 s5 d) PTCP服务器端和客户端的运行流程" R: k9 _/ X+ Q: y4 w  o

/ J- f9 I) @) {$ m, N4 j6 Z, H
- O: o9 R0 Q3 ~2 `2 G6 F
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
" l* X9 x! Z) Q! V" _- c
- M: b7 F: I/ Z* j0 h

& Y2 B- @8 `# k4 E$ E* F1.创建socket
1 W; T# l" M, ]' Z# u$ ] socket是一个结构体,被创建在内核中
5 [. {, B: e8 F3 r0 o sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议; h6 u: M6 a; T# H/ Z* A8 M0 i
/ M8 f4 {8 X/ M8 k+ O9 Q- S2 O

; ~4 Z8 k7 o9 L2.调用bind函数
; {& h) a1 U" t" j% w 将socket和地址(包括ip、port)绑定。" ^3 W* o3 F, |# [& Y1 T
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序" \% m, y& J* k1 u
struct sockaddr_in myaddr; //地址结构体
2 J2 Z- [9 b( |0 e3 J2 S% h8 k0 E bind函数
8 S4 F- Q$ a* ] bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)); X* E5 V; b' f9 B( R8 s6 D5 L
; G$ E; l- J: z3 P

$ U$ |6 r# m: a/ k! p3.listen监听,将接收到的客户端连接放入队列/ B8 g' U3 B* m5 e2 @9 V- T
listen(sockfd,8) //第二个参数是队列长度
" F% L) n! @( u$ J" i
. T  \! {0 D& Q' v

$ R3 A2 _) l! {1 @: S5 X: l4.调用accept函数,从队列获取请求,返回socket描 述符
3 W# l9 E; D: E  如果无请求,将会阻塞,直到获得连接
: C# |1 E* U1 X5 \, h/ Q  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
. ]2 G7 a3 r. E. @1 f* T
9 A5 m4 ~) p' p1 s& z4 ~

$ T+ H! M+ d# t3 S! W( w5.调用read/write进行双向通信
/ _9 t" W9 n% |- |6 U- s! b0 S) X, u4 q4 {; C1 Q) @1 q9 Z/ N( @

1 `- Y. `1 p0 {! c. U* @6.关闭accept返回的socket/ x) R2 e9 u- \
  close(scokfd);; ~" v6 d6 |0 X& @: l
) ?4 y0 o  g% s8 {& {
5 @* I. ?% S; b. V3 E% T) j0 P/ |$ N

' n! P# o: w, ~' Z; O3 `
7 c8 q0 ]' e& W7 b! L6 E
下面放出完整代码
, E+ A3 l" U7 k# Z. h& L7 p# G
5 @* s' ]" x8 m4 X$ ]
  1. /*服务器*/
    0 D$ O9 U/ Z3 p, m$ @' D
  2. #include <stdio.h>
    4 {7 D# C  Q8 [3 S$ [1 \, k6 f) t
  3. #include <string.h>
    $ I9 b# J3 P, a( U
  4. #include <stdlib.h>
    * |: o. v4 \% f9 @
  5. #include <strings.h>
    0 i. M. m! g- F+ \$ l" h2 b
  6. #include <sys/types.h>
    2 G+ T. [, a  g* u, ^7 \- {4 J& I
  7. #include <sys/socket.h>$ A; p6 \9 O( F. y2 |
  8. #include <arpa/inet.h>
    6 Q0 v6 x- H( R% a8 u( M0 T
  9. #include <netinet/in.h>  x8 P9 Y" F) a& W4 M9 Y. w" J
  10. int main()% L& J& w. w7 K* k( X) w
  11. {8 v- P9 F9 t- d( I& f
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ) T& O. d# Q% x$ m1 m/ P
  13.         if (sockfd < 0)
    5 [9 K1 X) M! u9 p9 u9 B' Z- o; ]
  14.         {
    - V8 p5 ~( p4 _. a9 n) }7 e* f) d8 L
  15.                 perror("socket");
    / [  @' F( h6 s8 j  Q, P: D
  16.                 return -1;
    8 q* t* X: H+ u$ [* z9 I
  17.         } //创建失败的错误处理6 ]7 C! r1 L+ X3 p/ i6 b1 T
  18.          printf("socket............... {' Z1 n0 Q( z* V4 E/ k
  19. "); //成功则打印“socket。。。。”2 H% H& O7 O; ^8 |* t/ l+ v
  20.          
    . d& n8 C! w4 Q, I$ k
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体2 u' H9 e7 w& S- z
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)2 y! c3 g( T7 L
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型! q# ^7 A7 Z% S! I0 |
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ! Q& [4 T( r1 i
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址. S) Q9 H: Z6 m( Z' h
  26. + p1 i( Y* J" A6 r4 u
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字, o( O- Y7 D" ~4 r
  28.          {
    6 r% `0 W: O3 Q9 g; B* j
  29.                  perror("bind");
    * d9 F) P2 A0 q! s. }
  30.                  return -1;* z8 x# z- n4 r; _
  31.          }
    ! F. i: d3 n" b# B5 Y6 _5 P
  32.          printf("bind..........
    + s+ m/ J' @6 p
  33. ");
    * P: j" v! H$ e/ z

  34. ( J; G: f4 ~% S" G" |9 p
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听% |8 l6 J! t+ B- h& g
  36.          {
    2 P2 P# P; o" K$ Z2 Q
  37.                  perror("listen");
    5 F$ @: `1 f3 @
  38.                  return -1;
    8 Z8 c6 b  E. ?) {
  39.          }
    * D5 o+ q. k) _3 a. H! x9 C
  40.          printf("listen............
    % m5 m. e0 ?$ a& p7 `' v
  41. ");
    % o. S; M8 ?% j
  42.          % V8 Q) {" h6 x, k3 p* a0 k
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ) X9 ?4 t$ d+ [" X
  44.         if (connfd < 0)8 w+ S, P" n' p  x4 Q. {9 r
  45.         {
    ( q) Q  u" J0 K! m4 V* `$ N. V4 V
  46.                 perror("accept");
    , w; x1 ^* n* [" v$ F( t+ G
  47.                 return -1;! c) i" ~5 a+ X6 K; N  H. ~$ ^2 k
  48.         }" x0 D  v) g. t
  49.         printf("accept..............
    6 W: M# f; Y: t  M, V+ U
  50. ");$ V) v* P9 s: r6 ~. ]( ~. D
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    1 P0 l0 L: \& j" C+ e+ [
  52.         int ret;
    " m8 C  E7 h4 r7 H5 N7 N! ?
  53.         while (1)* e2 ~* M8 f+ |
  54.         {6 l% Y" [% V5 t; E/ ?& I" M
  55.                 memset(buf, 0, sizeof(buf));8 }' H6 s9 \% x1 D, }+ O
  56.                 ret = read(connfd, buf, sizeof(buf));( I8 ^' I! X8 k4 h7 s% ]4 w
  57.                 if (0 > ret)9 {  v* F/ c1 Y: m
  58.                 {/ P7 a8 l( K7 `
  59.                         perror("read");
    " L% Q! c4 k& j
  60.                         break;
    ) t* [( u  f8 O& y, r
  61.                 }//执行while循环读取数据,当* f% `! _+ m0 A; {
  62.                 else if (0 == ret)! A7 N2 R2 X1 y" c4 S/ b3 n$ u
  63.                 {) e6 W  S2 G+ G) V1 y9 t8 C# N
  64.                         printf("write close!
    2 D+ v& ~; n% u" h$ g  g
  65. ");2 u) s* y% I  f
  66.                         break;  N" B/ L2 B& m1 [! T5 u5 ]$ [$ i
  67.                 }
    ' B) j9 q  H( S4 C. T
  68.                 printf("recv: ");
    5 s; t3 B+ {4 A6 R* E2 d9 C' A' H7 h3 Y7 u
  69.                 fputs(buf, stdout);//打印接收到的数据5 w' g4 D/ R6 Q, D5 l8 X7 \
  70.         }
    1 W1 b( J, M3 J9 W4 O) T
  71.         close(sockfd);//关闭套接字
    + T8 w5 [4 S6 y( P) D
  72.         close(connfd);//断开连接
    / G, x- P" I; C! x1 V/ b) f& ^1 Y
  73.         return 0;
    . a0 t+ D6 ^" V6 Z
  74. }
复制代码

' a: U4 y. y3 r7 {/ G' Z6 t& X1 G  s4 L: H8 j  J6 {
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
      v, n" ~! v9 v& c. b
  2. #include <stdio.h>8 v9 I9 L: m5 ~: L6 v4 W
  3. #include <string.h>
    2 h  m) i1 z4 N" ?: ]2 O; x9 L
  4. #include <stdlib.h>7 \3 B; A$ z& Z% F
  5. #include <strings.h>
    ) E# p9 @7 S+ o6 p% o9 o4 A4 S3 ~
  6. #include <sys/types.h>8 b3 {$ X0 q# Z9 T
  7. #include <sys/socket.h>
    7 j2 i' I: ]: m2 W' E
  8. #include <netinet/in.h>
    # S; ~2 Y+ I+ r2 O# d, ~3 w' {0 n
  9. #include <arpa/inet.h>
    # G! F6 X0 k; p* x) M  b2 p/ N; W
  10. int main(): X' A/ ^: W# c- C, `
  11. {
    / y" n+ o+ a: v( K. M$ N
  12. int sockfd;" Q5 v# ?3 z* E4 y( R
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    $ {; g( y  Q) @* Z+ W, L
  14.         {8 j9 d% Z6 @4 ~+ G3 T' n& R' b
  15.                 perror("socket");  c9 m& q. n$ E, X  m  d- l
  16.                 return -1;: {, b5 x5 B4 @3 x* ~: ~! t& t
  17.         }
      ], E. \" x$ [% z% @. @
  18.         printf("socket...........
    9 ^8 ^! I1 d( j4 r3 p
  19. ");6 ?, W/ M' q) C/ F8 I
  20.         # f. P7 J8 o7 V8 v
  21.         struct sockaddr_in srv_addr;
    7 ~5 f+ u' I# d* P
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    / b% ]; l2 r" v! Z) W2 e' C
  23.         srv_addr.sin_family                 = AF_INET;. o. Z$ S$ m# y, |) _* \
  24.         srv_addr.sin_port                         = htons(8888);
    9 ?3 J. V, t) [+ V
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ! a" Y0 }( C0 l' }, j% V
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))9 k! X! g; l$ P! l
  27.         {
    . q1 G: L5 N3 v+ `4 M; b
  28.                 perror("connect");
    " _/ @; E. f" z2 `( Q# ]9 ~5 Z  D9 H
  29.                 return -1; //exit //pthread_exit  h* e1 }$ w4 X" t
  30.         }
    9 l; H. {4 U8 {
  31.         printf("connect..............
    , t! n0 q8 u0 ]
  32. ");4 u3 \% O8 ~  Y/ R( }
  33.         char buf[100];
    # Y1 F$ T/ S4 G1 a7 e( y
  34.         int ret;
    : Y' G; ~4 Y# X* m" g
  35.         while (1)
    4 a+ V9 m5 o' j# u3 C9 L! A6 b
  36.         {6 w1 X2 s; h) D
  37.                 printf("send: ");4 V+ q% n  _( Y
  38.                 fgets(buf, sizeof(buf), stdin);, l5 U/ d( U/ r4 Y% T8 V. z+ P
  39.                 ret = write(sockfd, buf, sizeof(buf));
    & z0 T, n# a* N8 l% d- ?
  40.                 if (ret < 0)
    $ U; i; r' `7 M5 k
  41.                 {. ~$ z5 x8 D" J* d) y
  42.                         perror("write");6 S; h  F5 y3 f- n. E+ Y$ {
  43.                         break;
      H9 e# m4 U; x) w$ y  L" T+ E
  44.                 }
    : w# ^$ n/ ~" H6 r  J9 Y% m0 [- ]
  45.                 if (strncmp(buf, "quit", 4) == 0)* O4 @; O( R, U& p; K: K
  46.                         break;+ O" V& K; Y* D8 Q' G& l
  47.         }
    / B- V9 }$ E& k, d5 h8 n
  48.         close(sockfd);* p% y6 W2 B# O/ O! a- c* A$ z
  49.         return 0;  ]+ Z6 m5 \8 I1 T5 e
  50. }
复制代码

. {" l- \9 B% {* W
& O; s  g+ r4 L7 o
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-5-2 11:45 , Processed in 0.105008 second(s), 22 queries .

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