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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
, }- B( s# L9 ?$ r$ l& U  ~% T7 a7 C, ?$ ]8 m

. [2 C7 O& {9 q* jsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ W$ [. {* z2 L4 b3 h2 W7 r% K
3 i' U" F# s* Y/ |" _& H" K
$ i+ b* F' y  d9 k
TCP协议
) e$ I# `, X! GTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 O! y& p5 \" Y; k1 D4 N" [) @+ [2 k: w/ ]
, Y- r( z8 g* D7 u1 \
关键词:三次握手,可靠,基于字节流。4 R8 c9 l7 U# k! F0 L# C1 `

" q# `5 K/ |" M5 B) w

7 `9 [) f% |2 |+ i$ g2 I! F可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
2 F8 T# ~3 i7 p: U* S5 J : S4 V2 a1 w+ i4 r+ K6 R7 l! Q
TCP服务器端和客户端的运行流程" d/ J" Y0 v$ {0 D$ V# Z4 j9 x
8 `- c7 S# ], F# o' J8 H+ x  ~3 K

- i: ~" P- G0 O6 d0 F3 D- R如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
& F9 t7 ^0 m5 ]& [* p* t* i" w6 H' K. O" F. I; @5 [* W' V
  P1 O3 [' j3 J9 {/ d' ^
1.创建socket9 U! n( g5 j9 U
socket是一个结构体,被创建在内核中
- o' P# O; D4 a! }8 u4 v( \ sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 |% b; T8 }- F# B  t
" ^" ~- U6 [1 y) N& U2 r5 a% h( ?
# j3 g0 N9 E7 T3 y* q
2.调用bind函数5 v# y* s2 Y! I5 l
将socket和地址(包括ip、port)绑定。
4 j' X! a( v$ _ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) @3 I- u+ @9 W( W
struct sockaddr_in myaddr; //地址结构体& k8 K& M5 s/ y. M* y; b
bind函数# }; j" d/ j! h& l/ v2 }  Y
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
8 q. q3 ~( p! y0 R2 ?8 X! S. n; \# g7 d1 Q8 y

8 j. q+ j& {# \! J2 |3.listen监听,将接收到的客户端连接放入队列; M; b/ b1 B2 L9 s" @: `
listen(sockfd,8) //第二个参数是队列长度2 R5 l: Z+ W% ^
# t  M7 \! {7 X7 A. n

& ?, s# V' \7 w! n  Z& \/ D1 P& E4.调用accept函数,从队列获取请求,返回socket描 述符: j- V( w& V8 F8 \% o2 y: Y
  如果无请求,将会阻塞,直到获得连接
8 F8 K+ B/ _" J  l  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数' I  k. {' O$ v# z' u# |! F; `

# c8 g4 n: M3 U4 T1 S+ s# W" y
/ q- x6 U6 G/ p6 z0 M( S; \; h) J
5.调用read/write进行双向通信0 N& C  h* h: ^* @' Z8 C& g

6 w1 b) F3 d7 j: n% O% x6 V
' Z" S: O, ?4 s+ P* q7 f
6.关闭accept返回的socket
2 Q& x: ]4 v$ x- K  close(scokfd);6 T6 s! P) X" o9 N+ T& d

& m7 @" {- T( K
5 L6 j% R0 ]5 G  i* X

9 ?5 H! `3 n7 h$ p, E, a3 C

' F5 _" i8 X/ k下面放出完整代码# X2 x9 F# e" c/ q% r; J* x
0 i7 v: O; k. f7 U% s
  1. /*服务器*/
    ( ~+ s9 D2 B0 z7 n, S* E
  2. #include <stdio.h>8 i( z8 I" F; l& U; W; g& i! _
  3. #include <string.h>% {/ ^1 s# r' W
  4. #include <stdlib.h>+ c( B5 [" q1 S: k
  5. #include <strings.h>
    ; J3 u% Y8 a5 `' J+ H) K4 w- @
  6. #include <sys/types.h>
    : l! T; \+ H+ k6 }8 }! K
  7. #include <sys/socket.h>
    5 v+ j! @# U  \# n* V
  8. #include <arpa/inet.h>
    $ S; e3 P! i9 k
  9. #include <netinet/in.h>
    6 }4 J; v; |" T2 ?8 ~
  10. int main()
    $ _8 G! ~8 u/ h) O$ ^2 S1 X
  11. {. T0 U9 O; u3 l, |% b
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    4 ~3 t, ?: K7 L/ G
  13.         if (sockfd < 0)3 a' ]' k' U4 T6 \
  14.         {
    # o8 z! u7 r2 N8 h  g$ G& R
  15.                 perror("socket");
    " A- }+ U1 j& d4 e* Y
  16.                 return -1;% @+ d( n7 d( n8 c' D! D
  17.         } //创建失败的错误处理
    ! x8 H9 k  U, C" c9 ]
  18.          printf("socket..............
    4 i: a, b1 L8 x( w
  19. "); //成功则打印“socket。。。。”
    8 B. b- V2 I# d) S2 Y  Q# g8 Z
  20.          
    3 w' O( o; p9 b
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
      S2 q4 y+ H# d$ {
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    0 `0 X8 z# O: r! b  C
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型2 r, {2 s/ T7 j- Q; Z% k( o
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    6 U1 E; X) Q7 T) z  c. [2 Z/ {
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    . \8 w1 s  U, r

  26. + E8 w* S1 E' E9 Q" D  u/ d
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字" o0 I8 Z1 A' |" d# Q
  28.          {2 `5 o) t& ~2 J; \- ~) O& S; ?2 o
  29.                  perror("bind");( m5 f" j- }% e, ?$ d& R7 s
  30.                  return -1;
    % V% j/ [( {* r9 z" ?( {
  31.          }
    , T% R/ Q" }9 g3 a& {" ~5 Y* ^
  32.          printf("bind........../ a: w% B' m4 L% G. z7 L$ _. t
  33. ");
    + o+ R" `& Z1 t- w! B
  34. ) L5 a+ B1 z" _% s) }# _
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    : U+ J$ ?: S- A6 g6 g
  36.          {
    ( ?% ^& R$ L% F% {) Z/ r: O
  37.                  perror("listen");8 l1 z9 F% |" @1 x8 f5 t) _
  38.                  return -1;* ^* I/ Q& t( J  k
  39.          }# ?- y+ q" L& x* Z: c
  40.          printf("listen............
    7 Q5 N. M* P/ n( L' l" D* D1 M3 s
  41. ");
    . ^7 M, T8 g; l1 e9 S
  42.          5 k- z4 r& Y9 S0 b" o
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    6 ?" a9 j# }" k8 U
  44.         if (connfd < 0)6 S, r9 a6 r' x
  45.         {7 P3 R- L6 l/ q, \6 D; w
  46.                 perror("accept");
    # g1 h8 o' z& ?5 U+ m# X. s
  47.                 return -1;
    ! x3 T5 f& W! u3 c' h' d: @8 }& |, l
  48.         }
    1 k, M8 A1 w' v- X
  49.         printf("accept..............
    - h+ Z4 N! g/ b
  50. ");
    : |* |% m3 @- @8 a! Z4 D5 V
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    ' H0 w9 v; m! O
  52.         int ret;8 [6 |, ^0 m, V
  53.         while (1)5 l7 v% c: H- \7 K# \: t
  54.         {
    + e) |% _+ w. o! Z! N
  55.                 memset(buf, 0, sizeof(buf));8 Q% I, h5 \& k1 _9 u5 x. y
  56.                 ret = read(connfd, buf, sizeof(buf));
    : R" j: U( `2 W9 L. f
  57.                 if (0 > ret)* @) V% F  |+ I" H
  58.                 {% R) U" f* z; g% e% |2 s) p
  59.                         perror("read");; m& M2 T* {, w1 k, y* r  |
  60.                         break;/ d' |/ B5 n+ a, A3 V, g# t: b
  61.                 }//执行while循环读取数据,当, ^% b! X) j) R0 ~
  62.                 else if (0 == ret)
    2 a/ ^7 e. J& v3 l) P0 @
  63.                 {. P9 ~7 `5 @$ m2 M- E9 ^
  64.                         printf("write close!
    & K% ^2 L/ Y* W$ v
  65. ");9 `2 `7 r; l1 d$ }; o
  66.                         break;1 |- ]- }; {+ s/ ]4 L/ q
  67.                 }* S9 O5 M! v8 B5 _+ i9 u
  68.                 printf("recv: ");
      w1 o0 C9 @# G. ?( Y8 `! K8 s3 r
  69.                 fputs(buf, stdout);//打印接收到的数据; Y8 P, _+ h' ~1 [
  70.         }
    . A, A8 [5 h. N3 Z* R' C% [
  71.         close(sockfd);//关闭套接字
    6 n! t+ @) H$ j
  72.         close(connfd);//断开连接' ~; R* ~' _1 d  B1 Z4 j
  73.         return 0;
    * H. K, P6 J0 g9 u* C" G
  74. }
复制代码
2 T+ W5 W+ [. I3 \' ^' _4 K

; j3 A: z- y; h' T- y6 p* |" K& Z
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)4 h9 f4 N- f3 B8 z
  2. #include <stdio.h>
    ! y$ B- X8 e5 N( i
  3. #include <string.h>! x- f+ G7 S  [; U% p
  4. #include <stdlib.h>* @# `3 \" @, ^1 p) i
  5. #include <strings.h>8 s. n4 J9 K* d3 G6 \/ g/ [6 R
  6. #include <sys/types.h>
    1 j* c( J7 G" r
  7. #include <sys/socket.h>
    ) j* _( H8 j, K! B7 O0 ]- Q
  8. #include <netinet/in.h>$ q  p" Y7 w7 {8 |( y: G/ l# f4 j
  9. #include <arpa/inet.h>
    ; q/ [) p* L! o' \+ u2 t0 x
  10. int main()4 d0 G' a3 J/ @, ?% U' n
  11. {: C: q4 `/ s4 a' m
  12. int sockfd;0 q" ?$ b2 O8 p9 I# E
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))). O& p9 R$ d* E: m' }
  14.         {& k0 L: a; Q& J4 M! }4 h
  15.                 perror("socket");
    * t3 d) n" B4 n. z4 x6 @
  16.                 return -1;+ H4 g# h, Z: K( Q: G# K
  17.         }- r* {9 g* L7 C4 U
  18.         printf("socket...........5 z+ w% B. ~0 g/ J
  19. ");: p; G& K$ d/ ~9 }0 ~
  20.         5 I' `, R  v4 \& M6 i" |
  21.         struct sockaddr_in srv_addr;+ e* T+ Y* p# U5 g% t
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    + h- Q3 B! z6 {: q+ ?
  23.         srv_addr.sin_family                 = AF_INET;
    & u/ f* a" W. S' l
  24.         srv_addr.sin_port                         = htons(8888);. {( h5 |  G5 y) k. a0 F; X
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ) N4 e# u) g5 L3 n# n# Z- ?. N
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    $ ]1 C* D9 T: z/ }7 A" k
  27.         {2 T# z7 k* C, Y; _5 D
  28.                 perror("connect");  A: \0 }( h& w; G( I
  29.                 return -1; //exit //pthread_exit* R$ M4 H6 K9 a/ h9 f) E' |4 @
  30.         }
    0 x5 x/ Z5 n& f+ x: u( ~4 Y
  31.         printf("connect..............
    # Z1 R8 U& j* v% L" T- o6 t- @
  32. ");9 t, X- u/ \" L9 ]! r
  33.         char buf[100];. U1 ^. W( B7 X/ `& m( Q  ]) E
  34.         int ret;; D9 x* W3 M6 T/ T  q
  35.         while (1)
    " r4 E- y& l* Q3 ?& r' P, N# t# }
  36.         {$ c# H7 H- n# F$ \
  37.                 printf("send: ");
    ; q- ~+ }& }. x7 x9 f/ x+ H
  38.                 fgets(buf, sizeof(buf), stdin);5 b1 A+ Z/ y- z6 Q2 T7 n
  39.                 ret = write(sockfd, buf, sizeof(buf));  o8 @; O, w+ m; [
  40.                 if (ret < 0)
    # N0 \& ?1 [4 C
  41.                 {; g* H, [, w$ l  Y4 `( e
  42.                         perror("write");
    # l8 L0 y! ?* H
  43.                         break;; q4 F- A! l! _+ J/ P
  44.                 }& @) s& z/ s" G8 i1 S6 W/ u5 y
  45.                 if (strncmp(buf, "quit", 4) == 0)" D; l6 e* `: a6 e% B
  46.                         break;2 U6 Q) L5 Z3 O5 \/ _
  47.         }
    - V" }% \; b4 b$ @" |* }( F* ~* L
  48.         close(sockfd);% N# l  b$ Q: P" K
  49.         return 0;
    # l% T- @7 f+ o- h% e
  50. }
复制代码

9 n4 T* X, N3 `2 y1 {1 a& B/ {; I) h( X3 W: _# t/ @. W4 a. T6 [
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-3 07:57 , Processed in 0.116674 second(s), 23 queries .

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