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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
4 i( o3 V2 p7 g4 |, c" K
" }8 c/ [6 e. q, ^) ?6 ]/ A

; H# a. ^% U, U0 Z* F7 M2 gsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
" ]3 H: c7 X! }2 k4 l' y. ~% F  ]; V# U( m( C8 X# O* ]3 Y

( s6 P: M) i, n: S, uTCP协议
  @( g9 V, p6 k. F2 CTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 [! _% r7 V# z$ s6 b, b( {8 Z8 o5 J; P) i* a+ t. t
- O) y8 \5 ^/ ~' ~' t
关键词:三次握手,可靠,基于字节流。8 U$ f5 Y: q4 l- Y5 `
8 d9 N9 G" ^5 R8 S! g
& ?# g; a- a2 f0 Q# \
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。. e0 |) S3 P8 V: f* U: F
- y4 U5 _) m1 r( A& p$ P# G5 @
TCP服务器端和客户端的运行流程+ A0 s( E3 E" j. W  m4 S
; l' ?1 d9 ]+ ?1 d+ N1 f

/ T% r1 x( m6 k# ]) q$ T- j如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?+ @4 _5 ~  A6 W5 X* W1 m
/ ?  \& r9 P9 Z: F2 C2 }

' V% V' U0 b! }% C+ H: C1.创建socket( }# H/ Z  N' m  B0 m
socket是一个结构体,被创建在内核中5 Q1 D+ p7 d" |
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
! v: ~+ L# l% O7 L- H
+ K4 r$ h& F# @

3 m' b8 \# ?( f8 v/ g2.调用bind函数
& C9 ]2 y* v" |: F 将socket和地址(包括ip、port)绑定。4 A  c9 A$ }* Q8 w2 X7 g* D
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序2 t, M3 C+ b) P, x) x
struct sockaddr_in myaddr; //地址结构体
9 l, ^3 f8 P; t5 b6 j bind函数
8 U" V5 M- f! h$ x bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
9 e8 F! V4 w; h$ Y# k6 h3 J6 a! L  u; k  ~$ c& _
) N, m( P5 r& R+ C
3.listen监听,将接收到的客户端连接放入队列, K5 \" f( j7 ?1 a7 t
listen(sockfd,8) //第二个参数是队列长度
; r( E( f5 K; a8 M2 ]9 |6 L; v1 g0 {4 T  A2 B2 H  @

$ p: Y3 B  A1 f2 r; I! o1 p4.调用accept函数,从队列获取请求,返回socket描 述符4 J' R% M& w% e) A' `: G2 k. N
  如果无请求,将会阻塞,直到获得连接4 q% T) |3 L% Y* M  h# q5 V' P8 a9 |
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
  N" T9 x; ]- Q0 R; N8 @0 {# n8 n% c. H/ y& \8 o  e7 ^& k$ T7 X

* |2 l* O. ]/ c' i# ^( @, y7 z1 N$ o, M5.调用read/write进行双向通信
% u4 Q* w' o& v4 O
! u3 |9 n$ I, @* Z# S2 M

% C4 N. r+ s9 D& z8 p6 R6.关闭accept返回的socket/ f' a. ]: F1 k% P. |
  close(scokfd);
