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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
+ J$ P6 |  r5 @9 l7 B' e) q3 W* w2 W! G: J% Z
+ R: v$ {8 y- `! |
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
$ F, f6 U6 m9 d0 T- C
& ^$ y# Q' `: E& }
: `8 c5 X% }3 e6 I, {( }
TCP协议
, W. R  y3 e+ W# UTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
: E2 y0 C2 ?/ j+ T/ Q
2 E3 ^) I. {( ]) y: g" {) m
9 D# a* u9 e1 q8 o
关键词:三次握手,可靠,基于字节流。' S# [2 O+ z. c4 `: f$ u
+ k$ X0 j, K, F+ m2 E5 O

- g6 I6 v& L8 E, ~& s: {可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
1 W( r" N8 L4 Q# _6 \
3 o! j: O8 o% e2 i' o' N( {TCP服务器端和客户端的运行流程# G' b8 k& `& S  u

7 g( L! |% g, L

& ?; K3 Z1 t: j4 n2 S& b: _$ L如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
- {7 Z2 V1 g# v- B, Q; e& \+ G( i- S+ E8 q6 Q
% t4 Q' B7 |7 c* H5 D
1.创建socket% |7 a% R. r  T+ T8 p" }; w
socket是一个结构体,被创建在内核中4 P# h. O1 k. r/ u+ R* p/ V. T
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议# Q% h% z3 C. r1 d. e4 X, J
/ o; Z1 n) l- V- g& B; o
. O4 H9 D$ I9 G5 D' r8 A
2.调用bind函数, ^4 I$ c$ c3 z( G: k
将socket和地址(包括ip、port)绑定。8 m% g9 b) G- T3 U9 v& V
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序' x3 o2 U* k5 n2 _' A( h* I
struct sockaddr_in myaddr; //地址结构体  v, f. d+ I! \$ p( q5 _
bind函数3 F0 M' @' }$ M
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))) M# |" s6 P- Y

