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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
3 Q: t- e% i% B. E# x
# s% c; r  g2 J% S0 `$ q9 V$ k0 q" n
3 S8 B( L2 ?6 e$ C
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。' ^! y: X" {7 W3 e" m5 z

+ V) x! J0 i3 J
* E) j. `8 A& n* V
TCP协议
% ?8 _# c& ?- o- x/ O# TTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。; P" q0 S( n4 s  c* H0 V. O' \
9 ]. V" Q- }  ]! d0 L  q

" \- e+ n+ X' n8 B  a关键词:三次握手,可靠,基于字节流。
+ H; i, q% I7 \4 E
3 i: m. O/ ?# d# a
9 ?  g0 k+ v; e+ Z2 D3 d' s( Y
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。7 ]9 V; g& i+ S; O5 f9 T% Y

+ }( D: g( \$ z* K: d. I. LTCP服务器端和客户端的运行流程7 @* w% h/ R# _+ Y+ c0 L, H3 \
8 E6 p; M, W$ C: r. u

1 k; ?( @  M5 f6 `) h! u如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ E! w  `8 X! M/ p# L+ x# y- ~' q' q

( p2 q- ~( ~& D- l- i* _$ z0 s1.创建socket
. u' g6 r. x) Q# o1 U' c. X' Y; M socket是一个结构体,被创建在内核中
8 B3 a6 _4 d1 B9 o7 h sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
6 w8 `& e& ?3 I! Z$ [, i. N0 }/ E( ~) @# v+ }0 t

