cncml手绘网

标题: 自己动手用c语言写一个基于服务器和客户端(TCP) [打印本页]

作者: admin    时间: 2020-5-9 02:09
标题: 自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。; R: N& S# G  l8 w+ z
. |! M: E7 u: w0 |# t) E: o6 P

' e' t8 Z( n$ n! Psocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。, N0 ]: A* ~4 h
- s5 O% X' |- T: c$ K) n

. t: v0 Z& C( YTCP协议4 g8 q/ g' l- w$ \6 n
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
9 n5 A) _, b8 L0 ^( E: K+ X5 t0 S
. |0 P* e1 z1 ]
关键词:三次握手,可靠,基于字节流。/ b/ T/ o+ t5 z$ A- s' G7 j
; t5 Q8 V( I: D, t, l  J

, N: ?- w6 |2 _3 P可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 ^& H7 Y2 w5 s2 o
2 q5 t- z: k/ m
TCP服务器端和客户端的运行流程3 A7 v- t/ K- f) x' A

$ `9 y* d1 k' R

/ [  x. W- z/ l8 n, J' z如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?( m. A3 ?0 ]4 e: O6 f# t

. x( X9 @- t, ^1 X/ a4 X; T
  P. O; D4 v5 j
1.创建socket
$ v6 X6 w1 W; L& g/ {' I+ x8 d socket是一个结构体,被创建在内核中1 D6 G* Q2 W* N3 X
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
8 N0 u8 L$ L4 d- K& e; L  i& {1 _5 F, g" A2 B1 f( F
1 P% L/ w& O) T6 A5 b
2.调用bind函数1 X  U2 F, E8 u- _
将socket和地址(包括ip、port)绑定。! [! W; C; Z: ?
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序/ b4 D* \# D' r1 a' G8 N5 R# Q' M6 s
struct sockaddr_in myaddr; //地址结构体
2 l  x" n) c7 @4 R bind函数
1 X2 g. c# K6 K% m9 g bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
- H( U% w6 A0 z2 P
' f& [0 B. L& g: S* v
" x- h& P" a6 k5 `; ^2 W
3.listen监听,将接收到的客户端连接放入队列* m# h! X9 `  U2 w' ~2 F$ c
listen(sockfd,8) //第二个参数是队列长度3 u) c. z2 S! M9 x& X9 B

% |7 p  s/ o: y# _' r