$ s& I+ M) [2 |. D9 L6 B  v4 z7 `. Z' w7 S( k$ d" \
; b8 S# e+ `  a+ V# Y3 K3 A
$ b& R5 F" g7 \4 p) j6 |) u& ^' W
2 E# x: M% Z% Q- {8 ?
下面放出完整代码2 a  T* i4 O; }8 v9 [: W

9 p8 Q9 y, U( h0 c7 @0 U$ ~7 k
  1. /*服务器*/  G! f, S  b* Y9 U0 l' ~
  2. #include <stdio.h>
    ; `3 w8 A( d3 v
  3. #include <string.h>7 n/ Q" r9 g( R6 `& V' V, e
  4. #include <stdlib.h>
    ! |" g' N, L$ Y1 R/ H/ L0 R! ]
  5. #include <strings.h>
    6 L, {; K: I( @* e0 M& m
  6. #include <sys/types.h>- @$ Q; h- T# W1 l
  7. #include <sys/socket.h>
    3 [$ T* ?- u5 ^( l8 Q5 s
  8. #include <arpa/inet.h>
    9 a" n0 I& N) E- z5 C' R
  9. #include <netinet/in.h>0 Z0 H) b$ j( Z  J, i5 {
  10. int main()6 x/ x' A8 X2 |2 ^
  11. {
    3 v4 _8 _1 k/ r5 D1 V
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    * n+ Q# a3 G# U
  13.         if (sockfd < 0)
      }" G: ^  G* H9 @
  14.         {
    , k6 E8 B" G9 c. w, \9 n
  15.                 perror("socket");
    $ X; B1 D4 G& Q  v5 W7 Q/ r( g
  16.                 return -1;
    ( U, {3 x8 x  m0 e
  17.         } //创建失败的错误处理- g1 l$ L9 O% ^/ |
  18.          printf("socket..............
    + g9 R9 Z# C1 v
  19. "); //成功则打印“socket。。。。”1 L3 A, j7 ~/ M; t
  20.          
    # [' r9 y* w. @
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ; [2 G" }; O4 ]" m
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    9 P% ]- `; j% |3 o8 A# Y' e
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    8 H2 `  e3 ?% k9 k; C& m
  24.          myaddr.sin_port                 = htons(8888); //选择端口号$ I( V4 Q; N: p7 j+ t
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
      g3 D7 D$ _6 a' I) Z4 I0 t
  26. 2 }# B9 A  S) ?
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ; }5 O" i1 n5 ?
  28.          {
    . m: i% V; ^" c$ s  u/ R
  29.                  perror("bind");
    4 }7 [+ R+ C$ r5 A5 _* |) A& n( Q
  30.                  return -1;
    . _6 f4 A9 Z8 y7 ~
  31.          }5 u( ^8 e7 i+ c0 c
  32.          printf("bind..........7 `2 K- v8 r4 @
  33. ");
    * m5 P6 \' d& E  r9 i
  34. ! ?6 f$ W" W( Q& a5 Z2 I
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    0 B3 j! Y5 u5 p3 ^! }: [
  36.          {
    3 D6 N( ~4 Q4 X9 a( I
  37.                  perror("listen");# B5 x  p9 e- x1 r; {* A
  38.                  return -1;7 p4 f' A1 N& N" T; ~( H
  39.          }
    ' u+ [- @. z. \& X
  40.          printf("listen............
    5 x6 v! ~6 o( r) s8 ]
  41. ");; R7 M& p  ]/ G, L
  42.          
    % ?2 g" a. [# Y% Y" I2 h/ z, C
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    & k0 h1 U- @/ i$ n9 S* ^
  44.         if (connfd < 0)* ?1 |  J1 y9 }7 O$ c6 I* b4 o
  45.         {
    . ^/ ^1 `6 q8 D; @
  46.                 perror("accept");, p# E8 b- Q5 T
  47.                 return -1;, m1 b6 S4 \+ t- `, ?
  48.         }
    ! u5 H: T/ h% I! L
  49.         printf("accept..............
    8 m% S! Z- i8 a& H
  50. ");
    3 e: T: E+ I$ h3 a  n6 t. k; @1 I% k
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    0 i7 F  F. t6 T
  52.         int ret;+ s6 ~7 ?: o* G  l
  53.         while (1)7 p# M$ V5 A6 _
  54.         {
    ; R* m1 ~; L! u3 N) Q3 _% s
  55.                 memset(buf, 0, sizeof(buf));* A! O, Q5 N  z1 t$ m
  56.                 ret = read(connfd, buf, sizeof(buf));
    $ s$ X/ i+ _; w' e
  57.                 if (0 > ret)2 n  e( a/ q7 C1 R1 B' o
  58.                 {
    * i+ q+ U, G. X: [1 W
  59.                         perror("read");5 y, l1 h& d4 f/ B2 A6 D
  60.                         break;
    / J2 y9 f1 Q, ]1 L. E4 N7 ^1 s
  61.                 }//执行while循环读取数据,当
    . k# B% e, W8 a7 [$ `* ]# j- {
  62.                 else if (0 == ret)
    $ K6 k6 S( ^' Y6 T; X/ Q% _
  63.                 {
    9 G, S; z3 b. S' A
  64.                         printf("write close!- M/ F* e% C8 d: s- d$ A- f
  65. ");. k" ^6 \! _0 {" n. ]% w& \
  66.                         break;; _% A! T0 l% O
  67.                 }; z( C+ n& t# T6 p
  68.                 printf("recv: ");& O* E* G5 a! w; Z8 l
  69.                 fputs(buf, stdout);//打印接收到的数据
    : v3 |+ J' g; t& o
  70.         }, ?" i1 R0 m& F; f8 [9 J
  71.         close(sockfd);//关闭套接字% T% M7 X5 B$ s! l* N2 t; m
  72.         close(connfd);//断开连接
    / q- z7 {3 V; T) ]* a9 B) j
  73.         return 0;0 r$ B$ Z5 A! p' h  h
  74. }
复制代码

) @1 i2 @4 T! i  Q
; v1 t& S6 V9 A( K7 O
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    0 {6 k# `2 Y# j) ]/ V
  2. #include <stdio.h>+ `7 Z7 _% b3 N0 h* x
  3. #include <string.h>
    9 J$ n& H8 s8 K8 J) ?+ c$ z
  4. #include <stdlib.h>( M# U( t2 x$ {
  5. #include <strings.h>
    : \, A4 W- |8 b. G& [4 }
  6. #include <sys/types.h>
    + c8 J* y. C5 P# ~
  7. #include <sys/socket.h>
    . A3 A% A8 w* g5 x+ N
  8. #include <netinet/in.h>
    1 H  r: z# y* E
  9. #include <arpa/inet.h>
    3 c8 l% [% u; M
  10. int main()+ f, @/ [5 j) F& q
  11. {
    " T+ N/ Z; ^3 Q5 P9 g, D" y
  12. int sockfd;
    5 _4 J3 X, u$ z0 F# h* m5 D
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    + X. Z; i; F" y( P9 v$ R2 Z; h
  14.         {9 m" n; K8 `* X2 [- [$ p: Q8 u
  15.                 perror("socket");/ |8 R- D. ]# v+ `; n0 o
  16.                 return -1;+ E3 r. r1 Y! p. _( A0 s
  17.         }) b7 z1 a9 m/ ]/ d5 |. j9 x7 }6 Y
  18.         printf("socket...........
    ) {$ J% Q. h0 a& h
  19. ");. w9 T  q% b+ x
  20.         
    : G/ ?8 p8 B! D9 A  D: _
  21.         struct sockaddr_in srv_addr;  l8 f7 t" ~, m6 Q% Q; K2 A
  22.         memset(&srv_addr, 0, sizeof(srv_addr));8 B3 l' v$ o- q  p7 S8 c; M
  23.         srv_addr.sin_family                 = AF_INET;' u; l: N; l# h- x& c% G
  24.         srv_addr.sin_port                         = htons(8888);
    6 M; B& b' m0 x5 N1 X
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    4 ?: ^( ]" L! H" }/ Y+ \$ X4 r7 q
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    - Y; L% p7 r6 F7 A/ Z
  27.         {/ T) ~1 d6 D) u% ]) l
  28.                 perror("connect");
    " O2 x. |( u  V' l& D
  29.                 return -1; //exit //pthread_exit
    , s+ f6 n+ O$ F" a0 [4 J! R
  30.         }  V! V. f# W1 ^1 k3 [: y$ E
  31.         printf("connect..............& I2 X: T7 G3 G2 r( o/ h( M
  32. ");
    $ F* X. j' n6 K! y/ i/ V- F' M
  33.         char buf[100];8 W9 U2 l9 _' k9 Y7 ^. f: E
  34.         int ret;
    4 w+ K- g! A+ [5 K6 o% C& d- i) f
  35.         while (1)
    9 @$ H& h7 q- t- T  {
  36.         {
    ! ^/ {7 m7 Y- W- C$ J# ]
  37.                 printf("send: ");0 D7 Z3 U3 Z; l# S: n5 ^
  38.                 fgets(buf, sizeof(buf), stdin);
    0 W" b7 R' I. W
  39.                 ret = write(sockfd, buf, sizeof(buf));) W# [. Y2 i) ^0 n# W
  40.                 if (ret < 0)
    0 V  g) {7 m( H) Q& y
  41.                 {0 z. q& D4 g: ^9 {+ r
  42.                         perror("write");
    / U* _- g1 ~8 z
  43.                         break;
    ! @8 d# i7 ~8 F2 F7 z3 k
  44.                 }
    8 T6 m4 P$ `' a; O
  45.                 if (strncmp(buf, "quit", 4) == 0)+ [# R) a+ |  K+ R6 B' ^1 K
  46.                         break;
    1 ]0 d" Q2 B: ]0 K& x
  47.         }1 a: u) v' j) z
  48.         close(sockfd);
    + A8 S+ ?7 V3 S& p! K
  49.         return 0;( ?. M( h: Z  }* F9 d: f; g
  50. }
复制代码
# z7 |! G$ X$ V4 u9 E

# L& T; I# q/ v! {' ~; B" y& `
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-16 16:58 , Processed in 0.062877 second(s), 23 queries .

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