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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
1 d/ i$ I9 U4 ~/ c2 U" d- e" B8 z2 n0 _  Y" X
3 V3 u, m( o! N4 p# `' O, `: x
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。' B8 ~/ d: P. O) i& b# i2 ?6 D

! I, t, W+ s  ^0 U
' }7 E" ?- j2 n5 @' H! N
TCP协议
5 N# s6 I+ k  I: [$ k' l- z% i2 TTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
( f/ d7 X& O4 c( W; F0 T
1 k0 F4 D: U9 L

7 c* y, n- O8 y$ u1 V关键词:三次握手,可靠,基于字节流。" K1 g' L9 Q" Z& @: m

. W0 S& f0 O& h4 _- A

( N( s# b4 x" @: D- @) ]可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 }- [' b; z' g

& l. W: K# S% H$ sTCP服务器端和客户端的运行流程& ~% k/ Q2 K1 @% l) f) W/ c
9 i4 i# i6 _' F( l

9 m* ], v! K+ t  m如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
" X" I' X- a% n$ n3 c; f: [! p5 l) S2 p+ ~: @' u( l/ ]
3 Y, I: Q) `6 d* q& Y! X  d# J$ I
1.创建socket
0 q9 v* `7 }* [7 O( ` socket是一个结构体,被创建在内核中
# N+ f; T- D% X0 r" X# ^ sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
3 l1 c8 q. K; T8 n* Y3 Q1 \# E
8 ^* T& [+ z5 d  q3 Y& w. {
' |' ~# _% s( e
2.调用bind函数7 S2 E$ _- E/ R* Z
将socket和地址(包括ip、port)绑定。
0 g3 t- j$ l" \4 @ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序" `- M" V+ C# ], C5 z3 h5 _/ y2 o
struct sockaddr_in myaddr; //地址结构体+ L; C; F& q# d. R' p) {
bind函数
7 y. [5 G/ i3 w" _) h bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
) V8 v  I4 D/ M9 X  _' ]$ k' y+ [
) e7 s0 L/ |+ s# s, ]5 p4 j
- i8 }& J8 h" _* i
3.listen监听,将接收到的客户端连接放入队列& z# a7 {1 ?6 {% b. ?5 `
listen(sockfd,8) //第二个参数是队列长度* ~  C& y3 i3 ]9 t- i! ?

" x$ l7 f) N# |3 X  Y2 d5 w7 z

- r6 @5 m: l: \: l8 p4.调用accept函数,从队列获取请求,返回socket描 述符1 o1 x) w% N8 C9 d
  如果无请求,将会阻塞,直到获得连接
* o3 ?- [4 X$ K0 f  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
( A0 T, O& ]  |2 R5 P8 \% z
/ |1 }& y6 @( j5 d, ?

7 H' K' d" L; N8 @/ R/ E5.调用read/write进行双向通信
/ D' C) R) x5 P8 R9 |3 J+ z9 O0 g* m
1 d. p) n  [+ W
6.关闭accept返回的socket; g" s8 V9 |0 ]' C5 q
  close(scokfd);- U( M( z3 e9 ^, j* @. J) V0 d
* i) ^6 W$ y. R2 ~% J+ W
, X6 L8 v' o  E

, \' M  A/ J% J8 c  }! q

2 M8 n8 g% C( n1 c/ _7 K: x: {下面放出完整代码
/ i6 ^  _. C: h1 c/ `/ H( d( W. G3 r+ [1 g; {9 X
  1. /*服务器*/
    ; m* k% i  c1 \, o* D* }) I
  2. #include <stdio.h>% \" M+ D2 m+ P+ s
  3. #include <string.h>
    ( [* M- N# n) h* i( U" r
  4. #include <stdlib.h>
    / L. d  D( n0 z; R
  5. #include <strings.h>8 ]$ V2 Y& |' T# b7 u2 j( r  j/ i
  6. #include <sys/types.h>% w/ O$ U, M4 e% D
  7. #include <sys/socket.h>6 L6 w8 t+ C; N# M' b' J$ f
  8. #include <arpa/inet.h>
    * r2 z$ T0 n. c0 D
  9. #include <netinet/in.h>
    9 L  p' }/ Z( d$ k2 p) N5 T
  10. int main()( m' b" V: O9 N+ S  Q
  11. {8 {3 i% [. e  t! w3 Q
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字4 ~! n- F  |, i5 c* A, q
  13.         if (sockfd < 0)
    4 W4 T7 S6 _5 V( y* H+ n
  14.         {- r7 D: j1 W! E7 d* H# L3 A/ M) H7 b
  15.                 perror("socket");6 P( X' g" H9 y: u: k% b
  16.                 return -1;9 F3 @1 @/ R& G  `8 n( j
  17.         } //创建失败的错误处理% k7 ~' ^1 ^1 z! x- F% h# b) F
  18.          printf("socket............... @' S; e9 w% X) R# y4 I4 t
  19. "); //成功则打印“socket。。。。”. g/ h" C( T4 Y* ^# D
  20.          . w0 z7 X0 T" A, b7 b
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    5 G( h7 C+ `. W$ p" g8 L' I
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    , g3 t- Z+ m* I6 a: O) G
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    . O  L" b- Q  ?6 [
  24.          myaddr.sin_port                 = htons(8888); //选择端口号* @0 g3 {# `, _) H( x2 E. }
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址# {- s# S& K: X( j% I' n: C

  26. ! @4 s" f% I) ~
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    * j6 [& \8 B; X* e
  28.          {9 M9 [9 G/ p: ~7 z2 d# k3 R2 m
  29.                  perror("bind");1 y, T. |; F0 S
  30.                  return -1;
    ' k, [4 `2 {% _7 ]6 a
  31.          }- r7 g% z4 F  u0 c% @5 ~# e( ]. l
  32.          printf("bind..........* U- N& B" [0 L8 M7 J
  33. ");
    , @' I) o1 Z) O4 w& A

  34. 2 \: `* H5 x; k# `
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听3 ^5 ]& C- i! |/ A$ ]; a# v
  36.          {
    , k; J0 C) i3 e
  37.                  perror("listen");0 U7 t' O. ?! Q9 Q4 R2 @
  38.                  return -1;
    # ?+ L3 g% P# [$ _5 r
  39.          }
    6 U5 w' Q8 }! O
  40.          printf("listen............
    + x" q. d9 j0 x- }8 c$ V
  41. ");
    5 V" ~: n, Q* X: t% v0 |" v
  42.          % d. G' n' u. S7 z
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求* R& c: N* T4 i* q, x. J3 L
  44.         if (connfd < 0)4 c- [5 v) t* W' v* f& a) ?
  45.         {
    3 @0 r5 `6 D7 @
  46.                 perror("accept");7 j9 P8 Q8 f9 s  R0 l$ V  x
  47.                 return -1;
    9 u8 s; o9 z6 ^
  48.         }, R* F* H% t* }- x0 l/ P
  49.         printf("accept..............
    : C) F' m' |, U1 N. U. x2 L
  50. ");7 Q. Z! p" ^, t- n& ?4 k$ a0 r& C
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    : K. s' P  F9 e1 X' f1 o7 C8 B
  52.         int ret;
      O+ W1 U' C0 R1 P) C
  53.         while (1)  z1 s1 N. D2 W- R
  54.         {4 K9 i/ a3 U' ^/ d4 p
  55.                 memset(buf, 0, sizeof(buf));' R0 ]. e3 Q4 `" W. t# J
  56.                 ret = read(connfd, buf, sizeof(buf));
    ( f- u: M* F5 R2 i; n
  57.                 if (0 > ret)
    4 T9 u* o5 J5 s8 p3 U; q
  58.                 {
    $ {! n" W! a% F% E* _2 l
  59.                         perror("read");
      z+ W; X+ u" z$ Q& K$ a+ {) \, H
  60.                         break;# X. L+ G" a# g' k7 K$ D  @8 M
  61.                 }//执行while循环读取数据,当& l4 A  t* K+ N+ ?5 P2 S2 u* M
  62.                 else if (0 == ret)% r* ]. G, x% I4 v; U" A
  63.                 {
    7 L! d) d% }+ h/ B# F* d8 ~
  64.                         printf("write close!* c; p* b9 q# Y, j% R  Z
  65. ");
    - K) K, X  W% D8 g
  66.                         break;8 z% ]7 U* G% I4 d1 a- e
  67.                 }4 \& x5 C( {( J2 l" v% a0 L$ h
  68.                 printf("recv: ");" P/ {- x1 A6 L1 P$ G# E: y& p
  69.                 fputs(buf, stdout);//打印接收到的数据
    5 h8 \" a% h' E4 V" n
  70.         }
    . g' a- w1 [; ?9 u, G
  71.         close(sockfd);//关闭套接字
    * F' W/ ^1 l. k  M5 |% Z2 J
  72.         close(connfd);//断开连接. y2 l. ]$ J  c  }
  73.         return 0;
    , K0 V  T3 Y* y1 J& Q* V
  74. }
复制代码

2 s2 q7 A% \4 `: S" t" ?- s+ ^9 d4 r3 Z" i0 {
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)  @. _1 X( g- I
  2. #include <stdio.h>- [- P. ?/ A+ w; ~: h
  3. #include <string.h>
    # t, C& y) X( k3 A7 {) \7 c
  4. #include <stdlib.h>0 z. y& d8 M; q* A; k' P
  5. #include <strings.h>! x9 N8 |6 z6 X2 Z& z$ y  s
  6. #include <sys/types.h>0 ^8 e: k2 M, d% c2 K
  7. #include <sys/socket.h>
    $ O+ R8 I3 K  u1 R# f4 t
  8. #include <netinet/in.h>! z! q% s* |* Y  L
  9. #include <arpa/inet.h>
    ! F9 O4 W$ Z0 F8 `+ r
  10. int main()
    4 ~% N4 Q/ S' V6 S: N7 I
  11. {
    . k' _  r3 D1 C, S3 C2 \
  12. int sockfd;9 t$ e! f' T/ E  b! [
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    0 v" ^' u: w$ j" @0 y
  14.         {
    1 v) S4 e  w: V8 E
  15.                 perror("socket");1 j( T4 m7 h+ h2 ~
  16.                 return -1;$ M  A5 ^- I# t, B. m
  17.         }
    - l8 h& A9 L6 p4 x8 y
  18.         printf("socket...........3 I7 T& ^1 e. [4 O+ `# V7 @. J
  19. ");5 E5 y' Z) h7 a" |1 |
  20.         
    ' j  A* v+ e# I, k/ T; \9 ~
  21.         struct sockaddr_in srv_addr;
    1 {) q5 ?% ]9 H
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    ; \+ Q! T; N- t" N, t
  23.         srv_addr.sin_family                 = AF_INET;$ t. l9 j1 t3 t
  24.         srv_addr.sin_port                         = htons(8888);
    ! B* ~9 O0 I6 [: y0 w1 u" S4 R, I
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
      w7 G- h5 t) q8 a
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))): V) I) S+ U- g/ p* _2 ?: j
  27.         {- x+ {5 k9 z) u
  28.                 perror("connect");
    5 x& [- Z. r8 B
  29.                 return -1; //exit //pthread_exit
      W6 E. o) k- P7 e
  30.         }  q0 G  G3 t0 u/ C6 A0 {; w
  31.         printf("connect..............! v/ [% T' G7 Y/ h/ U
  32. ");( F" r$ o0 p5 }* H  P. W
  33.         char buf[100];' s: Z/ m0 _9 g6 e5 ]3 L  }* t; a
  34.         int ret;
    / n* v; ~& {# F9 w
  35.         while (1)" a7 V- N9 G1 B4 C, ~; v
  36.         {/ n3 t9 E" W7 P( c. B, T( u
  37.                 printf("send: ");
    & _. C5 I( S* R8 Z' a/ t
  38.                 fgets(buf, sizeof(buf), stdin);
    / @3 R( P: h/ T( _* I6 t0 j1 h9 r/ E
  39.                 ret = write(sockfd, buf, sizeof(buf));, ^: y# o  a% ]' N6 r
  40.                 if (ret < 0)5 S- a3 E. \0 y, M/ D# F8 i
  41.                 {
    0 H# h0 j1 T( U1 t" A
  42.                         perror("write");! t; N3 r/ B! j) V3 c
  43.                         break;
    : t. h4 _- B- m  |! e" m% R
  44.                 }
    ; y3 H9 z; q4 @# @
  45.                 if (strncmp(buf, "quit", 4) == 0)9 E' u  m% L# Z( L0 _
  46.                         break;
    , Q4 S) u& x  L! \5 ^% {; h) V
  47.         }1 K% L- ]& ^9 o" X: r0 F8 |
  48.         close(sockfd);: D! t+ D7 l* h& X) l
  49.         return 0;* I0 A- N" Y* g  C6 `% k
  50. }
复制代码

0 f3 B9 w# m# N7 \9 ~$ V* @. }6 b3 g- a! w: z
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-5-2 11:48 , Processed in 0.057744 second(s), 23 queries .

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