8 A3 v! j4 O9 z( Q8 \

0 a  ^  U+ N. e7 N3 I- U5 j/ X3.listen监听,将接收到的客户端连接放入队列
3 O1 P2 H) Y& w9 }8 [ listen(sockfd,8) //第二个参数是队列长度
' X& K( Z2 d* r" J8 `1 q: v
% o0 o% P& A4 U% Q7 {# ^5 R" m5 w

) w6 F, G) ~. f4.调用accept函数,从队列获取请求,返回socket描 述符
2 {7 Z2 G2 K# L0 b: F7 r  t9 S  K6 S/ D  如果无请求,将会阻塞,直到获得连接5 S) f/ j" }8 p& N; g% g+ C
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数5 _8 i3 P6 p# w# w, a: u

$ O$ W3 q# R, x6 e# z9 `* h
  Q# N) U# W3 d! I! K
5.调用read/write进行双向通信
( T1 S6 k' D9 K% y% ?, F
. C8 ]" w% @4 i, I, g+ x
  s- x, o# {& d# y) N
6.关闭accept返回的socket9 P7 j+ J( A$ B! @5 ?- x# ^) b
  close(scokfd);
9 o1 o' [" k% H) j0 H: n* u7 O; P$ S+ x: p' h

3 |. s. k2 Y% W4 b
  S3 \! @- t0 n6 I& M( k

% q, x& u# k: j' N/ C1 q: ~下面放出完整代码) w8 K- V& U, K. _/ p) G$ P
$ L+ I* F* D0 b8 X; U
  1. /*服务器*/4 m+ Q" m3 R, g& X
  2. #include <stdio.h>
    : |% Y9 W* ?6 Z, m
  3. #include <string.h>* \5 [3 j+ P6 x- O7 {- e  U# A) s
  4. #include <stdlib.h>
    - Y/ v, x# s- [5 `) |* I0 m
  5. #include <strings.h>* s  |/ i7 B: M% U
  6. #include <sys/types.h>9 S+ r8 ~5 G9 k0 K; i- P
  7. #include <sys/socket.h>9 L% q/ @7 b* ~, I( f
  8. #include <arpa/inet.h>
    ; S/ b5 ?2 x% W, P3 t, ~. m
  9. #include <netinet/in.h>
    # I7 W# Z2 ~; O
  10. int main()
    4 S1 T  Z& D, \! Y. M3 Z
  11. {
    # y- z+ h, T" Z* ~: `/ P1 q6 R' e
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字# i. s# {) ~! K3 j4 }
  13.         if (sockfd < 0)6 @4 V# J' q2 y$ Q# }! ~" C- R
  14.         {# \( Q; l( k" |
  15.                 perror("socket");6 @4 |* P: V( S- Z7 W
  16.                 return -1;
    , G4 m  I9 P& b! M
  17.         } //创建失败的错误处理; I$ `+ N5 r  Z2 R# ^/ K) b
  18.          printf("socket..............7 a8 M/ `( j3 i# ]
  19. "); //成功则打印“socket。。。。”
    8 l* d7 Q$ B+ W: u! P/ S
  20.          
      \& f3 h" V: M9 e" u
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    # j! A2 l$ z  O7 x9 o: ^* w9 l
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    % `* h' d: \4 b" C
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    5 P' e  B' a$ c! I" `4 z3 {  O
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
      |" d- \" R8 f( S
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
      P3 u  M. q. O6 @! b5 A) [! z* S
  26. : r& T, k1 P) }. G; ~% f9 o' ?
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    1 _7 t: E; y3 b" z
  28.          {+ v% ~5 C% j; d* I
  29.                  perror("bind");
    8 f4 T) P$ L; \7 ?0 E
  30.                  return -1;
    - N+ z6 ~$ p# b; i5 w" [
  31.          }
    8 _) a9 _, R+ j3 T! n
  32.          printf("bind..........; M. W0 C' w2 {& W6 p; d
  33. ");
    ( f1 R6 V+ T+ t! w) J
  34. ) v% ~5 Z  z, z- Z" r& E$ q
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- L! n9 O2 ^6 I3 d+ i2 w
  36.          {
    : J  m+ |+ h0 X! h9 Y, ?# p
  37.                  perror("listen");
    : x, z) h1 C2 ]
  38.                  return -1;
    . `6 ?2 ]: D& N
  39.          }0 H! \) _- g: ?
  40.          printf("listen............
    ' n3 `/ D) ~4 r/ O
  41. ");9 n! C* |) E% Z& n2 z
  42.          * o& D- O6 I* _' j2 J& v, x
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求1 T" `) ?# l& |
  44.         if (connfd < 0)
    " {" J' b$ a4 j0 f5 L/ L
  45.         {
    3 `1 M  q* ~- s+ ?6 T* c
  46.                 perror("accept");+ p/ V1 z( x. j6 I0 {# N4 O
  47.                 return -1;
    3 G5 [1 i9 N( M5 f: B8 @8 j
  48.         }# j* q9 r5 D+ W1 k2 D# Q( i
  49.         printf("accept..............
    $ T4 T/ I! A: N6 a% J
  50. ");
    3 ^8 r  h$ y( [( E# D' v
  51.         char buf[100];//定义一个数组用来存储接收到的数据8 D. w# n' S& e6 Z, ^2 l
  52.         int ret;
    & P7 Q( X* G; U; _. w
  53.         while (1)
    5 v' I2 H- x% o& s0 p
  54.         {
    4 ^$ f8 w5 u) s1 |1 p- P
  55.                 memset(buf, 0, sizeof(buf));
    3 U+ j7 _( b7 H: n* h' Z3 C7 n0 X+ W3 l
  56.                 ret = read(connfd, buf, sizeof(buf));% P+ N& Y2 R# N0 |
  57.                 if (0 > ret)5 P# S6 S5 O5 d* {
  58.                 {
    / A2 k8 _6 T- ~( S: U: C
  59.                         perror("read");3 w1 L; a, o9 E: ]5 k5 l* f! k% I
  60.                         break;
    5 h0 h, l7 N9 O5 Q9 w+ ^3 b
  61.                 }//执行while循环读取数据,当8 Z/ g/ f# b& u/ v. B
  62.                 else if (0 == ret)
    " p, l( x0 k6 ?' ?/ R
  63.                 {5 j  y' |1 z) r. |# B2 X
  64.                         printf("write close!8 c: I( U' }" C
  65. ");& _- T! S, s6 N/ H
  66.                         break;+ G( V0 S/ M# j% Z9 |2 J
  67.                 }
    ) ]+ G1 v  \5 ~) v# y
  68.                 printf("recv: ");' H" N" u5 a" w4 U/ Z
  69.                 fputs(buf, stdout);//打印接收到的数据  C0 P  a% ], M# O* L# i- Z
  70.         }
    ' B+ p. Y  r3 ^
  71.         close(sockfd);//关闭套接字# [8 R- R7 g8 W  v( q
  72.         close(connfd);//断开连接( ]/ a' P/ k( X% r( {" U4 f
  73.         return 0;
    4 n' T  S8 N+ f/ d) i. k! c6 G
  74. }
复制代码
7 X$ f$ q1 B. w* t& \

8 I; C$ g1 p, o5 t2 J' P1 P' i; H
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    ; H" t* w1 n$ ^& b
  2. #include <stdio.h>6 f3 s! p) T) m
  3. #include <string.h>
    ' X1 N$ r5 W5 S' B
  4. #include <stdlib.h>
    % W8 ]9 j/ e; r6 L
  5. #include <strings.h>
    - C; i$ M, s3 X7 X/ ~5 `, ]
  6. #include <sys/types.h>
    ; Y" G9 q) y& O
  7. #include <sys/socket.h>6 C( c# g) r$ B- b
  8. #include <netinet/in.h>
    . E9 k( t, a7 f6 G& g" S
  9. #include <arpa/inet.h>' y9 C. [( I; c9 c8 F6 ~1 w. ]& R
  10. int main(): ]9 `" {  I5 l& P
  11. {# _% I# u3 d+ Y/ N' h6 A1 w) }1 J3 X
  12. int sockfd;
    - r7 b' L/ h% B( b4 j
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))% p/ e% _$ ^$ _% \& e
  14.         {* n, Y0 y4 R0 `) m8 P, A) p
  15.                 perror("socket");
    ) B. \3 k4 V6 K% ]  I8 u" w8 X7 C
  16.                 return -1;
    & l9 Z) V3 y( i* R( b' Z- h
  17.         }
    + q0 g3 O; X2 W( C$ s$ s
  18.         printf("socket...........
      t/ |) T, N3 D4 g
  19. ");5 W+ ]# o& }) x( Y+ `
  20.         * T7 M6 U' u+ K9 Y4 Y
  21.         struct sockaddr_in srv_addr;3 U, m- |! g( t6 j* h
  22.         memset(&srv_addr, 0, sizeof(srv_addr));) w6 u7 a. @0 M/ J' Q' o
  23.         srv_addr.sin_family                 = AF_INET;
    5 F# y; U  G$ E) R0 A$ W. Z  H0 ]" n
  24.         srv_addr.sin_port                         = htons(8888);2 k1 y  b' V# M6 c
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    2 }% }. p& Y+ W2 V9 H) V; h+ X; B4 d
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))  I" ~1 m4 a7 J! _2 m* e
  27.         {
    ) a) ~# l' E) S( S
  28.                 perror("connect");
    ( s( w: s/ |0 A  ~& R4 Y
  29.                 return -1; //exit //pthread_exit: B( p. ~. D% J/ P! q
  30.         }. v- [- m4 a5 o, D' T8 D
  31.         printf("connect..............
    & F/ S( B& o* D0 Y5 o3 J. T
  32. ");5 g1 ~0 g. N! w9 M& M  l
  33.         char buf[100];% [1 G, k6 Z2 F& u% \5 P
  34.         int ret;; Y! H1 l# N' a6 S0 H) B
  35.         while (1)
    ( D1 k) ?+ ^# I) G' `/ {
  36.         {
    8 v7 E5 X6 f7 ~  W" M9 |5 p
  37.                 printf("send: ");
    9 _7 d! x; ^9 v6 ~) C" F2 W
  38.                 fgets(buf, sizeof(buf), stdin);
    1 m& E  p% c: Y8 _  R; U
  39.                 ret = write(sockfd, buf, sizeof(buf));8 g+ N/ Q7 h# _; K! |0 j2 K
  40.                 if (ret < 0)
    , _( y- T4 Q8 z7 }4 b0 }! Y
  41.                 {2 y) {  R" I+ p
  42.                         perror("write");
    4 Z' [* h0 V; [$ T
  43.                         break;
    % J& w7 }, K! ~! i' R( X9 ^
  44.                 }
    5 S, R0 ]8 L6 W& R. l
  45.                 if (strncmp(buf, "quit", 4) == 0)
    8 l9 D3 b8 A% K  A) H+ u/ q! i
  46.                         break;
    " t, I3 e1 z$ C! }- ^4 T9 c
  47.         }
    : t$ S  g  N( O7 V9 V
  48.         close(sockfd);
    4 V9 ~2 t7 g* z8 U9 w0 Q& R
  49.         return 0;
    . v* {5 {$ T1 a# _, h7 M; P
  50. }
复制代码

+ f- M( }8 U5 V' k) a. l7 g/ \3 J% B. v' i/ H, A* |8 j. c
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 06:19 , Processed in 0.138958 second(s), 24 queries .

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