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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
' K/ I: Q9 I  u3 K0 P3 `
7 u; D/ W8 m* b
0 g# Q' d# B/ o$ I
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。0 k. c* s) R: I4 p% b
2 t, g9 t* M+ O" _2 w# z+ X( E

2 j: g( n6 k$ f7 w4 wTCP协议. y# |6 Z+ ]# k# t4 k8 S7 m$ g0 F; [
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
- B3 n2 U# o2 t' ^1 X; ~" K" ]4 l% B7 h5 d5 Y0 ]1 [
% F8 C  ^4 R/ C9 q$ O- y5 O! M' ?; ?( G
关键词:三次握手,可靠,基于字节流。9 N$ F# I6 f1 G5 T2 b
) Z2 s, l( }4 q: O  _- l
3 x& _+ W8 h' R' U1 Q
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
0 y+ q7 y, A) z: [: F
! T  u% e8 Z/ a, W+ J# u1 }' HTCP服务器端和客户端的运行流程  n# y0 W7 N" Z$ s# p

9 H2 N1 I! e7 r# ~, v' H' A

5 b9 S9 T) [9 j如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
6 n' J, Z- l1 G+ C- B4 X$ x
% Q$ V% S" X4 g5 m/ K6 w

/ |  W7 Y% a6 Y3 F: J- j, h1.创建socket; \3 r, D3 S/ i  d3 O
socket是一个结构体,被创建在内核中
3 y% a1 |, S; V9 j, f sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议3 T: o+ E! Z7 D- U
5 f. |8 Q/ H6 L& A

7 O8 f/ t/ N* ~- j  S! _2.调用bind函数7 @4 J; q5 u  e: q
将socket和地址(包括ip、port)绑定。
' A6 A/ d2 h( g  S; h$ ~' ~3 m 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
$ k$ H, A0 p3 I- t struct sockaddr_in myaddr; //地址结构体
: H& o/ w; P5 H bind函数& G, x# M& e& E/ Y
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
9 ?$ q8 E/ d" K3 w( [% v3 i" Y7 [8 p5 Y6 R/ b2 r' O

' w# A: a1 }7 G' p* q9 C. W3.listen监听,将接收到的客户端连接放入队列
' g- \9 h1 E  n6 h' I listen(sockfd,8) //第二个参数是队列长度
/ h& X# e1 M+ o" z
2 q; q' Q6 l8 w/ e5 S

" ?0 s: i/ u. B* a  X4.调用accept函数,从队列获取请求,返回socket描 述符
. a( I/ Q/ E1 B) Q# E. q7 Y  如果无请求,将会阻塞,直到获得连接1 z& n& s$ A( w! s
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数3 z) G1 U: q' R5 @) w. j

; N* F& w% q' s

" z/ }) H: {7 d2 Y" C5.调用read/write进行双向通信1 _6 G. P: u. o. j, |

4 g' `+ L+ Q! u" k" @* n/ T
/ D  [) _$ o, l  |
6.关闭accept返回的socket
5 _6 o9 C7 ?6 I0 ?6 {# q, l8 F  close(scokfd);7 W8 e) t! m8 E) P

0 y+ c$ T2 U  X- C
6 F' U0 K& Z+ f) H% A3 G
/ W2 |  y/ I" L
$ p- R7 z, O& O0 @
下面放出完整代码
4 _" L$ W5 k9 w5 ~' c  C& d& F! V6 D* `7 K4 N6 r- ^
  1. /*服务器*/4 Q" L& s$ Y# a( y+ y& a
  2. #include <stdio.h>
    0 P' K* f  T- ]
  3. #include <string.h>
      h8 \" E. o! a6 x2 _
  4. #include <stdlib.h>8 I8 [5 X" `3 y6 T" y
  5. #include <strings.h>
    1 `- q4 j/ Y* \7 D( X, {- D7 m
  6. #include <sys/types.h>
    4 m7 o" F/ u6 [2 e% E
  7. #include <sys/socket.h>, B7 V: a1 X' _7 P6 b: m7 T& d' Z
  8. #include <arpa/inet.h>
    ) x8 ^3 K$ v4 Y3 ^4 T9 I
  9. #include <netinet/in.h>% E' C! j' A* }2 R) N: T
  10. int main()
    3 Z% X4 z' y) A( U" \
  11. {
    * E( r2 k- P, b- e4 f; d( U* ]8 o1 P
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字/ z8 A; i  P# a$ X5 p
  13.         if (sockfd < 0)( c# }- k8 S, `+ k. P' N
  14.         {0 Y: f! _$ v3 t; N% R
  15.                 perror("socket");
      g6 V0 u. F/ O3 y* H
  16.                 return -1;; j7 Y( q5 H& s% ?, O" ?( i
  17.         } //创建失败的错误处理, q: }; q+ V5 y$ X5 L9 D) [
  18.          printf("socket..............
    5 k# B2 V1 ~1 N( c
  19. "); //成功则打印“socket。。。。”5 o& V* n$ A) y" b) d5 x( @) }( ^
  20.          
    * d) }3 I+ W( E( ~
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体" D5 ?- c0 |1 Y* M/ g! t$ ^- G
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)! F3 e* Y4 E8 r0 E' u6 m2 F
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    $ N( q1 m* X/ m4 j1 Z/ R2 o6 n
  24.          myaddr.sin_port                 = htons(8888); //选择端口号. b  u8 r+ U/ Z
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址' a4 f+ d1 m; T! q

  26. 4 Z3 b0 [0 a7 l# a' q, y7 y5 m, c
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    # L, @8 ^, e2 s7 _& F# z# t9 ~+ u
  28.          {
    ) L6 W+ y, r: B
  29.                  perror("bind");' p+ B+ |% C& _8 B5 @2 ~! [5 j' D
  30.                  return -1;
    7 ]6 X6 A, e$ o$ d) [
  31.          }/ K0 h. e$ N% I' L! p
  32.          printf("bind..........' [0 C( `% G' T3 n
  33. ");
    . m" e' q: E1 Y' Q  `
  34. , A* [- j& W2 o6 G$ v7 {
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听* X% u' U9 ]* N. ^
  36.          {+ u5 C# \8 y. r1 Z; V
  37.                  perror("listen");
    9 W; X( B8 ^7 Y( e3 P
  38.                  return -1;6 v3 c4 E2 E5 ^# ]* |
  39.          }
    ) M3 |7 S& M8 U; u
  40.          printf("listen............1 {- v- \% G8 h! A8 [
  41. ");/ M6 j0 a5 \2 T  y
  42.            Q" [- }5 D& o* e1 z$ d. w5 c
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    5 e& e2 f, f8 a
  44.         if (connfd < 0)
    + L3 t2 M4 m3 K) w$ v# _: G7 a
  45.         {
    $ ]  z( r6 k+ v$ y2 s# p8 v- Z
  46.                 perror("accept");) T) x- T% |# L7 e
  47.                 return -1;
    & d1 G  C: [& q& D  L" }% d
  48.         }
    / p2 O& ^( Z2 J0 C0 k; ^; ]1 G& t! U
  49.         printf("accept..............- T9 M1 R: ?# H$ y/ l
  50. ");1 ?" k: a7 S, i
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    , q- u* w4 m+ ?
  52.         int ret;
    ) O% l, }" h* ]; V( w1 \
  53.         while (1)# s* m, n9 }* j
  54.         {
    + U8 ~% D7 f3 W1 @; M- \
  55.                 memset(buf, 0, sizeof(buf));8 J, S% Y% j( v& L( R( V" Y' L
  56.                 ret = read(connfd, buf, sizeof(buf));) U8 R% S- R7 v3 O. p( ^/ G
  57.                 if (0 > ret)9 c, F* \; Y1 i' }0 Y
  58.                 {7 G6 x* I& l3 z$ S+ E
  59.                         perror("read");
    3 J% f) v$ e* C( n" O5 A
  60.                         break;
    : ~/ p! U3 w( u4 b- D
  61.                 }//执行while循环读取数据,当* z+ _* N9 }, V( A6 D
  62.                 else if (0 == ret)6 R0 q" |; |. F- f: e, s3 H
  63.                 {; q0 B. L% C- g4 n" s
  64.                         printf("write close!
    ! c% C) N2 U4 d3 ^' H
  65. ");# e5 ?4 q4 O, e
  66.                         break;  _0 m! V. Q/ H% c+ \6 e: x
  67.                 }
    / q% M, T, B/ H  l- D1 _. f2 {
  68.                 printf("recv: ");' I/ S9 C$ Z, o
  69.                 fputs(buf, stdout);//打印接收到的数据
    1 m; D& j! K' N8 h: \0 L- \7 W
  70.         }
    & V+ {, j( I% B7 t" ?; @1 Y2 L& R
  71.         close(sockfd);//关闭套接字
    & ]) Z4 @2 a2 e5 M5 k
  72.         close(connfd);//断开连接
    - q# W8 @4 E& A
  73.         return 0;
    / z. I1 K' o1 i* w7 a4 x# i
  74. }
复制代码

4 r/ v% S! J( x% ^8 C# J5 G5 W1 ?* ]2 e: }6 U( E- t
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    1 \7 u5 ]/ }6 F$ I9 `: t0 H1 {7 ^
  2. #include <stdio.h>5 y/ \( i: e  ?, g" h* K2 e
  3. #include <string.h>- e" A5 r5 u/ d: R1 k# V' j
  4. #include <stdlib.h>( C, F# L! j0 R
  5. #include <strings.h>0 N, Z7 N1 i$ t$ X
  6. #include <sys/types.h>8 ?' B$ [. a0 Q" e' a% Z/ Z
  7. #include <sys/socket.h>* l: s8 i9 E) Q; Z
  8. #include <netinet/in.h>' j! ?( N" K( o4 Z$ m" s# N- ^- d
  9. #include <arpa/inet.h>
    , c2 E4 k7 f2 I& @' y8 p
  10. int main()5 K! o7 L5 i: Q& p* O5 l$ z# Q
  11. {, w6 I- x" l# c/ r, f
  12. int sockfd;
    0 m* ^' t' s1 d, p# l
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))- P" [3 x4 m! x$ f. I
  14.         {+ _- @8 [5 z3 H6 i* E0 \. F
  15.                 perror("socket");! n) Y8 }! I  q0 i
  16.                 return -1;6 d+ [! w6 g$ i. D9 `9 F
  17.         }3 B9 S6 c& h6 _- m) T% n8 n
  18.         printf("socket...........
    % O+ H0 s6 ~+ D( E# H
  19. ");  e# }8 e% `+ T$ c8 |0 ~
  20.           _! b. l6 B9 k' `( T& m9 J
  21.         struct sockaddr_in srv_addr;
    ( u8 _- o5 w. m2 K1 A
  22.         memset(&srv_addr, 0, sizeof(srv_addr));  J1 p3 ]8 d) o: ]4 ]! S3 H
  23.         srv_addr.sin_family                 = AF_INET;  p! k, R6 r- l" n+ @4 p
  24.         srv_addr.sin_port                         = htons(8888);
    / |4 [% D5 v- V( \
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    + K% W5 a8 c& A0 W* J2 m$ T( O1 W
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))1 t% C3 K, s1 n4 z5 X8 m5 `
  27.         {
    . M/ G! t/ p# ?
  28.                 perror("connect");
    3 R, D8 L! k6 |- b2 i) a" t- f) z4 W
  29.                 return -1; //exit //pthread_exit
    ) k# `2 t. _) ?  X' S0 Y
  30.         }
    : q5 W: x& n* D0 L9 a% Y
  31.         printf("connect.............." R6 J. Y( `' K$ F( E2 h8 r( \9 y- S
  32. ");
    . p2 x. K! t9 Q% V# Y
  33.         char buf[100];  ?4 o7 i6 F; [9 O
  34.         int ret;6 ^; z, h' E! d% h
  35.         while (1)
    1 F6 ]3 o% L- b! x: h8 M+ Z7 m
  36.         {, ~) z7 M3 Z( d9 I6 L  D9 _! K
  37.                 printf("send: ");
    , e+ V2 A- e" n
  38.                 fgets(buf, sizeof(buf), stdin);
    8 C( R4 R, ~9 Y8 x# K
  39.                 ret = write(sockfd, buf, sizeof(buf));- H# p* ~$ q- H6 C4 h" Z( U( E
  40.                 if (ret < 0); P. \* H0 F7 \# O' C
  41.                 {9 J9 J4 @( ?6 @" c) Y# p
  42.                         perror("write");9 B4 r4 Y6 Q  h  Y" \, d( K3 N! F1 o
  43.                         break;  n) [+ V2 c2 C: E6 N8 w) f
  44.                 }
    $ {' u9 n! k' `5 G* K9 U! {* t
  45.                 if (strncmp(buf, "quit", 4) == 0)
    # S. ?+ b% q& i9 f( U! x$ q, L
  46.                         break;
    9 @# [5 T- f7 @5 u0 ~8 H. P7 z( ]
  47.         }7 G* f- L5 z0 }* o7 n
  48.         close(sockfd);
    4 B' D! P0 j* a! O  p
  49.         return 0;
    8 ~' a: ~: I8 q, P4 z$ h
  50. }
复制代码

: y" ^0 i7 a7 e1 ^1 n9 h6 J, \( W+ r& ?0 X8 M4 i
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-17 18:43 , Processed in 0.158703 second(s), 22 queries .

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