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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。: K7 R9 T( ~; I6 ^% l: E2 Q
8 x6 ~0 J. f$ K
( O3 J3 U! X" s$ Z
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
# y9 ^& N9 P. ^' L+ L" ^4 j) E- Z/ T% D5 e( C( u; d

. N/ e9 h) m/ _' n3 v% }TCP协议
: D; h) }( `# ^/ C$ ]. N; N: FTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。5 s# W+ q, K2 a- n9 R7 \

- a9 i7 q" q% w- S
+ [  Z! y& D  r/ [4 j  }
关键词:三次握手,可靠,基于字节流。+ k0 }( L1 H  B/ F! }! |+ F

" R* W3 Q$ M/ V8 j& t! A+ J. M
$ z! d& N: C4 {1 X' b6 _
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。& E/ @: E* m# D& Y) W( D

! a: |+ j8 p" u, V  }6 p# UTCP服务器端和客户端的运行流程
+ [. D2 G0 f% s' @6 M2 P$ ?5 S
7 z5 b+ B& a7 A9 ]! I3 ~
' W% H# `7 ?8 }. c
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ I9 J% m* U& H0 e4 x" X0 I
9 x) K% K" v9 D( _# A* T4 ?

: u+ h' k" s& _+ B3 C1.创建socket) ]6 a! ]; M4 T
socket是一个结构体,被创建在内核中
- e) X$ C, O) ~! a" E: P0 @ sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ d" ]) L4 N: V9 H" g% p$ f

