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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

8 q9 D0 g2 u, u, asocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。$ x; w$ `0 `6 ]1 _: L
2 e' s* w  a$ Y

* D% J5 _# P3 C7 OTCP协议& ?( z3 {! G3 h9 h- ~; U) G
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 a+ M6 E# _# c" k& Q) a4 r6 D  B4 w/ e9 r& a( L* w. F
, ^1 q" z# g: B
关键词:三次握手,可靠,基于字节流。3 x3 Y9 \' Z4 U! w* X

& @& D  i7 f. Y" H

: w' i' o) v( n  n. D: m7 S1 @2 u3 d可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
. [, m# W; f" C  b+ {* `7 V / n3 o9 T$ H5 K( `9 H4 \
TCP服务器端和客户端的运行流程  M4 N4 y' q# r9 b" r  Q

8 t. }$ D* m4 Z2 j! I+ }+ d) g

4 f, f' C: q7 z4 v1 E. n如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?, a8 q4 d% A0 e: ]
; h9 K' w* _, v6 ]. Q: F

  B' x5 w* [! r2 h" q5 y1.创建socket+ L/ W$ d% K0 \: F! h; r
socket是一个结构体,被创建在内核中
' N0 k) a- ]7 ~9 k7 Y sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
. V1 z3 }6 M% }1 }7 K* n  w: Y. k, j# ~- x4 g, A
, j2 r- ]: M% M- B: v, C
2.调用bind函数
' W2 V, A; q9 z/ f7 \1 ]% d! z 将socket和地址(包括ip、port)绑定。
" n; i! h( _( y1 U% j: \ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序$ e* o; A( i4 ~+ D7 T8 M' L
struct sockaddr_in myaddr; //地址结构体
* o4 h* v2 t7 ?1 L; r1 K' ] bind函数
6 j8 i9 i! _) C6 j# O0 X- I- b bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% N$ H+ r2 t$ F5 ~2 `9 ]' i& f
! G4 c" c( c$ N' |' C! \9 p/ u
( N: ~- m5 x1 L1 j
3.listen监听,将接收到的客户端连接放入队列
4 R, g! h0 A  x, N$ R listen(sockfd,8) //第二个参数是队列长度/ \% H8 p$ ^, q* W- G% ^+ x

* }2 E4 m- _5 s& \& [9 x" Y# b
. r" B( N& q3 X6 Q
4.调用accept函数,从队列获取请求,返回socket描 述符4 G! ]. j  h8 w/ H
  如果无请求,将会阻塞,直到获得连接) \& p9 @. G% u/ l
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
/ B! K3 D4 ?  m* Y1 q; B8 o! r
6 y# H$ X9 G  i1 A  {

" e6 y* K# e7 u$ `5.调用read/write进行双向通信7 C; R4 x3 E. D, F) k! `/ p

, r$ M8 j. p9 X  ]$ N4 {. z1 j1 E
8 ]% k; D  z0 {$ g1 H
6.关闭accept返回的socket- J; @$ X4 @' l3 d- Q3 q
  close(scokfd);& D3 ]5 j# c: x: c! _- `
* d" [' c% P3 R5 _# a7 P# a/ o: W/ y

