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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。0 x% k5 D6 {9 p: B7 ^7 U
/ N1 K. l- k4 _3 H, `

. z5 u9 a6 }4 |$ p/ Msocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
2 G( x2 m' m8 P
7 C8 i1 X" w3 P

- \+ ^  `0 E1 VTCP协议: A5 Y" [( |" s7 }# v
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。: O* A2 p. A2 G, P

( z# y1 r5 u+ b/ Z  }

4 n) J- T2 e- n6 v7 Z6 e关键词:三次握手,可靠,基于字节流。' p5 y1 j3 _0 {
+ d3 E3 A, Q7 G9 o. I
3 X! J( ?3 l. |4 O& x' ?3 s, k
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
3 c. b* D. O& l* J2 Y! G ; w; j6 u1 t: r2 x3 T* |! B5 C
TCP服务器端和客户端的运行流程$ O5 h9 ^3 s1 e9 F

; Y- T, a! I6 ]  [
) M' G4 v: W% Q- A4 \) _( V/ m( B8 j
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?$ A" S# ?; A4 j+ W5 O: p

  ~7 P) n/ T) I5 x

6 X+ [) m: E! s$ j1.创建socket4 q: E' p- \. N% G; k
socket是一个结构体,被创建在内核中
3 W; e* t  V  [9 M! t" z- [: D sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ q4 D' b! m) J: _. E$ N

- G* O, X4 e# Y6 z- U
$ |3 _! f& f% R* \6 P$ k2 M6 X: ^; H
2.调用bind函数
: c$ d" u! c7 K( p: i2 _! w7 b( m 将socket和地址(包括ip、port)绑定。
7 a; p9 [. L6 M0 v- U" i 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
+ O# y4 t  m8 g8 `3 ~& T struct sockaddr_in myaddr; //地址结构体
3 _! [4 s/ J7 a2 A+ y1 ` bind函数
9 n6 [8 S9 c! \& W1 b9 S bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))  G" F" r/ I& Q- o' ?

# f% w7 l7 U- Y7 J7 u

" f, d/ Q' P  l  }% l  N  r3.listen监听,将接收到的客户端连接放入队列
8 d3 L' w0 O: E+ a3 O" T, C& o listen(sockfd,8) //第二个参数是队列长度
( [8 {6 E" t! X. B9 |! O0 ]* n* Q% }/ j' P

  y" [4 g2 C; l, T4 X2 U4.调用accept函数,从队列获取请求,返回socket描 述符2 D5 [- t! l3 C6 y" s0 t
  如果无请求,将会阻塞,直到获得连接/ N; l) p: O/ \3 n8 T
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数+ B8 U9 v, C5 i

7 x) V3 G  @1 H6 d& I1 M! w

0 R5 T- h" r: A/ E" m0 F# r5.调用read/write进行双向通信
' V$ `7 l& {$ |0 Y& K
4 i6 i& ^! j5 P( e
$ n4 T- ~/ ~' K, \
6.关闭accept返回的socket- y- F5 [# y, Z; J' T; Z+ B
  close(scokfd);. y7 V+ v: i& M0 M: w) V3 o4 W3 f% I

" r% ]$ p8 z& n+ Y  d5 [

+ g) h* O/ n3 a
. [0 [' S0 @" \0 ?& Y

0 O, h1 `$ W( S8 l& Y下面放出完整代码* ^. R0 J' ?, S- O# o

3 f. K% P) t8 q
  1. /*服务器*/
    ; I/ d$ k" W1 t) A
  2. #include <stdio.h>4 E5 O6 O6 W. t" B* }: w7 _
  3. #include <string.h>8 N6 z; J2 j' \0 t" W, k( M8 Y
  4. #include <stdlib.h>* R4 ]6 W6 n& p- _: s5 G, S: Y
  5. #include <strings.h>
    - b4 z, y+ ~8 l2 `1 @& q) ~
  6. #include <sys/types.h>" Z6 w  r" N: V8 j7 ^( I2 B
  7. #include <sys/socket.h>
    2 a/ S, A" \5 s% V
  8. #include <arpa/inet.h>
    8 v1 o$ _5 w, K; d8 m% u) w7 I3 e" u: e
  9. #include <netinet/in.h>
    6 M2 i3 r1 P- |; j/ Y+ I
  10. int main()3 i0 Y$ D1 i- Q( \
  11. {$ q( X+ n# ?9 T$ Y2 r
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字. c8 Q! n9 n6 ~
  13.         if (sockfd < 0)
    1 J  |; H- h- N6 J0 Y
  14.         {! i8 E  C4 K7 V+ _8 C+ w+ \
  15.                 perror("socket");+ R" R7 f! ~( o  u0 u1 W" }& T# ~5 D
  16.                 return -1;
    2 w  D) R% v4 k* @* D% h: k, x
  17.         } //创建失败的错误处理& Y8 l' D6 D' d2 A+ ]
  18.          printf("socket..............
    " K1 }% M+ D& w0 M" S# R2 c0 i2 O
  19. "); //成功则打印“socket。。。。”5 u# \& u3 n  M
  20.          
    " ]9 |& m  x! Z/ g! N! h) o
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体7 I0 E; y9 y1 u; {' {; [
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)# g2 _+ ?! n8 N) a' }9 L
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    6 H$ j$ ^0 ?- y
  24.          myaddr.sin_port                 = htons(8888); //选择端口号6 R/ Z& @5 A# ~- S  p7 C" S" P6 @0 o
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    : ?+ O; O0 \( Y7 F

  26. $ X! B  q# m& S* U5 u% {- V. ]& _4 a
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    3 L1 L: o# K" K9 z: J) A! g; C
  28.          {/ m* S, m5 ?6 x8 @" `) v" X: {
  29.                  perror("bind");* u7 c4 |) x9 r1 d1 t3 `* Y
  30.                  return -1;
    5 g3 V- b% q" B- m" a/ ^6 J
  31.          }
    : M) h: g7 W% }+ ^
  32.          printf("bind..........
    % M3 @3 p1 i+ ~" a+ J0 K
  33. ");
    9 ]5 j$ f$ U/ p% J: p+ r

  34. 3 l# m  X, t/ }. U, C
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- m; @7 r% Z1 F1 i0 u
  36.          {, W! S9 O2 p) I4 ]* g# Q$ s+ X
  37.                  perror("listen");2 Q! u- g( ]  b+ K' `. s
  38.                  return -1;2 {4 E1 D2 u7 O1 J3 H; g! ~% g2 Z
  39.          }+ @* M3 q, h  b
  40.          printf("listen............
    ; o' d) `4 Z8 a. H0 q  l) Q3 p
  41. ");
    2 Q) C3 Z3 l. b" R
  42.          + N* W+ C0 o2 \8 Z) Z3 Z* ^
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求% }9 t- L0 v5 n3 U% b4 x  n. n
  44.         if (connfd < 0)8 j. f7 _# N6 l
  45.         {# _1 R$ D5 J9 r: L
  46.                 perror("accept");% Q' ^4 T+ V2 U0 k! E2 e# V
  47.                 return -1;3 L) n  Z% m3 ^+ Y
  48.         }
      z2 o/ h, q/ L' U0 j* @7 j5 I3 \
  49.         printf("accept..............* v+ L( Q5 Y5 B
  50. ");
    ' ~4 R6 i% _2 g
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    $ Y, t0 o$ q/ x6 G  A3 H, E
  52.         int ret;* j2 ?, u) X% `* L3 {" x5 g* A
  53.         while (1)1 H9 L, m4 Y6 l$ f' |7 x
  54.         {
    6 m, f% X- H2 c+ w* U: K
  55.                 memset(buf, 0, sizeof(buf));
    3 w1 t1 K" k0 d' f9 e3 Z
  56.                 ret = read(connfd, buf, sizeof(buf));& j/ U2 G% ~: ^" {
  57.                 if (0 > ret)
    % Q7 D2 }- P" i' V) F9 r+ O
  58.                 {" F& h3 z( I6 K0 e+ p: E8 C: Y
  59.                         perror("read");
    $ V. Z+ r# O2 q, E. c! x# P* F
  60.                         break;2 Q  h' Y$ t3 ]5 H  m/ A3 V9 C
  61.                 }//执行while循环读取数据,当! X8 b& n7 q: B/ c
  62.                 else if (0 == ret). i! S' g; ~1 ~5 E; V. w
  63.                 {5 W' y; h7 R! M, [5 }
  64.                         printf("write close!6 c7 S7 E6 @) W, E* ^/ ^- j
  65. ");" e! I& r& {/ f( W) M! T
  66.                         break;! f  q( s) F$ ]; p3 B3 X" v) ^9 `
  67.                 }
    # U7 \. C% d+ X6 J5 ~0 T
  68.                 printf("recv: ");$ C0 |  I+ t( i  J1 z9 a' ?
  69.                 fputs(buf, stdout);//打印接收到的数据
    / X. s8 _: B% J- y% B7 R4 i
  70.         }
    ( h2 B1 Y7 E4 B
  71.         close(sockfd);//关闭套接字
    ; k/ e! M* T4 z/ o' {
  72.         close(connfd);//断开连接$ f. U. V2 q) [# t
  73.         return 0;
    ' F2 L& P6 F+ j, X& n' P
  74. }
复制代码
& _% g4 x! ?$ ~, h
' M6 `% ]( j! W( L" ], w
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
      G5 j/ a; f) x  @$ N' i  o
  2. #include <stdio.h>
    ; m: t2 F; z6 s2 l, Z4 u) M
  3. #include <string.h>
    0 r7 O; C% @$ g
  4. #include <stdlib.h>0 I# U0 F3 d. r8 R4 z
  5. #include <strings.h>  ?+ E7 H0 `2 ^1 h( b3 `  P
  6. #include <sys/types.h>
    . s& Z, h1 \1 k7 _% _2 q! w. [
  7. #include <sys/socket.h>
    4 [) G* ]4 U. B5 H( x
  8. #include <netinet/in.h>
    , j, S4 W/ B% j) k+ S% M
  9. #include <arpa/inet.h>
    . F5 V$ E, ]) a# e$ j7 k
  10. int main()
    ! K8 u- X* \- o. g9 N% X
  11. {7 {5 F8 C  L7 {9 A6 u( ^
  12. int sockfd;
    7 b! d8 g; ^3 q. a+ g0 D& k
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))/ E9 I2 \( p+ j8 o
  14.         {
    & j( R5 p! P- \7 w  T6 J
  15.                 perror("socket");1 q4 I3 r! a- Y& P2 B
  16.                 return -1;* M# a# C- }. [. D6 `
  17.         }7 z0 V7 Z! N4 \% u4 h5 z# R
  18.         printf("socket...........
    6 M* ~% r; F8 V
  19. ");
    ; a7 @2 b+ d$ A: {
  20.         
      B4 C! j6 `1 v4 _
  21.         struct sockaddr_in srv_addr;9 ~" \. J* G/ M
  22.         memset(&srv_addr, 0, sizeof(srv_addr));& W! y1 ]: j3 t; v# i
  23.         srv_addr.sin_family                 = AF_INET;
    1 f+ i2 T7 `3 Q
  24.         srv_addr.sin_port                         = htons(8888);+ x& l3 C9 _6 l4 {! X* H, Y
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    - R) Z/ J4 K* o
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))# `: ?2 v; a/ ?2 |9 |
  27.         {9 X3 P: |7 ^- i+ O
  28.                 perror("connect");
    " I4 D( w) \! J5 _; z8 K* j
  29.                 return -1; //exit //pthread_exit& N, i. t6 w8 h' q0 ^8 V9 s, G
  30.         }
    . |2 m& P* g* r% l# i8 [& S- {" ~% V
  31.         printf("connect..............% d- J% R. x6 ^6 u& b
  32. ");
    5 N  O9 n) J7 \/ x7 d. F. w
  33.         char buf[100];2 v( K  B8 }2 y, w* v
  34.         int ret;
    # Z& G* m1 k# y* d; V4 o6 ]
  35.         while (1)
    & w$ g  ~3 v# z9 H1 B" V
  36.         {) C' n, _, p* q& a1 w) [+ P
  37.                 printf("send: ");7 {0 I; P/ v5 u/ b: x
  38.                 fgets(buf, sizeof(buf), stdin);
    2 O+ |0 k5 g' f) W7 C; g
  39.                 ret = write(sockfd, buf, sizeof(buf));
    # |2 l6 M" d5 P2 R* R5 W
  40.                 if (ret < 0)
    5 g9 U, l3 B6 ]% j; C
  41.                 {
    , e; B; i/ v4 O. y
  42.                         perror("write");
    . x1 e% h$ R% k3 M2 m
  43.                         break;
    1 C; _0 k1 b: h2 c- C- S
  44.                 }
    # t2 u4 f0 K, @( [. J" s
  45.                 if (strncmp(buf, "quit", 4) == 0)
    & J) F  W3 g! O4 Q
  46.                         break;6 U: `( T3 X  T( v! D$ E/ K6 [# F
  47.         }
    9 R9 A" h! x: ~3 u
  48.         close(sockfd);
    % N( u+ f6 f% @
  49.         return 0;
    4 f1 f, B6 n& g" L& _' T% ?
  50. }
复制代码
: o3 g+ O' c2 }1 Q4 z# v% {
9 Y$ p5 S. j& i! l( m
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-10-24 12:53 , Processed in 0.074464 second(s), 22 queries .

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