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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
1 h' w3 u& y5 Q/ Y6 R
% _" T- x% I% \) x

6 G& `1 O! e, B* qsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
0 ~+ h( K' e0 G6 \4 Y7 g7 D; @$ d: }, |  W0 ?5 q- a9 ]* o
6 R. y2 k" S+ `. s7 Z0 I$ _
TCP协议
0 A; w: k+ `; F" w* j( STCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
; j4 \9 @. z0 ~" s2 x; W/ H0 V4 r1 Y& e! B- J1 a# v

3 f( P) _. L/ V/ d关键词:三次握手,可靠,基于字节流。! t% Z% z  N& {- p) W  r$ l
( `+ v$ ^! p: Q* W  h3 l

/ R) w& ]+ t/ y% [可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
8 |7 i' I. ^$ p0 t# w 0 C4 f( X+ B, O" ~  l" [  d  e8 |* ^
TCP服务器端和客户端的运行流程# \/ L; ^) f, R$ r

) a& p1 c1 q1 ~( x
$ Y2 S0 L; v% K: [6 _
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
. \3 h/ y8 d8 e5 W& w7 P" v  F  B2 s/ o$ }2 c1 I
' O  J( v$ H0 ]# `  E8 C. ^
1.创建socket& @0 K" \% q3 ?7 i8 G
socket是一个结构体,被创建在内核中
6 c7 p& G% V) \1 z+ Z sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
8 R, ], @2 B# O/ ]! K6 o. C. C2 f7 Q. `4 [

  w1 c3 A1 S* U4 @$ R2.调用bind函数
, ^6 K0 ~; `0 } 将socket和地址(包括ip、port)绑定。- S' u% i$ S( \4 e+ I, {+ N
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
  g% N1 Y' j9 @1 Y' W4 u) z struct sockaddr_in myaddr; //地址结构体
9 B' T4 a2 K4 o0 C9 B' t) C bind函数
# V  r+ p4 O/ G% `6 U1 b bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
% ?. o" V5 ]& ^* O' h8 I
3 P/ r  M7 @% v& F: I
( L; R$ s7 x( ?* u2 I
3.listen监听,将接收到的客户端连接放入队列  d8 i0 M; D& [
listen(sockfd,8) //第二个参数是队列长度/ [- u. i! W; D3 I* F- x
+ X( m7 c" }! O
6 Z2 ]2 C) F  a, y2 }
4.调用accept函数,从队列获取请求,返回socket描 述符6 ]8 g# q. u1 }
  如果无请求,将会阻塞,直到获得连接
3 s: p" z! ]1 k& ~) g  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
) p: t! @7 O5 D2 \3 d- j7 J! j, w" h3 N* D% M7 J- `* S0 k5 s
- [: S; ]0 x8 Q
5.调用read/write进行双向通信- x4 V( m. v2 }5 f. m

