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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
7 Z, {7 W, ?! n& d2 d- f8 H  i
- n, h# G, _. t! r
( ^- `& K( \, d3 e! d
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
( _3 }8 R% l/ i# k' S! k
! Q" F4 k/ ^5 b9 X) A8 \. |

$ g8 n! S- c( a" ^  LTCP协议
% b# p# J& @$ L! y' yTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
7 Z; V- W" T+ t3 s3 V( i
+ n( y9 Z0 M) R4 ?
3 ]- r5 ~! k+ w
关键词:三次握手,可靠,基于字节流。
/ f! W* k/ d6 z5 z5 z
0 M% N4 ?6 n% D2 `
( m  v; `  l8 n  c8 u' s6 _: a
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
; q: M0 o# R6 B* R" r( ~) _9 D9 h 9 }/ k* S% s( F4 u$ g- Y
TCP服务器端和客户端的运行流程
8 w. u- u7 f7 H+ F7 I
. p4 t" w( A/ O) t
  ^; f* M/ O# y3 Z5 q0 |$ T8 a
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?) F( J. c" [/ c
* `* J0 o, c: j

+ G8 g% s% f- Y' G$ O8 u- v1.创建socket
6 c! o* A/ y' y5 }) w5 S$ G! ]& E: D socket是一个结构体,被创建在内核中; T$ \; e# R, M! D8 @& R* U
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议; B3 y* O' k0 o7 D/ \* S

/ g( ?- _/ j3 V; v: G' O$ J) s

# B: r& {3 B/ B, \8 X  S. Y2.调用bind函数
9 Z/ i6 O$ b) ~: T9 c0 | 将socket和地址(包括ip、port)绑定。; S" n/ B1 o7 D" v4 m7 r% s' K
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序: }+ V8 j3 |) B: [8 j1 e+ u: G- Z
struct sockaddr_in myaddr; //地址结构体6 w4 d1 ~) M+ ^- n6 ~% i+ i
bind函数+ }1 M9 v) s" k- g
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
6 t( i7 ?- A7 B, G1 p6 E
8 K# y0 q( m2 x7 Q* q
$ R5 L, W; e5 ~& `
3.listen监听,将接收到的客户端连接放入队列# }% k, a! y! j2 f. \8 J/ d. w
listen(sockfd,8) //第二个参数是队列长度% t8 t1 o& ]$ |6 Q8 Q

+ a7 i! G- u& ]

7 Y. o6 K; ~& W& B6 v& o4.调用accept函数,从队列获取请求,返回socket描 述符
4 |8 ]. \) `0 _1 B4 V  如果无请求,将会阻塞,直到获得连接
- s2 S0 n4 j1 s: t" M# ?8 w  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数4 }7 v" ?. a. u6 d" t

2 F$ d; p( [8 T9 p0 K3 w9 `

3 h8 S, V% I4 f' y) M, h  O: I5.调用read/write进行双向通信$ O  i: d% d1 }) j2 u  O
7 D; ~7 n1 a/ e, N7 ?4 B9 b! t7 S
9 u8 p& n' Z$ T! T: |, Z
6.关闭accept返回的socket" m( y& @% j4 \* J9 b
  close(scokfd);
1 R6 W% L; z1 I3 V8 H& F9 A6 h. |* B2 V% j' ]2 j
$ Q, K2 R$ m: H! m- K

( a! r9 ?. b1 A! D

( g# d5 ]3 r  D  q# P1 }& K1 s/ J下面放出完整代码* [0 q; S- z5 B) a- ~: p

, \1 x, k6 l) t5 Q
  1. /*服务器*/
    : `$ x' x9 D. A; M% U  }
  2. #include <stdio.h># v4 R& L3 e$ E" H& v' }
  3. #include <string.h>
    3 [# t# A* E6 p  z; l9 m5 \' ~$ g" W
  4. #include <stdlib.h>
      h& c; _0 m6 c  F! p
  5. #include <strings.h>
    , d6 p7 q5 [9 o, O% n5 S/ Z
  6. #include <sys/types.h>
      L" y  \) Y, [- i: p5 v+ ?
  7. #include <sys/socket.h>6 t  W8 E% B/ r& F
  8. #include <arpa/inet.h>
    5 I8 t6 w- f' M! e  v
  9. #include <netinet/in.h>) p) b  y5 e8 C; Z. u2 W
  10. int main()/ A1 O/ z  n5 _' ~5 N: M. p
  11. {7 F  f% X& z& U4 O
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    " X8 r+ O' U5 t0 b3 K) @8 T
  13.         if (sockfd < 0)0 _! U+ ]/ s- D5 r. c4 P, \
  14.         {, }2 [- q( a3 B% i5 k
  15.                 perror("socket");
    ! t8 \5 Q+ H! |1 P' q/ J0 T
  16.                 return -1;
    + S: @( k, m* q4 m
  17.         } //创建失败的错误处理
    . a6 ]7 q  Y$ v4 r
  18.          printf("socket..............
    7 L9 F6 i4 B  b6 {1 X6 Z2 L
  19. "); //成功则打印“socket。。。。”
    6 `, Q9 z! i/ [, B; h1 ~* [
  20.          
    * ~# w: k2 |5 T3 W7 k# A. @
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ; q, R; i( l9 C! f7 ~
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)* b4 G# d8 \: s/ l
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型' ]( {# p7 b: P; {7 g
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    , h& G; M/ K# ?! J$ W8 P" u6 _
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址* C% n( Z) C5 J4 z8 n& W5 p
  26. * K1 w- B% v. \. _* u7 r2 A
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字+ L( O: h" f) v1 }$ E! G
  28.          {+ W" G7 d+ e! y
  29.                  perror("bind");
    ) \9 @/ p' X, p, a0 e
  30.                  return -1;5 T% ]8 Z8 t& \$ N2 v% U6 m
  31.          }3 j. g3 y" ]% @% B. M6 |3 C. g
  32.          printf("bind..........
    9 h2 q+ R& }5 z9 Z
  33. ");
    $ z+ h5 J) _* E, P- ~! i' o, [

  34. $ |; t8 a4 P: J: R- ~7 k; S) x
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    " t; G$ Q- `% V. N
  36.          {$ \9 H1 j9 d' }! d6 Q) g
  37.                  perror("listen");5 i5 [3 n* \  T7 p
  38.                  return -1;+ l+ r! }9 w% a) V2 Y+ _
  39.          }5 D4 u' }* J- Z, O
  40.          printf("listen............
    1 o: C4 g) d* y- H/ @
  41. ");+ e! y! d' P+ n4 x6 D- o+ U# h# e  s
  42.          9 C0 Y3 Z+ s' m% \2 W8 }; V; i
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    7 I3 {5 k5 P& B$ ]0 W1 y
  44.         if (connfd < 0)
    1 A: i; V, b( x7 }
  45.         {! P& A) D  Q% P2 a
  46.                 perror("accept");4 l. v/ J4 M5 s! C* N$ r
  47.                 return -1;
    0 g! z" H* D# Q9 t6 G+ }5 Y) [
  48.         }
    : F0 Y0 M- G8 Q& m
  49.         printf("accept..............4 t- ^, p3 Y' r9 q7 q5 Z
  50. ");
    ) }+ K) n$ Z6 Z; {( B" W
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    0 u8 o( V5 O) B, D& R: J, r8 X
  52.         int ret;
    . k/ \5 Z2 ~. L6 \. ^' D  C, |1 h' |
  53.         while (1)
    + c7 M6 r/ L- r/ g+ h
  54.         {
    ) N2 h& x/ t. {0 }% _, ~: R$ p
  55.                 memset(buf, 0, sizeof(buf));
    5 Y6 a9 ~9 L2 s
  56.                 ret = read(connfd, buf, sizeof(buf));
    4 U1 Q" K- x1 A
  57.                 if (0 > ret)
    2 g1 r( j8 n' l: c! |
  58.                 {, w1 ]+ H. K5 X% H3 O( g* _. o
  59.                         perror("read");
    4 H1 u0 E) I5 d' b8 w  ?
  60.                         break;  f3 l8 c( C  y3 e$ a1 G
  61.                 }//执行while循环读取数据,当
    : z9 \% p  ~9 q, {
  62.                 else if (0 == ret)4 e0 R3 X) d. P- f- L8 v
  63.                 {
    ( v/ u$ [0 {- m$ C
  64.                         printf("write close!
    : S& Q: @- V4 b( B% O8 ?1 U5 \
  65. ");# W6 X. W! z& w/ a  N" w# P
  66.                         break;
    & E" p# H, ]6 E" d4 \2 f9 E# C
  67.                 }
    3 E  ?! U; T5 H: o' }# F- {) T
  68.                 printf("recv: ");( E2 u2 s8 _: Y+ P
  69.                 fputs(buf, stdout);//打印接收到的数据
    & I- B. {+ E1 ^9 ~( L( ?! Y% u
  70.         }+ e! `) E5 Z; R
  71.         close(sockfd);//关闭套接字: I4 @. E* m' n6 d6 Y
  72.         close(connfd);//断开连接
    2 i" }* i; X8 d# c, y- s# k& d
  73.         return 0;" u8 t9 O& Y1 E1 d7 d  P: d
  74. }
复制代码
7 z5 b/ }! W$ y0 a

/ y' a, u4 f3 T! y+ y9 T( q
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    ' ^* K' v! ?5 g# y& E3 X
  2. #include <stdio.h>
    6 d: i  F8 W6 N# g
  3. #include <string.h>! ?( S) f+ p" `& r: P8 s2 P8 ^
  4. #include <stdlib.h>
    5 M( H% E% X# M7 ~4 f1 ?0 |
  5. #include <strings.h>2 T: M+ c8 B+ Y# w. m/ p% v  Y
  6. #include <sys/types.h>
    & R% a! h% U/ H# h
  7. #include <sys/socket.h>
    $ R( O, l' w8 B" G6 |- B
  8. #include <netinet/in.h>
    & T0 K. n- f6 y1 v8 v: D8 z
  9. #include <arpa/inet.h>1 b+ l0 c3 x! C! `& f! K  M2 J# B
  10. int main()
    8 x& L7 L) |( f# \! ~6 r! z& u
  11. {* v* B" J8 L+ i4 v1 Z
  12. int sockfd;' T. `" z: k$ o" E/ U1 C
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    2 b+ M4 d9 R0 A: l7 ~5 \
  14.         {1 b1 o8 r) i; k: a8 z7 D- t- w4 ^2 S
  15.                 perror("socket");# Q$ }6 c; D/ }: [/ k4 z& m
  16.                 return -1;8 D$ o9 y; E2 K5 b
  17.         }& ]7 L# e" ?6 G+ g3 N# G
  18.         printf("socket...........% e, R) |' G/ P; c/ l9 \) M8 l
  19. ");( J4 y, e+ q9 K& s% }- }8 O
  20.         
    ; f! }$ s4 y) H2 N
  21.         struct sockaddr_in srv_addr;
    ( U7 q& Y$ g: [' o7 R( O
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    ! W: c. S+ N- m& m5 ]2 Y4 J* C
  23.         srv_addr.sin_family                 = AF_INET;
    2 H/ L3 M8 h; t/ c! x  K6 P" o
  24.         srv_addr.sin_port                         = htons(8888);
    / s) N& `4 q* l- s* `
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");( l! `9 \) o! a  U' t( U3 ^
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    % P! E& y6 O6 E7 F
  27.         {
      O+ v8 m6 O) ?8 p& ?2 d" u! k
  28.                 perror("connect");; f0 a3 D( |5 `! _
  29.                 return -1; //exit //pthread_exit
    % D5 h" C) E7 X! m) M: w, w7 {* x1 ?1 h
  30.         }- ^( {" \0 X1 o
  31.         printf("connect..............
    0 W# o7 v" I+ x% d. u+ W8 h: ?
  32. ");: ?) q6 p/ h. z' p+ s5 ^
  33.         char buf[100];
      z8 D' H% T0 T# d7 _$ f
  34.         int ret;" s! c# t0 j4 u4 {0 c: _
  35.         while (1)# Q+ j- C3 B" u6 G' ]
  36.         {
    * O8 W1 y3 L7 R+ U. F
  37.                 printf("send: ");' a- s  A% O! ?" ~9 k4 q7 Y  B
  38.                 fgets(buf, sizeof(buf), stdin);9 A2 U  ^, @3 F  K
  39.                 ret = write(sockfd, buf, sizeof(buf));
    + L. l% @4 q! f5 `) r" L2 i
  40.                 if (ret < 0)
    ( i2 n/ |' O- s
  41.                 {
    8 Q$ A6 \6 s) D0 q
  42.                         perror("write");* X& s' `& q! `2 R+ t: W9 L
  43.                         break;
    ; L/ p. B9 n  m+ Q7 b7 U
  44.                 }+ y5 m; S/ `: O; V$ j% `. ]
  45.                 if (strncmp(buf, "quit", 4) == 0)
    . Z, o5 \4 d( J& }" ~0 _
  46.                         break;
    4 o, s. o+ |& k. F& L
  47.         }9 M- ~7 Y8 y- _
  48.         close(sockfd);
    6 c3 |' ^2 Q' H
  49.         return 0;1 }% Y6 Z  w- U6 y8 ?& @
  50. }
复制代码
, z7 n# L5 v- w# q7 N2 ?7 J1 D

+ m2 N. {  W- G0 l7 ]! y1 _& E
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 21:28 , Processed in 0.133691 second(s), 22 queries .

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