/ J9 R3 }/ b' Z3 D/ W' L4.调用accept函数,从队列获取请求,返回socket描 述符
4 k6 a1 v/ O  S- n2 m  如果无请求,将会阻塞,直到获得连接
2 P5 D7 E) ^/ h6 ^( t" {2 I  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" g; i# M0 H9 Q. q- {* Q  s  B
2 i4 p' B5 L* Y' j5 h( w' r
5 H1 B) h0 b+ s3 N' u7 o0 ~
5.调用read/write进行双向通信
2 _$ Z7 e) m* M' ^- ]0 ]2 n7 ]" S; Z* {& \$ K
9 K8 ^. ]2 k( N* R6 e2 W
6.关闭accept返回的socket# Y. J) r4 z7 ]7 `/ z  I7 x- [
  close(scokfd);
" e% p" B) i, X2 n
0 x5 W- `( C/ L. A8 e+ r3 S

: l' \/ _' |) P4 }; X! @5 z. c9 S8 w5 ~( }+ D  n0 S8 e
; c/ X( k2 T) J* e' ?
下面放出完整代码2 W0 _* m* M$ b* i2 g4 ~

4 y% n, S( g+ }1 f
  1. /*服务器*/
    6 ~! [/ n$ d) k3 [/ T6 _/ }
  2. #include <stdio.h>
    ! S( y4 o/ x$ c, h6 I' v& b8 ?( k: ?
  3. #include <string.h>+ N4 S9 c( }4 W8 I7 P% \' h
  4. #include <stdlib.h>$ ^4 _8 Q8 _" m; E
  5. #include <strings.h>
    ; A; M  W' q) n7 C
  6. #include <sys/types.h># C- B0 R* X8 h9 O
  7. #include <sys/socket.h>
    $ F# m/ h( S" l  h
  8. #include <arpa/inet.h>
    " w3 [; M( O# S
  9. #include <netinet/in.h>1 U  g; Z1 L$ b/ {! K  \
  10. int main()# G4 x$ i( `  @  _9 K
  11. {1 ^- ]5 e. A8 L$ s
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字! D; n) L) W# f3 I# w  e
  13.         if (sockfd < 0)# Y! k* k; C% T% o
  14.         {
    , ^; o8 B3 S% P  W) {( I4 z
  15.                 perror("socket");% m& l' _# n. f, |1 T
  16.                 return -1;- V! v7 }7 ^0 v8 \
  17.         } //创建失败的错误处理+ O9 S9 \, B* b, D0 q
  18.          printf("socket..............
    0 U* M6 U$ l- e0 F* \
  19. "); //成功则打印“socket。。。。”
    6 F( V) n  k9 n: }+ Z; Q! p
  20.          
    8 _5 n, B& y8 e# o
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ' }3 V- N( s. z+ C
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)1 C' I9 N( T+ k9 ^+ P* ~/ }& X
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型( A, n2 L6 R3 C1 z4 \5 D- `
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    * Q. |9 ]# L# g- n- D( j
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    & u, s7 e  S* Y$ H' {' U
  26. 1 J5 t" i0 m/ [  ^! m0 R
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字" e! a, r' _9 A$ U) K
  28.          {6 Y7 _7 h0 o, g! z0 J
  29.                  perror("bind");$ {" v! \* h  C* A+ V: ]
  30.                  return -1;, P# o1 {+ X) j: D: e  x
  31.          }- ^( l2 w. X) p! h
  32.          printf("bind..........: C) n2 P3 G9 S8 J
  33. ");7 x$ i( p1 j0 j  N" E8 m- `

  34. 4 A& [2 T0 @0 v6 C6 h
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听$ l  Q& T6 E- A; y
  36.          {; }3 V- }. ~) }" x: y
  37.                  perror("listen");
    % v/ R, q. u0 g+ `% Z
  38.                  return -1;
      E7 T% L4 s# R# h4 Y8 \! v4 |
  39.          }
    # o* w* d% d* J9 N/ @
  40.          printf("listen............
    ! l% ~& z1 v4 i: T, B
  41. ");
    8 p( n) ]" Y5 c+ G$ F8 |
  42.          ; R, ~' Y4 ^4 _/ S$ G+ u1 G
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    6 T. F' p# X* s# k# Q
  44.         if (connfd < 0)
    7 s0 \8 p6 {1 h) K( I
  45.         {
    / Q, l) w8 L6 W4 M
  46.                 perror("accept");
    3 z8 K* W7 d# x; f/ o; {( ?
  47.                 return -1;, L+ h6 f6 s& K+ W( T4 J6 P2 x
  48.         }
    : w. m, }( y' j9 M
  49.         printf("accept..............4 d. b9 A" l# D% \3 I& a! s9 {
  50. ");' d4 ~& z) C, ~  {
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    ) |; q: B* b" y# }  [! {
  52.         int ret;
    ' ~3 _( Z: h5 N# j- H' U* W; h
  53.         while (1)
    1 d: r9 H/ I" Z* j  j9 H
  54.         {+ m! ^: P1 i2 m8 J  R
  55.                 memset(buf, 0, sizeof(buf));, c9 C5 H4 R! R
  56.                 ret = read(connfd, buf, sizeof(buf));
    ( h* j$ U% U0 R
  57.                 if (0 > ret)
    3 F( ?9 d5 g8 v
  58.                 {
    4 a+ v# X  h' P) c
  59.                         perror("read");
    2 Y) ?: E4 A/ h
  60.                         break;2 q4 ~) x+ K. f0 f
  61.                 }//执行while循环读取数据,当
    # b0 a% t' t5 e( f
  62.                 else if (0 == ret)3 R$ B6 N0 g# R  H/ c$ I
  63.                 {
    7 X/ E& b; q8 d& ]- I( {
  64.                         printf("write close!4 |( a  w2 q% b5 j8 k
  65. ");2 @  d, K6 v) d+ o" E; r. I9 X
  66.                         break;
    - G3 w4 Z3 s+ c: E- q' T  u+ v
  67.                 }
    2 ?1 Z3 U. V6 A* z$ v! U9 a9 y
  68.                 printf("recv: ");# `* e! i1 B+ G0 K
  69.                 fputs(buf, stdout);//打印接收到的数据
    ; O+ E! u( b* I) ]
  70.         }& K6 z( H. h  e( O! `6 K
  71.         close(sockfd);//关闭套接字
    0 ^6 Y; c. p( P' e6 P% `
  72.         close(connfd);//断开连接
    6 }8 R/ c, Q/ R2 w2 q6 H
  73.         return 0;
    & H: U. z* {* X2 n* h
  74. }
复制代码

- l/ q& s; |, X2 R+ Y( W
- k/ W8 D" {2 n
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    8 S! R& ]/ }. h& T. A0 P" X
  2. #include <stdio.h>
    % t( N  o8 k- I# p  R
  3. #include <string.h>
    : r1 p5 C- \8 A# _% z' Y. \
  4. #include <stdlib.h>
      {: A6 v$ Q0 g9 z+ g
  5. #include <strings.h>
    ! N4 Q5 z* a  B, k! [
  6. #include <sys/types.h>
    " r3 ?) u. X7 }! f- @% y4 A. T
  7. #include <sys/socket.h>- s! P2 U# O( w! G: u
  8. #include <netinet/in.h>6 U: [0 ]. u7 D7 M4 f
  9. #include <arpa/inet.h>8 Q, t3 n! C+ l- q1 a
  10. int main()
    ! h: m8 `! {% ?" Y0 G& g) E
  11. {
    4 D. i, O' j: ]# c" V2 ?
  12. int sockfd;: {+ ^9 k8 L. J' Q. {, r1 g: o& N
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))7 y0 H0 W! {- q8 E/ q% h
  14.         {
    , y  {0 h/ q. |" Y2 P
  15.                 perror("socket");
    ' D. @9 W* ^8 I
  16.                 return -1;8 W2 y; Y5 ^0 @7 j! l5 ~7 `% m
  17.         }
    6 M, A5 w9 J1 X0 M" `
  18.         printf("socket...........
    2 k- R8 z  t2 ~+ o
  19. ");
    4 C- o. ?% g+ u4 q
  20.         
    , F) f7 w2 o" T. Q7 C1 f/ d6 C
  21.         struct sockaddr_in srv_addr;
    + M5 }4 U! `: E1 \5 a+ r
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    3 B" N; }, p# [" W3 X
  23.         srv_addr.sin_family                 = AF_INET;- k, Z: S: b8 Z9 t, j: H* s0 u7 L
  24.         srv_addr.sin_port                         = htons(8888);; G# Q" Y+ f% h  P
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    / v8 u$ y3 D  W0 ?1 `( M5 D
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    9 i3 H/ c: G# H3 N# u8 s& l
  27.         {( J) A5 d# o; Q! @' |
  28.                 perror("connect");/ x% ~2 `$ P. s; t0 Q
  29.                 return -1; //exit //pthread_exit
    0 }5 N7 W- f6 `, T# V5 c# Y
  30.         }
    % [5 W1 E; I7 l, J
  31.         printf("connect..............
    1 [. x+ h/ Y' _8 C4 j: d2 ~
  32. ");4 T: R, M) h# _8 g% }
  33.         char buf[100];
    9 @# y/ G: S8 a' v
  34.         int ret;
    0 U! g  g' ^) }9 K  Q  N/ ^7 |9 {
  35.         while (1). ^4 n3 M7 s) ~& p, k: J0 ]$ I" r
  36.         {' Z7 C- t+ u2 x) ]/ y* D1 O
  37.                 printf("send: ");
    ; P. N/ I" F$ r* G* y  m# C
  38.                 fgets(buf, sizeof(buf), stdin);. Y. b8 ]6 `4 i
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ! f6 e. k% Q+ J
  40.                 if (ret < 0), u; @9 V; U0 X4 N
  41.                 {
    5 `: q  I8 h# B: m
  42.                         perror("write");# c* o( Q- Y5 g/ _8 I$ l6 I
  43.                         break;
    ; }/ u! w5 q8 d# S" p/ b
  44.                 }9 \3 T8 {4 I! }8 n6 P7 P' y
  45.                 if (strncmp(buf, "quit", 4) == 0)2 f& C" n; n, ]4 V1 ~( R* t
  46.                         break;8 @$ D) E; C* ~. ?3 `5 ?$ A
  47.         }
    6 S( h! b1 Y* h0 q) O$ j7 g
  48.         close(sockfd);( U# U9 |: T" {6 A' S. T( R9 C
  49.         return 0;
    1 W  Z) _/ Z- P' q7 `: \4 j1 s
  50. }
复制代码
) }( U% P) ^2 g' h9 H
. ^( w7 O/ V$ L0 q0 M( J





欢迎光临 cncml手绘网 (http://www.cncml.com/) Powered by Discuz! X3.2