. K3 C4 s4 O  f; F; @
4 C7 d/ ~4 S/ m. k
5 K% A* F. o% \8 T
下面放出完整代码! _1 @6 K% B) d1 e5 m! _

) ~& F, N0 B$ c( {1 m  X
  1. /*服务器*/
      B" R, \' w6 S' V+ K* Y6 ]
  2. #include <stdio.h>" z# J# H: O1 w6 P( i
  3. #include <string.h>. f7 }  K# i8 e9 _1 d8 t
  4. #include <stdlib.h>8 X8 Z) g* s7 B$ {& {, G
  5. #include <strings.h>
    4 Z+ T' c6 i, p. M
  6. #include <sys/types.h>
    0 G" e9 T# P( E
  7. #include <sys/socket.h>
    % @7 i6 \$ H5 R$ k) w
  8. #include <arpa/inet.h>4 n6 y: u/ A4 K# z+ E/ @
  9. #include <netinet/in.h>
    " ?& M% e8 b% G% @! O
  10. int main()6 H3 K* L4 q  I, L/ L
  11. {3 F4 J9 U! a  l& |/ `
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ' @1 }6 ?* ~) b( ~
  13.         if (sockfd < 0)
    % x* R& I4 ^  a5 q0 G) t
  14.         {
    * E- }$ A+ j  i% e: J9 d5 A
  15.                 perror("socket");
    ! K5 D/ @0 C' L- G0 ^
  16.                 return -1;& y% N! z( c+ d0 y! L4 M+ a0 a
  17.         } //创建失败的错误处理
    6 D" A* T  R- F% M# [
  18.          printf("socket..............; m9 [" d% N1 q/ `6 b
  19. "); //成功则打印“socket。。。。”  y1 y! `# a* o" h# K2 t
  20.          " f: y) ?3 ?; z% g2 H
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体+ K2 Z, |( J( m& s: G3 R) S
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    - d# d) g+ B9 q9 Y+ x5 T+ R: a
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ' `0 r. {$ \( E8 l2 y. `/ y
  24.          myaddr.sin_port                 = htons(8888); //选择端口号( H. ^" b8 e; \) C
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址- Y0 W# z, p. R1 S7 v. m2 m

  26. 6 O+ D' Z0 T6 x. K+ ]& s
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字3 t% E, Y: H) ]  f4 K+ O
  28.          {" P2 ?1 J$ V6 g+ a9 m* S
  29.                  perror("bind");0 x1 Z9 h! M4 Q% l3 L+ V
  30.                  return -1;
    * p+ V( ^0 ~4 k0 `8 H* B
  31.          }, {% W) s% c% [3 M: ]6 o
  32.          printf("bind..........
    ' i; O0 ]7 k/ A# H, m$ |
  33. ");
    ! [# G  [) U7 b8 Y- q; S

  34. ) {) T& V3 R$ c$ ?9 C5 l% k# i4 R
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听' }! _0 N7 X* g( L
  36.          {+ z$ l( z  {* n  ^  _% O! X4 L. [
  37.                  perror("listen");" f+ k- r( P6 {% n
  38.                  return -1;* S4 c  _+ s5 e2 V+ g' U5 u
  39.          }
    # r( q& v, s6 O# o/ j
  40.          printf("listen............% _0 l# }! ?2 _7 g% S* t" W. \
  41. ");
    " f( v  b6 v% T4 }: Q
  42.          ( r( H) H. a3 d1 n% M* u- z
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ( ^8 w- t% J; u, l) E
  44.         if (connfd < 0)3 s$ |$ h( r5 t0 C1 r* o' k
  45.         {
    : l  T4 m% j3 h- U2 H/ X3 Z
  46.                 perror("accept");
    ( H. C' A& p$ c7 Y9 j/ B4 D
  47.                 return -1;7 h1 p2 \5 |: [+ {
  48.         }
    9 _- u* i$ b* W. L5 v# i; u
  49.         printf("accept..............
    6 U5 T) A) j/ A5 q' T: K9 f
  50. ");8 L4 R* J0 z7 N) I: `3 @& J
  51.         char buf[100];//定义一个数组用来存储接收到的数据, x; o( w% l" p1 S& z  G
  52.         int ret;8 V$ f4 i: @0 Q$ o0 Q# N
  53.         while (1)- X) X- n3 s2 e5 m0 ]& q7 P  x5 P% y  Y
  54.         {2 L% m: x" [4 Z! s* l5 B6 _
  55.                 memset(buf, 0, sizeof(buf));. C/ _2 f& a) ^' |+ N% d' b
  56.                 ret = read(connfd, buf, sizeof(buf));
    4 ^# i0 ~" o8 q, n
  57.                 if (0 > ret)
    * u" o) l7 D8 q+ |  b
  58.                 {
    $ G0 B$ W$ ]% i% w+ J
  59.                         perror("read");
    4 X- ^1 x& J8 D* n6 b
  60.                         break;0 c3 X& N  i3 J$ d% `' Y$ M
  61.                 }//执行while循环读取数据,当# i7 `+ R8 T* X" k. T$ p% n' M
  62.                 else if (0 == ret)% D. R, w+ D- ~4 K! i! u9 [
  63.                 {
    / w) X4 X1 l5 J( ^- s( h
  64.                         printf("write close!! o9 E4 h2 B" s# }- V5 [
  65. ");  y6 h0 o# @1 Q& v& y" C9 S
  66.                         break;2 e9 s2 A' y0 D1 C9 w
  67.                 }3 |& F( k) \+ k2 R) x
  68.                 printf("recv: ");; `4 [! e4 Z4 U# `* H
  69.                 fputs(buf, stdout);//打印接收到的数据
    " o, X3 `$ y- @5 g
  70.         }6 m# c3 ^3 M: c
  71.         close(sockfd);//关闭套接字
    1 T) t9 C( J  Q8 f2 ^0 I0 y
  72.         close(connfd);//断开连接" K8 m) m! F4 f; @
  73.         return 0;6 I5 s2 O# `% G. I  ?' t5 o
  74. }
复制代码

' K; f$ u* Y7 N4 ]3 s  h* {6 x1 L' f# F
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释): O' U' t# H5 U1 s) y! x: v
  2. #include <stdio.h>
    ! C: W9 f% z5 t# j2 z1 f. d
  3. #include <string.h>
    " |6 e. g7 m) I. w
  4. #include <stdlib.h>! W. |! M  H6 ~5 X( h
  5. #include <strings.h>
    # [; d# G/ d) X
  6. #include <sys/types.h>
    9 e/ L* K. O0 o
  7. #include <sys/socket.h>
    0 T+ N0 p5 ^3 B  k% r; _8 j  m0 B9 T
  8. #include <netinet/in.h>
      P+ [# ]& q! x) s1 W
  9. #include <arpa/inet.h>1 H7 Y* T, i- Q0 w9 X6 I# B1 z
  10. int main()
    . r0 O$ j- @  ^% j0 j
  11. {
    8 C8 I1 f- B$ P' {
  12. int sockfd;3 t: B3 L9 p$ _- @
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))! `, [& F2 w  D8 f9 A3 `
  14.         {! d) }1 k7 w) J. b9 s) [
  15.                 perror("socket");
    8 l1 ]5 a3 O( Y/ x2 w  q% o+ @
  16.                 return -1;  R6 p& x3 w6 o. O; y. Q4 E
  17.         }2 W* h* C$ D+ _( }; v
  18.         printf("socket...........
    ) @- a/ Q% J2 y- G! q. W7 v/ k
  19. ");
    3 W( v$ d. Q* \6 O
  20.         2 R) v5 O2 [& u2 S: J& a$ w
  21.         struct sockaddr_in srv_addr;; V9 m' q) k: i1 O
  22.         memset(&srv_addr, 0, sizeof(srv_addr));8 N$ K7 T% H0 ]1 Q
  23.         srv_addr.sin_family                 = AF_INET;- k1 z# P4 o4 ^2 x0 a
  24.         srv_addr.sin_port                         = htons(8888);' b( f. s* L$ o8 s+ u- c4 a
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");& t1 K' z4 z; o& X) E
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ) J- x. t) z0 ^2 E4 Y
  27.         {0 X/ W6 x& A% J2 ], a1 N+ P. m
  28.                 perror("connect");) I% V6 O' g0 c7 u, o" \4 d0 d
  29.                 return -1; //exit //pthread_exit
    + t+ `+ z. f7 T
  30.         }$ v2 H- b0 b, ]# I& R1 v" z
  31.         printf("connect..............5 Z7 t/ A) E6 c8 ~) q4 n  K3 \
  32. ");' p$ _( F9 L: \8 w& p: y. U
  33.         char buf[100];5 W: [0 c1 Y6 z1 B% i$ P+ ^8 C: \
  34.         int ret;/ m6 V8 D$ b2 ?0 q1 E6 h
  35.         while (1)
    ; Y% u: f  L1 b1 d; k
  36.         {
    ! R) Q( t$ m& s. u9 z- O
  37.                 printf("send: ");" l2 a0 J# a1 @: }0 c+ Y4 x
  38.                 fgets(buf, sizeof(buf), stdin);
    ! o& W, y' R& m+ ~
  39.                 ret = write(sockfd, buf, sizeof(buf));
    $ @5 o9 z2 z, @* B0 c5 u) H
  40.                 if (ret < 0)
    - U. e$ w7 X- I7 C+ ^6 F8 K
  41.                 {8 F' s! C( ~" l
  42.                         perror("write");
    , f) h* W4 o' B* e/ p) ]
  43.                         break;
    5 Z6 h5 w2 ]6 {
  44.                 }3 M7 Y3 [, p6 H1 C) C
  45.                 if (strncmp(buf, "quit", 4) == 0); \  h1 D( T0 Y" v
  46.                         break;( g' v0 ~; B, Z! j# C0 K
  47.         }
    % @3 F+ J3 m; U2 g" A1 b
  48.         close(sockfd);
    & K- e& _$ F" S) k
  49.         return 0;
    6 g$ k0 p5 j2 f/ E
  50. }
复制代码
4 c/ r1 Y  o1 \. B* e
$ M# F+ r# y. y$ C1 q
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 08:14 , Processed in 0.057576 second(s), 23 queries .

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