; _5 }+ g# B. a, n, G
  ]8 O4 a! d/ P4 H! I2 `: b
6.关闭accept返回的socket9 ]% N, a8 k4 O( C) r% W# y
  close(scokfd);- D/ v0 A. u# l, s& O+ g

* g" |7 K( k" V: V& B
. J( e* J; ]+ |; J
# \/ w  @" B/ n, F) K! R

& c/ E7 z& F+ U4 x2 `下面放出完整代码
: [. D' A! k, a) y! Z8 q8 Y" d7 o; e1 s) i
  1. /*服务器*/
    & W- r9 B  N1 w- s5 \
  2. #include <stdio.h>
    3 E8 }( s( `/ N, |- \4 a
  3. #include <string.h>
    : X4 I- }" }; f  h& J1 B3 g- J
  4. #include <stdlib.h>
    ( f; H  u' J- |+ J4 ]# t5 R
  5. #include <strings.h>$ T3 o) u) ^0 Q* N7 ?
  6. #include <sys/types.h>- s5 H( Q8 i0 @/ o
  7. #include <sys/socket.h>
    # E& V3 n  t! Y3 r. K5 d
  8. #include <arpa/inet.h>
    - [% u$ q2 o) ?+ r, V
  9. #include <netinet/in.h>! o4 K# t, l- @$ X8 S& j
  10. int main(): {3 E1 V* {4 D4 C5 I' A2 a+ r
  11. {
    ! u) ^% w( B0 t+ }0 j
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    9 X* t7 R3 n' F7 k% {9 x# i
  13.         if (sockfd < 0)
    ; z9 ]4 {# ^6 b/ T
  14.         {
    ; Q- w' ]0 ]# M! }4 P9 e* o
  15.                 perror("socket");
    4 _/ F9 u' \$ U* i/ ]* P/ _! s: L3 _
  16.                 return -1;
    3 y! J% E) v0 O, ]" T
  17.         } //创建失败的错误处理4 C, m+ \4 @5 v; |- I5 I
  18.          printf("socket..............& o( a4 j0 }. ], Q( t
  19. "); //成功则打印“socket。。。。”
    % d: T. p8 H& |9 H
  20.          
      r) V* u) k& q1 `
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    : h* T$ X3 y0 [: b
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)1 @1 d8 d3 L) a7 x+ t7 ]
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型1 i# I1 J4 d/ j1 ^
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
      ^& F9 f0 e) _! W  |
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    0 \% {$ `5 {: m! m
  26. 4 m* H  A9 x; B( _' i& W
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ( Q, x" f9 z; E- P" q$ D9 w. k
  28.          {
    : n9 f# I5 D& H. o
  29.                  perror("bind");
    3 o# |; O8 u4 E% o7 b# e: K% Q
  30.                  return -1;7 Z, c% Y6 Y6 W. F2 W1 b+ S6 {
  31.          }: G' r. r) G2 U6 L1 H1 i( ^( @
  32.          printf("bind..........$ {9 I( g2 f1 h/ J4 V& R
  33. ");
    % F" S5 w9 L; _/ F, J2 {% E- g# K
  34. % m. L+ A( y& O, Z1 U1 ?
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听) u( t+ V! x; A: G( K$ ~5 K- w5 w: F
  36.          {( Y) R1 T% Q" b8 s  S% V( V+ L
  37.                  perror("listen");
    # V( ]$ k' t* i; R$ r9 W
  38.                  return -1;
    2 J$ A- G' t) U! ~0 v% w5 ], f
  39.          }$ p& V5 ]9 d+ [" q- r: U
  40.          printf("listen............
    6 j2 w; W3 B7 i  R' S
  41. ");
    " ~+ `* S- E) q3 R
  42.          
    : j6 c% Y6 u1 D" r$ E# a  g* [, E
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    3 X( z" y5 S9 F, j# a
  44.         if (connfd < 0)
    / F0 q" j7 i$ N: u7 D
  45.         {, Q8 C( T* h2 @$ S
  46.                 perror("accept");
    - z9 {" t% B2 x" D4 M! q5 O7 q
  47.                 return -1;- }+ a) i. L% a( U2 P
  48.         }) s3 a& P0 o) d% X* d4 H* |& X, C. W
  49.         printf("accept..............4 I) d5 q# N6 [9 F2 _. q1 ]1 x
  50. ");
    8 N+ u" X) S) O" F9 L7 c* k
  51.         char buf[100];//定义一个数组用来存储接收到的数据; ~: M# \/ _- |' K  D5 I
  52.         int ret;! M8 W8 X1 X( t$ W# [: y! F
  53.         while (1)( ?$ ^# N3 s; r- o+ D
  54.         {# O; B- c( h+ |! J/ t
  55.                 memset(buf, 0, sizeof(buf));
    & V9 c! [! N7 d9 X* w2 ~1 y& S
  56.                 ret = read(connfd, buf, sizeof(buf));, i. z7 @" w" g; N2 M) u, U
  57.                 if (0 > ret). I4 }2 T+ g* E" V2 K  U
  58.                 {
    / e- ^. {  v) i4 x( N
  59.                         perror("read");
    % b  o; o9 H& i
  60.                         break;' P' }1 I- k4 b& n6 b
  61.                 }//执行while循环读取数据,当* H+ b- b' u# _
  62.                 else if (0 == ret)
    # I. q# t5 N. H* |5 y& ]6 |
  63.                 {
    ! b, w7 z/ K3 g, c& u. P
  64.                         printf("write close!' b$ S5 \/ Y. J: `" L) E& ?
  65. ");
    3 _7 T, V) Z0 ~2 z
  66.                         break;
    4 s" L7 b6 \1 G& u+ g
  67.                 }
    % C  g- ]% ~+ a: I
  68.                 printf("recv: ");
    : H8 s2 w* T) a" {; o2 N9 j$ m
  69.                 fputs(buf, stdout);//打印接收到的数据
    ' Q1 C) C% P; e1 Q% n2 `& A/ q3 j
  70.         }
    8 z* c! F( a& }6 e& T) i% c
  71.         close(sockfd);//关闭套接字
    0 v) T  x9 v5 v6 C
  72.         close(connfd);//断开连接
    3 p8 `; t) C7 f/ o
  73.         return 0;$ A8 T5 s6 j/ s8 o9 a& d, O$ D
  74. }
复制代码
& k1 r3 Y! N: J( |; l
8 j& M; C) _; n5 z6 g+ B3 X
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    . z8 i* ]1 C9 i
  2. #include <stdio.h>
    , Z% u( i/ E  E$ @/ X
  3. #include <string.h>
      ]% m; |' d# K8 z" m0 O7 g% E. c
  4. #include <stdlib.h>2 ~/ F3 v8 B& j$ \% S+ [
  5. #include <strings.h>$ S6 g0 e. f! S8 S5 `
  6. #include <sys/types.h>2 @! ]4 k" b9 h/ j" L
  7. #include <sys/socket.h>/ v$ q5 x7 ~% |0 [* {
  8. #include <netinet/in.h>& Q) R9 J0 k6 D1 y, k! }+ m4 q* f
  9. #include <arpa/inet.h>
    3 K6 W) k( O' z' T) F# ?9 w  l
  10. int main()
    7 e( v0 `' w2 g- ^: S  q2 i
  11. {# ]) E3 M& j& ]0 P
  12. int sockfd;
    ) p3 q9 s3 j9 Y* ?7 `; @
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))" ?4 _# {, q- ]* y/ P
  14.         {! Q3 @9 @( }: T: g& y1 k" d
  15.                 perror("socket");
    " v$ F$ X3 y' j
  16.                 return -1;, t( e: ?6 k9 a* c# `
  17.         }* J, D6 y0 k' O0 |! o
  18.         printf("socket...........
    # ^$ Y% F) e9 A3 p
  19. ");% Q1 |# a2 V1 D8 B1 U5 ]
  20.         
    5 m3 p; o$ y3 x
  21.         struct sockaddr_in srv_addr;
    * ]7 j+ }. n/ I, E  i
  22.         memset(&srv_addr, 0, sizeof(srv_addr));* c5 `0 W$ D" P0 e2 E% E% D' P
  23.         srv_addr.sin_family                 = AF_INET;
    / P0 Y) w. q$ ~% \  n' m6 K
  24.         srv_addr.sin_port                         = htons(8888);, O  B2 p- q) B
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");( m% }1 u9 \0 b, S& r* W
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    6 P" q/ i# \; b; k
  27.         {
    " G: ^. M7 u* w9 P& i
  28.                 perror("connect");
    4 p- B1 [/ V% s' n6 `, C3 @/ f
  29.                 return -1; //exit //pthread_exit
    ( x5 r1 O4 y+ ~3 U# i9 R8 l' Q
  30.         }" N7 E) o" G$ R: E/ l
  31.         printf("connect..............
    4 h1 s9 R, D! `4 Z3 z( N
  32. ");* v& q/ k( R' A3 [. Q+ i. [
  33.         char buf[100];
    ( o  Y- p) q1 w- V( K2 j# B
  34.         int ret;# P* H: n0 I- m' s( \: |( a# {: z
  35.         while (1)) u+ k/ i( O1 S+ T1 V
  36.         {0 v. M" K4 ]1 Y7 @" [) h$ Q
  37.                 printf("send: ");: |% }9 w' H, G$ f7 q4 J
  38.                 fgets(buf, sizeof(buf), stdin);
    ( W5 A5 U9 h, V; H+ E7 U) R
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ( W( k- U8 _7 |! s/ ?
  40.                 if (ret < 0)& p2 ]! q9 c$ n
  41.                 {% T" K2 S0 P" D: ^7 K9 _2 ?" w
  42.                         perror("write");
    4 U& J/ Y3 Z7 ?
  43.                         break;
    4 j4 l4 Y. r: E5 W3 }" m
  44.                 }
    : X3 {* ?4 a- C, H) [2 I
  45.                 if (strncmp(buf, "quit", 4) == 0)2 N. o# t: u- p# j
  46.                         break;7 F0 ]+ l" p" t) C# G5 j# i
  47.         }
    ; [2 X* D9 L* m# [" m! {3 K4 X2 l; p
  48.         close(sockfd);0 |+ F3 u1 w) f. @' [4 c' e9 q
  49.         return 0;8 }6 p% L. B* g
  50. }
复制代码
# T- ]/ V5 H' _$ \2 Z
4 Z6 J7 N7 F/ {' t3 t
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-4-19 16:59 , Processed in 0.155537 second(s), 22 queries .

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