3 E9 ~9 n1 D0 u- d& l. i2.调用bind函数+ Z& b; A( M3 ~
将socket和地址(包括ip、port)绑定。
7 `( L& w) \) ^/ v 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
& o/ z  ~/ ~( C struct sockaddr_in myaddr; //地址结构体1 `  D( \: r$ z* v; P9 L6 O' P
bind函数
, e7 I% a$ Q! H9 ?* p: ]2 B4 [" u& l bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))2 D/ q; ?4 j3 P* U( @: D
: F3 C6 F7 C+ Y
: o6 n3 C* P4 G$ g, Z( w! q5 ^$ a
3.listen监听,将接收到的客户端连接放入队列
8 ~3 d/ T3 v4 f- d- m$ M" e% b. D4 h listen(sockfd,8) //第二个参数是队列长度  b( q* @% b! {
# P5 N1 g. j+ u. m8 D
/ D- s: x1 v6 }! \1 C, V
4.调用accept函数,从队列获取请求,返回socket描 述符; D4 N' P, ~) j/ n8 R6 Z% C" n
  如果无请求,将会阻塞,直到获得连接, M* j+ A* w7 m2 _* K) _6 I& s
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" W4 C6 E. X7 M  _* `- J9 b+ J# f( R( j! H

) Y) w$ r; x& N( `. s5.调用read/write进行双向通信
4 J+ V2 q( m$ R2 D8 @# S; x: l" T& X# A  \$ O/ _

$ i9 j3 w. \# A  y" `6.关闭accept返回的socket
1 {. ?- F% O6 w! @# e7 i: m  close(scokfd);
( ^4 d, }* e) n0 Y% N2 H, F  J
# @1 I9 Q: |6 `* F# m. F3 Y
9 X. l. \' Z: K, O( r: w

+ [: g) D% |2 r- _3 p7 F0 w
7 G# t# P! P% w4 ^' F  C3 b6 `" u0 h
下面放出完整代码
. X% G) f+ m* Q/ Y5 I
$ I9 [' M! W& r, r/ L, u8 w" D
  1. /*服务器*/# V6 `5 ^' h: |
  2. #include <stdio.h>4 h" m2 W4 p6 s; t( T" A
  3. #include <string.h>
    ; b7 P) f1 ^5 Z( [9 N8 \; I( K# x
  4. #include <stdlib.h>" O: Z) V2 {! J. f
  5. #include <strings.h>
    5 X8 ?  X' V; I1 l! L+ Q5 h
  6. #include <sys/types.h>
    7 c3 }$ [0 X# g* [
  7. #include <sys/socket.h>! R9 |$ k+ Q' [) X: H  }
  8. #include <arpa/inet.h>
    & l# t6 l& W! w6 f+ h
  9. #include <netinet/in.h>+ S+ M  A4 \; x" j. t- b* o
  10. int main()( P3 y' l) p; F" f
  11. {+ T& T3 m' w& N
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    & X" |- }6 A8 c+ F. F
  13.         if (sockfd < 0)( M9 n9 [; y5 M+ Q' V; e/ y
  14.         {
    ; X% w  S% z0 L+ N
  15.                 perror("socket");
    ) q" T* B" S( h
  16.                 return -1;
    * x  Z# r4 o3 A4 w9 b. [5 n
  17.         } //创建失败的错误处理
    ! B8 g% F/ E- i. c8 N
  18.          printf("socket..............
    4 z/ Q) M' H6 z6 z
  19. "); //成功则打印“socket。。。。”* V# H1 ~3 S( D5 ]( J: w0 u
  20.          
    3 g1 ]' l$ o; X
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体- W& U/ F" Y' Z' }5 C
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    2 S$ j6 m: Q1 u
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型9 h, k% I; o8 T2 G6 W  L6 ]
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    8 t0 }" X# _$ b! R: I
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    8 T0 y5 W( n- e

  26. * d  S( H0 I/ M) K1 V1 A) l. x! O$ P
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ; R0 y" k+ w: g5 R, F! F8 N
  28.          {
    & s: y- _- s; ~+ ]5 i
  29.                  perror("bind");- d& t# G1 G; C$ t3 k0 ?
  30.                  return -1;
    9 y% L5 A7 T; z
  31.          }
    / k- P$ E  \4 R6 a8 r
  32.          printf("bind..........3 q1 T$ ~/ t6 W1 e8 E) K
  33. ");
    ' A; m! B. Y) D- ?9 s, @
  34. , _7 P% U2 s( J" `% F
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    7 l* i: M/ K  V$ |# }
  36.          {; o3 v0 o' h/ n/ ?) _& a
  37.                  perror("listen");
    / M9 a8 T: q% g( }
  38.                  return -1;4 [1 G5 ]" |, h, L2 R5 Q2 M0 {4 {
  39.          }9 n! f8 e2 `/ R, g( e6 l! u7 _/ ^
  40.          printf("listen............" C, J) v3 |7 T1 G
  41. ");) d, B6 c! J# m/ {: X& I. v
  42.          8 L, [4 D; f' [% [
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求! m, X6 h7 _9 G$ E; z* l
  44.         if (connfd < 0)
    # s" b# l% a+ S$ b% D
  45.         {
    + @3 u7 |- b8 t# \0 Y
  46.                 perror("accept");/ |* q; c# v4 `- U* p  x" {9 u/ c3 q# ~
  47.                 return -1;4 F% c1 [8 y" k$ q. O/ K& p
  48.         }/ b5 b! ]' L' r  e# n, y! O' p: q
  49.         printf("accept..............3 {  ~% E* X, M8 `# B) U4 c
  50. ");3 w( w4 @% s7 `1 G  }/ a6 V4 R/ n  n
  51.         char buf[100];//定义一个数组用来存储接收到的数据9 w1 T( W( \- D9 H4 D# X* s
  52.         int ret;# @! y; o+ U# ^8 U) V( W
  53.         while (1): X2 q" U$ G" H1 D1 \& r: T
  54.         {- S0 `# P- w! m9 Q7 n' V# S' U
  55.                 memset(buf, 0, sizeof(buf));
    , K$ T3 }1 S+ O# j+ p& o
  56.                 ret = read(connfd, buf, sizeof(buf));
      s) J2 N1 N2 B* R$ x$ Q
  57.                 if (0 > ret)
    - o' e- @4 P8 c6 p
  58.                 {& i$ G4 Y, v- j2 d" G+ H+ R
  59.                         perror("read");% p& M! C/ }9 R
  60.                         break;$ M  _; f* x) X0 S$ [0 \" j* b
  61.                 }//执行while循环读取数据,当/ n1 |8 x. A2 y( p6 g, v3 k& p# n
  62.                 else if (0 == ret)
    5 u( o  z" W; G2 u- Q5 N* l
  63.                 {" s% k2 Z! o* J
  64.                         printf("write close!
    " h  g- ^- W9 t1 Y2 ?( W/ X8 s
  65. ");
    ; D% S. r/ o1 B; F( W9 G2 q
  66.                         break;
    $ J4 w4 B7 d/ s- u4 n5 [6 N
  67.                 }
    ( P) N+ b* Q, o8 p( w3 [( e1 V( K
  68.                 printf("recv: ");8 i# K# t6 k( t- A) ~) A  K. M
  69.                 fputs(buf, stdout);//打印接收到的数据
    + s& E; Z2 l4 F- G5 b0 M: k/ A
  70.         }. y0 d. D, M% Y4 S( Q; D
  71.         close(sockfd);//关闭套接字
    2 i4 H: }8 x0 J8 }. l/ {8 p
  72.         close(connfd);//断开连接  d2 V4 _+ I* d4 \% L
  73.         return 0;
    " |7 I7 z! t! w# i) d
  74. }
复制代码

5 w6 H" J+ L8 Z& R2 [$ d- W3 H. L7 u' b  J: Y# R7 h4 I& y; z
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    2 N. M% `2 I4 r! i/ s' Z8 a
  2. #include <stdio.h>8 z. w# Z# ~$ c/ V. k& [7 [
  3. #include <string.h>
    % [3 u  @; ^7 p- ?( K( R
  4. #include <stdlib.h>, h9 ]7 _3 b6 u, E5 w. `
  5. #include <strings.h>
    6 Z; X9 [8 Q% i) W
  6. #include <sys/types.h>2 u( J$ o5 T. L+ X4 o
  7. #include <sys/socket.h>3 }! B: R7 H; P+ J) P: }6 s
  8. #include <netinet/in.h>
    ( @7 v' O7 u8 R/ z. t* P4 z
  9. #include <arpa/inet.h>/ V, C3 k! k' T) m. N
  10. int main()
    ( I$ n. A- W1 E# R
  11. {3 N% Z# A2 }: h. B0 n" T
  12. int sockfd;
    1 M5 K* Y2 i; z& ~# g1 `
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))* x  t* B/ n* ]' e2 `3 g
  14.         {& f# d& N: b6 M$ J+ f- p. @& O6 h
  15.                 perror("socket");8 U" o1 _3 T( t1 a1 a
  16.                 return -1;
    ( u4 \% q; U5 `6 s7 r9 q- p' r  \' f
  17.         }
    8 N' b$ o7 G4 {& u
  18.         printf("socket...........! J/ h$ ~# V% y2 m3 N& g* I4 R
  19. ");
    * A+ A. A; {1 O5 P
  20.         ! U$ e" V. @1 r& {% {
  21.         struct sockaddr_in srv_addr;
    : H* t' U, _; \2 j
  22.         memset(&srv_addr, 0, sizeof(srv_addr));; F6 c) J3 {! j  ^. l4 u7 z' o
  23.         srv_addr.sin_family                 = AF_INET;9 z! b' _9 z: b* Y8 K
  24.         srv_addr.sin_port                         = htons(8888);! R7 G$ @  Q: L
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    + n: k9 s% n2 o
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ' @) z. Y8 z8 R- N7 e
  27.         {% c: s* C' p  P3 |
  28.                 perror("connect");* E4 J( U. Y  f2 ~. T; V
  29.                 return -1; //exit //pthread_exit5 ^& k/ l' J3 \/ R/ ^! o% }( K4 o
  30.         }# L* U* ]4 |4 w2 Q9 k2 h4 M
  31.         printf("connect..............
    ! N) W* E: y) x+ Z
  32. ");2 G* K) q7 Y) k% u/ e& v- o
  33.         char buf[100];
    ' h/ C3 T0 W4 [. ~( J" Y! f4 `$ l
  34.         int ret;
    : n# H, c' F1 S0 ]
  35.         while (1)7 N) O/ ~) K8 r; g- [/ C
  36.         {8 Y  L+ t- e  `# ^
  37.                 printf("send: ");' r0 C7 m' E7 o. c9 D* {( @
  38.                 fgets(buf, sizeof(buf), stdin);
    $ l( ~1 T" p$ r& k6 w
  39.                 ret = write(sockfd, buf, sizeof(buf));* M5 b, \4 i0 x, s; p' \
  40.                 if (ret < 0)0 I, F+ ~. b0 U2 m
  41.                 {9 g( [% y* @& Z* N8 M: G. _
  42.                         perror("write");
    # q  }4 z. G( }9 R/ l: j& a
  43.                         break;
    " \9 f5 }7 E2 j# F/ u% x) V# e6 y
  44.                 }
    7 R  V3 e) f3 Q9 y% I5 q
  45.                 if (strncmp(buf, "quit", 4) == 0)
    % H$ {' w3 ~4 m, K8 f; L
  46.                         break;0 s' N' A. v$ N2 B
  47.         }# ]0 \" {" T6 a( \5 e
  48.         close(sockfd);2 E  M: j% `! t( O- l( a% y
  49.         return 0;
    3 ]* y  n  \. s, }+ _
  50. }
复制代码

# ^/ o. w4 b7 K' O' T
9 C3 l  ^+ j/ u3 [6 L3 f
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 11:34 , Processed in 0.059445 second(s), 22 queries .

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