/ g+ U. S% X, l  s! @
3 _/ P( L7 X  I6 f
2.调用bind函数6 `8 ]0 ~  f0 I" @6 ?
将socket和地址(包括ip、port)绑定。, Z6 ]9 f6 @% G! p, w& r
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
. a, f% I$ Z% o struct sockaddr_in myaddr; //地址结构体8 x; b( B7 Y: L
bind函数
8 Y9 D' g. I5 E  ?  K0 N6 i bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
% |7 t9 M( a. z& M1 |  b  t& {/ m# ?3 d( I3 X* w( @1 P
5 N" B# g0 H5 }2 g" Y
3.listen监听,将接收到的客户端连接放入队列0 d% q. L7 \$ s
listen(sockfd,8) //第二个参数是队列长度) ], q7 ~+ M7 F7 v: P4 G0 B
) @) M% W+ D2 c) ]3 ^/ }; d
  k" Y: z" s& v+ c! a
4.调用accept函数,从队列获取请求,返回socket描 述符- P& B" Y9 y1 @$ z: n" s
  如果无请求,将会阻塞,直到获得连接
5 p* H& ?7 C$ d0 p! `  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
* F6 ~9 a( o  F1 a
, u& F( s# I8 B) l
8 t' V) E  Z3 r# G. z  K! @
5.调用read/write进行双向通信
: \. _8 @% v' R# M
$ u* ~2 P4 D0 u, y" Y
" b* M' E: H7 g. z  n
6.关闭accept返回的socket
1 p+ Z9 I, k, U# a6 H! l  close(scokfd);. `5 E6 O- F; O8 t

( v3 z- [7 |. a
( E- `( _. C2 O: E) D( o2 K' C

$ x1 T" d2 S) n0 P# K

8 x( m* ~. k/ P; q" F" V下面放出完整代码1 }0 J# B4 J3 k, ~0 z( t/ y4 p
9 |4 p, b4 ?" t. E+ N/ U
  1. /*服务器*/4 L( w% ~. B5 z" r9 G
  2. #include <stdio.h>  H0 g) r3 I7 r# x9 K8 B# o
  3. #include <string.h>
    % ^. S' e" ]' o+ M. X+ z: I
  4. #include <stdlib.h>
    9 N% w2 i4 }/ N+ I! k' u0 S  A
  5. #include <strings.h>  U. \9 m8 X$ S) p! w
  6. #include <sys/types.h>
    ' s& T0 z4 Z6 l  `( x
  7. #include <sys/socket.h>* j, Y) K* q. Q% s1 `5 c
  8. #include <arpa/inet.h>
    : u  H) Y4 T) m" R- _& l% G
  9. #include <netinet/in.h>. {* Q; F- m( J* T9 ^# G' d2 ?
  10. int main()# Z9 F$ w& `1 U( W" A0 o
  11. {
    % `. R% ~, n" G5 j# I/ p
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    6 ]2 L: h6 C; _
  13.         if (sockfd < 0)* M$ @% O- z* }% N4 Y, Q
  14.         {
    , C! p4 N: A$ x* o* z  d: e7 N
  15.                 perror("socket");
    ! V- n4 w# M% O% }
  16.                 return -1;9 {% h) ^" z. X6 V
  17.         } //创建失败的错误处理: E, `3 g* g+ ?! Q3 @
  18.          printf("socket..............
    5 D. ], t/ B. E2 `6 m7 X
  19. "); //成功则打印“socket。。。。”" z3 ^7 ?6 \: S" M% X
  20.          
    $ A0 E# E/ f3 O! U2 i/ l2 r" b. u6 q
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    9 u  Q/ y: N4 `3 j( n7 B: Q/ z
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)2 }: u8 R9 I/ j. B/ {. B/ R" Q
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型! [, I' d, l8 C: U. {5 q0 k( ]
  24.          myaddr.sin_port                 = htons(8888); //选择端口号$ G7 l! ?5 R1 {( w$ H$ [
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址! X1 q6 d) h# }3 V! R) I/ S- I
  26. / l' B. i% ]6 ^  n: O
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    7 C& K) G# \/ J' B5 c, c
  28.          {7 b9 {5 Q+ `7 ~; t7 o6 ~' C
  29.                  perror("bind");
    0 N) a9 H$ u0 T* x+ H1 t) q
  30.                  return -1;* ^1 w8 q) d! @! C: ^' p/ Q
  31.          }' t6 P# d! e5 {6 C: g2 i  X. r
  32.          printf("bind........../ Y( m5 l' D+ G; }3 s' g
  33. ");( z) o7 R( A$ R. ^) c3 @% e
  34. % T8 X( W9 @- [
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    7 v) F% a! E1 H: c. ?  @8 \
  36.          {
    + C9 e% N0 G4 ~$ N2 h6 q
  37.                  perror("listen");
    5 I, ]" [9 n7 E( O  W
  38.                  return -1;+ D- {( K; Q( _7 h
  39.          }! r# E* x0 Z1 h! c/ f
  40.          printf("listen............
    8 S* J: k" r7 ]) d9 I( Y" L
  41. ");
    5 a% o( B! I) `9 E
  42.          
    9 t# q& \( q9 @) O( d: z
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    6 _2 Y0 I" F$ K1 A8 H
  44.         if (connfd < 0)+ V; w5 R9 W0 {7 i) q4 w/ Z
  45.         {) T5 W5 L8 D$ `% d8 ]( q
  46.                 perror("accept");' {2 i: `2 W3 M/ j  s
  47.                 return -1;2 M) q  Q: J0 b  }& W$ _
  48.         }
    ) ~% I( C- _$ ~) S. K
  49.         printf("accept..............
    , }+ d) X6 E4 ^; Y! @) C
  50. ");$ ]3 y1 v9 R; b0 \+ l& b) r# k, f
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    : V! U# a  o, j& N) x- [
  52.         int ret;
    ( x3 s) G+ k0 z/ E6 x3 E* [2 D
  53.         while (1)
    0 p5 W$ x" K9 p$ I! f. V
  54.         {1 o. O5 q/ v3 O/ W
  55.                 memset(buf, 0, sizeof(buf));0 R6 v& j6 ]; `* A$ X' G
  56.                 ret = read(connfd, buf, sizeof(buf));: |! V& F0 k8 W
  57.                 if (0 > ret)
    ) x* v* o$ ~+ R% `
  58.                 {
    ' C# t3 L: P' s2 _6 M
  59.                         perror("read");
    * J' E$ b; o* d" i2 O4 @6 q  |
  60.                         break;
    5 W" z! M0 V* X0 B  ~
  61.                 }//执行while循环读取数据,当
    + g* j8 I1 ^9 [+ J. O+ K9 j6 g
  62.                 else if (0 == ret)
    9 |6 N  M  M$ k
  63.                 {
    1 @* Q$ u; S! \
  64.                         printf("write close!: l9 G! ^' k6 D$ s
  65. ");
    & e  _5 V3 A- E7 P
  66.                         break;% S5 v  Z, l! W7 J0 S$ l
  67.                 }. p/ @3 i6 J9 e9 ]  u. _- \
  68.                 printf("recv: ");
    ; U3 z/ l8 {' z# F) O6 `2 J
  69.                 fputs(buf, stdout);//打印接收到的数据
    4 L: J# u; r2 J1 m( |6 J
  70.         }
    0 I  P$ z4 T, J7 T' Q: q
  71.         close(sockfd);//关闭套接字
    * n$ U( h* K# y. T
  72.         close(connfd);//断开连接! \8 t$ f6 b# G/ x4 |  N  Z7 x$ e
  73.         return 0;
      ?/ F' P: O" A* J, R
  74. }
复制代码
" b9 j: ?! b4 E! Q! u% \' c
# |4 {2 U; \' D" m' A# _8 f2 s8 D, N
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)6 E, l* H& `6 ]9 q( M( U7 o" M
  2. #include <stdio.h>0 d  D- Z% f9 \+ P5 @: {
  3. #include <string.h># |  p: s$ e1 P6 X/ O& s
  4. #include <stdlib.h>2 B: w0 C  |, w, W5 Q+ @: g
  5. #include <strings.h>; i/ O) M4 e8 b2 @/ ]! m6 u9 g  Z0 k5 Z
  6. #include <sys/types.h>: G/ y/ v- r1 F# B, K
  7. #include <sys/socket.h>
    * O8 x! U  n  t1 w9 w# l
  8. #include <netinet/in.h>
    , H* n) a! D6 Q2 o  d* n1 N
  9. #include <arpa/inet.h>
    ; M- J  W" L8 L2 f; }1 S2 W9 m
  10. int main(), K7 s; n5 ]* P
  11. {
    & h9 D7 u/ ~$ D% ?8 w. w
  12. int sockfd;
      ]5 k, H2 m) _1 F7 Z1 \0 D' E4 k
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))) P6 n& z; p& e- i6 S
  14.         {
    5 }; E5 U1 g6 t! F: |( w
  15.                 perror("socket");
    5 |* A) y7 u7 G- [* z# [
  16.                 return -1;
    + z/ ?2 \9 k: P7 N  J
  17.         }% K+ ^, C$ Q& n; m
  18.         printf("socket...........) u* m; ^& w1 {8 o) N
  19. ");
    9 j" i  m/ p1 `
  20.         . m, t" a4 r/ }2 p1 m
  21.         struct sockaddr_in srv_addr;8 {% n; Z# O0 Z( `( w3 B
  22.         memset(&srv_addr, 0, sizeof(srv_addr));# e" g9 c2 N6 e! v* C
  23.         srv_addr.sin_family                 = AF_INET;
    6 x& I# R7 X) K0 j7 a$ ^. P
  24.         srv_addr.sin_port                         = htons(8888);# t! z) }5 V: ]* n
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    $ r, d' j) |" h, \5 X- `
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    7 U$ q; K: Q3 A9 z0 R3 I; M
  27.         {
      c' S  `1 n- M3 d! O$ F
  28.                 perror("connect");
    ; V4 f, d& D1 q: M: N' ^
  29.                 return -1; //exit //pthread_exit  g# F+ K3 b1 x7 f# A
  30.         }$ Q6 c4 N1 Z1 w9 r1 T
  31.         printf("connect..............( B) \- `! m8 Z5 k  w; K0 Y
  32. ");: [. @6 `5 z% @) i" K* q$ G
  33.         char buf[100];* B, j- h, C3 j
  34.         int ret;7 J2 q. n, r- S& O' ^" v7 V
  35.         while (1)
    + i" w' p. V/ X
  36.         {
    7 _: C6 g3 w, H* K) f0 k# [
  37.                 printf("send: ");
    0 b; Y) T& @/ J) e4 D+ d
  38.                 fgets(buf, sizeof(buf), stdin);& a9 d9 m9 }; L! R
  39.                 ret = write(sockfd, buf, sizeof(buf));0 U% a1 |  B+ M2 a' r
  40.                 if (ret < 0)+ e0 R# w# y8 H7 a$ j; }
  41.                 {) {2 {' e5 \$ \# n# |6 d0 Q2 i
  42.                         perror("write");# Q' m8 K& P$ P2 L7 ^/ T5 M
  43.                         break;5 o% F: O9 X$ t4 T$ B' z
  44.                 }
    ( I6 t6 m$ d8 a  S+ V0 Q/ B
  45.                 if (strncmp(buf, "quit", 4) == 0)# k( U7 D. _! e8 e( F6 ?$ t+ r+ Q
  46.                         break;) b6 w. f' }# L* n
  47.         }9 l) w9 z  k% n. `/ ^; _
  48.         close(sockfd);& S7 ~! Y$ b' b& u2 @4 s9 Y
  49.         return 0;1 F( J# J$ I2 J* v) t3 O6 p
  50. }
复制代码
! ]7 @9 ]" F6 O- \% q% a+ P

. Y' U1 D( M) `+ X8 D
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 02:06 , Processed in 0.055163 second(s), 23 queries .

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