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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
7 a2 r( D5 L9 D$ M
' p% ?/ B1 W  ^- ?/ I# A
% Z7 w* C& L  v2 D  S
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
4 ?$ V% F( ]. k4 r- u" y6 ]- ?1 e& Q4 ~. d# D

& t" Q' l! V4 W. tTCP协议8 _- @, P  z! _  }( ^
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 g& Z8 R6 e' t3 q# ], @7 V2 F: \$ {" K( q
5 T" t* B  \( @4 s! y+ ]4 w
关键词:三次握手,可靠,基于字节流。
7 k) W7 B+ t  ~* x0 T2 h$ p
) n" n% q: W* H# i4 [  w

0 h' d* P0 c% J9 Y  e! G( u$ J可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
; ]+ J+ q, u2 G8 m( \9 d + p4 A9 w, A4 f# b7 M
TCP服务器端和客户端的运行流程
/ d" d- y8 c3 p+ x3 _. p1 L/ {  H
6 j; |* V5 J4 H1 }- O( D

/ r+ U& W4 |& a1 _; o如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?( t- b2 n9 f" {2 W% G  V2 g& K
4 ?. K; }. S9 W* H) N; Z; j' H7 d7 P* K  r
& J- i8 f* _# `8 e1 p; {
1.创建socket
+ v$ r3 I- k9 r& Z: r' a, l+ `7 ` socket是一个结构体,被创建在内核中
8 y9 n, t4 n& B) G6 X8 x sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
, P1 o8 j( \9 H$ K( Y8 M; s' K+ V: I- C( o0 X
/ o$ ^& X4 G0 ^: |) [1 s
2.调用bind函数  M2 |0 R( t( X& y
将socket和地址(包括ip、port)绑定。
! {- @+ U6 A' a; z! Y& ^" I 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序! M& K, j* d, u
struct sockaddr_in myaddr; //地址结构体
8 x+ [8 V3 R% X7 @2 X0 e9 y7 U bind函数! s2 A/ m' T1 T
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 ]2 V, g$ O- g) c6 E
1 P: I( Q8 k2 K3 [3 Y! t0 B

' a. T# A& a% s* T3.listen监听,将接收到的客户端连接放入队列
( l/ o9 j- P# G listen(sockfd,8) //第二个参数是队列长度
8 h/ I) B5 X7 }8 c7 r+ h: @- E6 n* H7 h0 j6 D( M1 d8 |
. l7 y# N! K7 J  ]9 I
4.调用accept函数,从队列获取请求,返回socket描 述符& X8 ]. q/ W! {$ ^
  如果无请求,将会阻塞,直到获得连接+ ?4 y' @) n, u5 m8 Q  d3 T
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
% R" l5 N2 z- ^8 F4 \7 L0 P. L( q+ E: e- p* O" {
+ \$ v2 O8 P; d0 t6 z' f
5.调用read/write进行双向通信
8 }: P4 X- u. W
: Z/ M+ l' }- s* y) n
- Z$ F( j9 `8 l" ^  L1 Q5 ?
6.关闭accept返回的socket- g. _3 V- `8 R% n+ O5 Y5 p
  close(scokfd);  k* j) Y' H( a( U8 [. R2 X5 }
; \2 F* x( o* y) d

: z: Z9 j: v% a& a9 O
8 m: H; T: H6 t. Q
% w$ z* D& m5 |  p) U- V
下面放出完整代码2 S0 W0 s, [: x( J+ }6 Q
/ Y! l7 C5 o5 c2 Q  b$ ^$ C& y% x+ \  Q
  1. /*服务器*/
    % O4 e6 ^' S7 s  f1 c* c
  2. #include <stdio.h>) f5 e8 V4 n4 j$ p" b
  3. #include <string.h>+ Z7 h  b4 P& E
  4. #include <stdlib.h>
    1 A$ K3 ^: K" f/ O2 h7 n
  5. #include <strings.h>% L) i9 k) V. ?1 q% m: P* s1 r$ T
  6. #include <sys/types.h>( G) w1 ?4 M1 Y
  7. #include <sys/socket.h>7 Z* q' X- r" B6 D3 A' x+ h
  8. #include <arpa/inet.h>/ g( v: x$ T* N8 ~9 C& q& e2 ]
  9. #include <netinet/in.h>1 G/ f0 B# l- t( G; [
  10. int main()
    8 A  U; @1 c* {/ V
  11. {
      N& U0 Z& b' h+ r
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- [- f2 w" q2 {& ^- f5 W
  13.         if (sockfd < 0)
    # @. I6 y( \$ a) n
  14.         {  A) V: {# R2 k' @$ z6 r( b* J% v$ G, F
  15.                 perror("socket");
    9 t- ~! I' f! S9 d
  16.                 return -1;
      ]6 G: M& o: j( Z! J: Q. y( O
  17.         } //创建失败的错误处理- m8 R6 E; u0 P+ |* R5 l: g" I3 A
  18.          printf("socket..............
    - M  b" n! K9 p" N6 o1 @; ?
  19. "); //成功则打印“socket。。。。”
    " `& I* o: m! p
  20.          1 z. v6 _2 G3 T5 a* s; F7 H
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    . B; \! \9 {6 {! [. U% w/ Q
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)% I+ b1 |7 x+ q! ]  r
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型7 U( [6 A; N( K  i9 ]
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    % c/ z( K( ~" U
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    9 ~5 {' w* i6 v5 F8 o; f( C8 s/ x, b

  26. 0 i, J6 u' {# d* H5 }% i
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    & K8 N" \' t, Q2 P& \( K* b0 [
  28.          {, M+ g# V( [. C) z6 O1 d" [9 \, M
  29.                  perror("bind");
    , J& V' [+ b3 C) Z/ k1 V) r6 ~
  30.                  return -1;
    3 F" t6 `+ T7 F" T5 k, z' W
  31.          }8 w4 K  f: b& x) H' A* T8 R
  32.          printf("bind..........4 }) b$ G" e6 a: S- x" J8 L% l/ W
  33. ");
      U. y+ z2 K0 c1 H, f1 u7 g

  34. : O* W2 s% V; E
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    $ H2 O) e$ R. W7 [3 U
  36.          {8 r8 `8 v8 {$ `5 l5 |! J
  37.                  perror("listen");
    4 R, _7 C/ w3 {2 I) n  s% t3 o
  38.                  return -1;
    ! Q) ^3 z3 ]1 M2 H; R: W& i' T# z
  39.          }
    9 ?: s" F5 q3 K& P) v) {, E
  40.          printf("listen............/ H1 m+ s* y/ r% i
  41. ");
    5 k: |& }& b5 h2 T, P7 g5 M" r  S2 F
  42.          5 l  b% y5 ~% S2 M8 r
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求. f$ B# ~6 c' x
  44.         if (connfd < 0)) o/ }( d3 a8 m+ \5 R6 J
  45.         {
    2 o$ m& b* v" q) N: n+ A( y( f) C- \
  46.                 perror("accept");0 x$ M' M$ i: V, b4 _! }/ p. c* {( f
  47.                 return -1;0 b* c/ q5 L/ {  q
  48.         }0 j( w8 F2 D1 f( N
  49.         printf("accept.............." B$ ?0 x- g7 E# e0 Y) [& ?
  50. ");
    8 V3 \) D5 x5 T5 M4 Z( w# m6 l
  51.         char buf[100];//定义一个数组用来存储接收到的数据9 r/ V; O1 f9 l! j+ o
  52.         int ret;
    5 j8 _* q& B$ m  u; z0 M6 x
  53.         while (1)0 H/ I4 ]' ~: K, r7 P& a9 x6 v9 P
  54.         {7 @. X0 \7 y9 u3 y8 e+ @
  55.                 memset(buf, 0, sizeof(buf));9 p* u* s) ]( U  d& w" y2 G9 x
  56.                 ret = read(connfd, buf, sizeof(buf));  P; z8 z  I' J
  57.                 if (0 > ret): a1 u/ Z' B/ R5 R
  58.                 {: M# U, B1 o. G# p( N  v
  59.                         perror("read");
    , U! y( B! o3 {  q0 I/ c$ l
  60.                         break;
    3 a: c9 o  q9 h) @) z( Y2 x
  61.                 }//执行while循环读取数据,当
    8 W: Q: T4 L" n" V4 K) ]
  62.                 else if (0 == ret)
    4 _) `# y3 B6 l, L
  63.                 {
    8 l* o6 K( |# o. C4 U; ~8 c) u# B
  64.                         printf("write close!
    # e/ b% d$ y6 N
  65. ");
    1 g7 h7 \. M5 g! e/ d' _
  66.                         break;
    1 w7 Q' s3 [; v
  67.                 }
    " n9 ^  z" j9 s0 ^6 w9 q
  68.                 printf("recv: ");
      m( R- J( S$ v/ z# n% _
  69.                 fputs(buf, stdout);//打印接收到的数据# H5 i0 N1 v: L* a+ Q
  70.         }% G0 H2 ]4 U) e( f& a) ^: G0 G
  71.         close(sockfd);//关闭套接字
    3 _* V- b4 C' B" ^2 |
  72.         close(connfd);//断开连接
    3 f: ^" I7 T# {$ c7 \
  73.         return 0;: [+ `1 s. K# b; H
  74. }
复制代码

, V" F* y. d" g- [6 W) P5 j: |
- D% F: T# r/ v; q3 F" [9 S
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)  ?) P' l  m+ x# \0 O
  2. #include <stdio.h>, c: m+ z$ {% M* f/ x
  3. #include <string.h>
    0 R3 K) |# {8 v5 V, [, l
  4. #include <stdlib.h>
    * J5 {) W2 `1 z
  5. #include <strings.h>$ F2 f) r9 J( l
  6. #include <sys/types.h>$ D# O# |8 s# f: L1 O
  7. #include <sys/socket.h>
    : m  d: M/ q9 u* e9 d
  8. #include <netinet/in.h>
    1 o8 a& p6 H7 i. T
  9. #include <arpa/inet.h>
    5 [# W7 s9 M* z9 j6 i0 u3 K+ ?6 [8 t
  10. int main()
      b6 |' z' W: y' L1 p) I7 p: u* h, \# L$ ]( a
  11. {
    , x1 r7 b+ [4 u! k$ K
  12. int sockfd;1 r! x! s# B. j: _2 }# G
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    + I" f2 b% b  `
  14.         {/ s2 Z2 d+ l3 J7 J
  15.                 perror("socket");, |/ w7 ^  D3 T- q
  16.                 return -1;1 |+ I2 u( o+ y3 {- l  S/ g
  17.         }
    8 X3 B( V1 K! ^
  18.         printf("socket...........
    0 N2 s5 H0 _1 i; D
  19. ");1 H5 u5 y8 `" t, q7 I. r' ~+ ^3 H
  20.         
    / `# |: @: M7 U  E4 u1 o3 p- e
  21.         struct sockaddr_in srv_addr;. o1 r, |$ _0 B$ l1 M$ q
  22.         memset(&srv_addr, 0, sizeof(srv_addr));) t) f* `: {, H, d( Q
  23.         srv_addr.sin_family                 = AF_INET;6 Y% s  [- x* {0 c& D
  24.         srv_addr.sin_port                         = htons(8888);9 m. \0 D( A" s4 X
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    7 n/ r4 D2 H4 f8 Q6 C% ~
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))% P1 {) j1 v$ _/ O* j: ]
  27.         {; G! G8 {9 R- p0 a. J5 l
  28.                 perror("connect");$ ]6 \' d4 c& |" E2 N7 R0 A0 \. i
  29.                 return -1; //exit //pthread_exit
    7 [: \( |; n- v& X
  30.         }
    5 ~5 A  y0 ~6 K* G
  31.         printf("connect.............., H) r7 ]  ]% Q$ g* d& Q( A" O5 ]
  32. ");, d% n; k7 u" G) k+ y6 i# N
  33.         char buf[100];
    # k# c/ [* I' `6 O
  34.         int ret;
    * K% F/ m" @. j) y
  35.         while (1)4 L) H# \+ I$ T3 F- D6 f
  36.         {6 h$ w. c2 o& Q8 q; Y
  37.                 printf("send: ");0 l5 g( u, Y: L+ g- O
  38.                 fgets(buf, sizeof(buf), stdin);
    % H/ X2 n6 N1 W: W, Q# P7 B7 r
  39.                 ret = write(sockfd, buf, sizeof(buf));( z, Q  _6 d5 O$ }5 D
  40.                 if (ret < 0)" ^% [: i3 t' h( w6 x9 {
  41.                 {2 q' H$ R+ C* h2 i) D; f! ?
  42.                         perror("write");9 k5 G/ s  G& o# w9 ?2 V2 ]
  43.                         break;: @- a; a$ X0 a' Q9 r4 e
  44.                 }0 Q% F2 q+ `1 H; g. a5 N& X7 e
  45.                 if (strncmp(buf, "quit", 4) == 0)
    : w; i% ?! g$ A* B% {6 n% G  j
  46.                         break;
    ( n7 j8 {, `: o) Z6 f1 ]
  47.         }
    5 i& |, F' h4 w& u* z0 t; }
  48.         close(sockfd);- U& W% \% k; V* D3 q. A/ a9 ?9 h
  49.         return 0;
    * U- M) |; q) q: ]3 {8 C: r
  50. }
复制代码

, e9 q1 J( ^. s3 Q( ~+ C5 C3 v
1 T) O+ G: N; q! z0 b- R! z( O
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-17 16:08 , Processed in 0.057625 second(s), 23 queries .

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