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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

' z5 o, k" V; S0 `& E
) X$ }7 F3 X$ o) b
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ @6 {" z+ |, }$ ]; ~! q
4 P% _& Q( ^! F4 d
- |4 h* z. g  q, G3 f
TCP协议
$ p/ t) b; i4 Y$ X  U7 N3 iTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 E& V2 f" o! V+ f  D. k5 G; h
1 a, Y, z) h" i3 p
; q; @0 z: B& K) `
关键词:三次握手,可靠,基于字节流。2 ~" ?9 k3 t1 b8 o0 `  `8 i

, B1 D0 H5 h, ]- P

/ L3 K6 P$ M) m5 W$ w. V+ r可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
! ?$ E3 @- v" |3 l
: B" r- L) [, v( V6 UTCP服务器端和客户端的运行流程) f0 E  d; K! N0 m$ A

- l  }' C( L. a+ Q' Q" n
* O# N, ~1 y- c+ u" `; ~
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?  R0 w- D: u" b

# g8 @( t7 o4 m

! J" J! L& s$ n( n1.创建socket7 d; J3 `6 @! k. q2 U
socket是一个结构体,被创建在内核中
) A; O5 P! `8 J, W sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
% C7 O' e6 Y/ s2 @
8 l, u2 ?8 Y. o7 Z9 u
3 u) f2 g2 t. [* ^
2.调用bind函数
( D" a# \, Q! J! A 将socket和地址(包括ip、port)绑定。# M" g1 V7 I( M/ G0 a* [" ?
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序% `- G- _9 o$ ~# O6 h. Y" n
struct sockaddr_in myaddr; //地址结构体
8 e/ f" j6 v0 c- ^4 R2 j bind函数7 z( m& _4 i( n* ~- [
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
6 b6 j  |6 s& H- D9 {5 A; `4 q+ a7 L
! ]3 _/ C; K/ P" X, x

3 A  G% p- y( h3.listen监听,将接收到的客户端连接放入队列
. T2 `- |3 L( r$ C3 V5 K listen(sockfd,8) //第二个参数是队列长度
7 j2 D! m3 f! J
( e0 V2 \+ U4 W- J+ d

6 d, Y! k" d( F& X, a4.调用accept函数,从队列获取请求,返回socket描 述符
/ a% o, ^* p$ @4 K, G) o. \  如果无请求,将会阻塞,直到获得连接4 `- C! E% P: P" Q) ]
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
' @) x5 t; R3 t
  I. P8 l- J8 }1 P

$ k; f* @, w+ y: h! k: s' v: F5.调用read/write进行双向通信
3 o% E2 c/ v- \) x  ^% T8 |0 K( B0 n1 k% F# f9 s* H& t

& x' T7 [( V- \( y0 \9 ]6.关闭accept返回的socket
6 _( q6 M9 I. f  T3 r  close(scokfd);
) p3 ^; \: H% C( e2 Z) m5 W  ]" e* _
. I+ |) X0 X5 g# G8 l1 H( B
* ]9 C4 ?% ?* H) D
2 m( j" p" y8 T2 n0 \9 A7 ^
下面放出完整代码
/ a: m1 O: N( P% k$ j- ^  y' n* r2 H; D  p: M
  1. /*服务器*/$ k' ?3 G) ]6 |+ _0 ^+ j
  2. #include <stdio.h>5 I# c- T/ m; r) f( K- i# A  F
  3. #include <string.h>1 N. u5 v6 O& J7 p% E% `
  4. #include <stdlib.h>1 Q4 f6 s# q2 D% S4 T! D2 h
  5. #include <strings.h>* O% S7 e1 S  Z
  6. #include <sys/types.h>
    4 Q3 ?0 O% r8 X& r( m! {  a
  7. #include <sys/socket.h>: N5 {2 d; a5 x: H& k
  8. #include <arpa/inet.h>
      w6 l% N! z% h. N( ~: x
  9. #include <netinet/in.h>4 t; n5 n' i/ C  p: F
  10. int main(). F. A% g! ~% d% B4 S
  11. {0 s: r6 f( s# I2 N( u' c$ P
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字' l" {, P/ `, W! P# j
  13.         if (sockfd < 0)
    % L- ?+ {& j1 P4 D" h
  14.         {
    - l2 @9 U4 r$ w7 M1 a% n$ t) b/ n: u
  15.                 perror("socket");2 _4 t3 ]5 ^0 d
  16.                 return -1;
    + g& d+ Y& S7 a+ f8 E; U
  17.         } //创建失败的错误处理* h* [- W3 v0 U8 ~/ a0 o* Y
  18.          printf("socket..............
    & K; u# x5 C5 |; x2 o2 v
  19. "); //成功则打印“socket。。。。”
    3 Q" v6 e4 S1 O" I
  20.          
    , K1 e+ K' l+ f1 A" O$ E' B. Q
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体. \$ z' S, x; \( F- c
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)1 O8 I: w; C5 P5 W3 V; L5 i
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    & y# H: a8 d5 ^5 q3 E/ E5 N" H
  24.          myaddr.sin_port                 = htons(8888); //选择端口号4 @5 \) f! T1 k1 r) y
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址) e0 w; Y, h' C3 }/ `8 D

  26. . s& H6 C% s8 w0 z5 v* F
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    " Y. e) r, Q( F( I# w$ M! J: w* R
  28.          {
    0 k$ v, Z4 i& N- g
  29.                  perror("bind");
    # M! M- k! z; H4 o# s: ?1 @
  30.                  return -1;3 o8 T& u/ N! X! _
  31.          }
    ! x6 M; Y! D6 p6 u
  32.          printf("bind..........
    5 ?$ v5 Y2 i) {( v0 p
  33. ");
    2 h$ m* G. B! Y2 B, D" C9 w
  34. 0 R1 o8 c% R0 c% `) H$ [
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听; V% y9 Q  G- i2 @1 W# i
  36.          {, o5 e4 U: B( i' k8 R1 i8 [) Z! D
  37.                  perror("listen");6 E+ o: t1 H( z
  38.                  return -1;
    / \1 p/ u6 g) N' x
  39.          }/ j# Y8 h2 D1 Q+ B( b
  40.          printf("listen............$ k* X/ j0 ]0 {/ k! W8 c7 J6 n
  41. ");
    , [/ i( g6 ^6 m# P6 G9 X
  42.          % ^) j6 S7 R" g" v
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    8 P0 p9 W9 f& [2 c, l/ b. h
  44.         if (connfd < 0)# G4 W7 `# i$ f# t1 l
  45.         {
    1 M# N% p: p! G# e
  46.                 perror("accept");
    , ^0 V& ]+ l# C! n
  47.                 return -1;
    5 X  k* Z$ i& T) ^, X
  48.         }
    + S  w& |+ o$ x( C( {+ y
  49.         printf("accept..............
    & t3 s- y4 u! ]! `& k
  50. ");
    3 l; N7 @9 F5 w; V: N2 y
  51.         char buf[100];//定义一个数组用来存储接收到的数据7 h0 Q% k0 m- R- Q8 Y
  52.         int ret;. x: G8 [3 i* F
  53.         while (1), W$ E$ r. G# e& X
  54.         {
    9 J8 }% z2 ~. F& m- t* Z
  55.                 memset(buf, 0, sizeof(buf));
    ! Z1 s  R+ \8 k5 J- y% K
  56.                 ret = read(connfd, buf, sizeof(buf));
    6 J3 r) `4 D' Y1 E
  57.                 if (0 > ret)
    ( z2 c# @4 @% a7 H- |
  58.                 {) e2 |6 O0 {6 g* ]) ]6 a
  59.                         perror("read");
    * o& h2 K) n. e' i" ]
  60.                         break;( @. Z/ X4 w! N. B
  61.                 }//执行while循环读取数据,当
    ; W3 W% z! B$ R9 g, D9 W
  62.                 else if (0 == ret)3 v, l+ ^' _  M2 ]; ]
  63.                 {$ [$ a, R2 X, C' `; x
  64.                         printf("write close!% a# J& Z; B. x, n% m" N  p) u  j) o
  65. ");- g, p% @  v. [
  66.                         break;$ Y' j$ ]9 L" l( `; E
  67.                 }
    7 f! C8 _8 |( T9 S! ]
  68.                 printf("recv: ");
    ! o- J# o/ B! N- I+ G
  69.                 fputs(buf, stdout);//打印接收到的数据2 |) u4 y$ D  h8 R' B& _, u5 O
  70.         }5 p/ B. z9 C6 i' N! ], C* `8 y7 E, i
  71.         close(sockfd);//关闭套接字& @1 m& n7 I$ G  y9 I6 N
  72.         close(connfd);//断开连接1 p- [$ o" N( T! s! b5 P: Y% p' T
  73.         return 0;2 w2 p& }1 u( Z* V
  74. }
复制代码

7 ^. h( N" T5 a, d7 z0 s+ r: j* m/ p$ C4 T
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)* m3 V$ `, i% k9 K  l
  2. #include <stdio.h>
    2 R2 B9 ?& _# j4 Q% S4 s. p
  3. #include <string.h>
    # S# w9 [4 A1 f
  4. #include <stdlib.h>
    4 h: M/ i0 {# E  z" e
  5. #include <strings.h>7 v4 a1 K% v8 j! s: a8 H
  6. #include <sys/types.h>
    ! s  S7 d8 Q1 R0 x$ {
  7. #include <sys/socket.h>
    9 c4 m" c# p" W: a! H6 M; _
  8. #include <netinet/in.h>
    8 u% A* G$ D- t0 V, U" n; j' X4 n: G
  9. #include <arpa/inet.h>
    7 K1 [4 p. T/ l! n9 V1 W
  10. int main()
    . h# s* h9 d2 ]5 w/ \* o
  11. {
    8 k5 b! S1 |* F8 ]/ F
  12. int sockfd;, ~6 d+ Z% e4 |8 |  ^# h
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))): J' T7 `: Y& r. t, a) ]6 |- w* G& ~
  14.         {
    % ^8 U' A* @. H2 z
  15.                 perror("socket");' g- K  h/ A! y# H, L
  16.                 return -1;
    9 k7 l1 D, ?' I( I, B
  17.         }
    % I7 q5 W8 \* _0 w
  18.         printf("socket...........
    8 M. k/ c3 ^0 M8 s% @$ `; q7 i$ I
  19. ");8 s6 Y, R6 Q$ b% f- V2 X
  20.         1 x9 L5 B, b+ h& u" v/ T
  21.         struct sockaddr_in srv_addr;$ Q; C& }  k2 _7 O$ i
  22.         memset(&srv_addr, 0, sizeof(srv_addr));5 k* F) T0 I# n  m5 C( M; d- b
  23.         srv_addr.sin_family                 = AF_INET;6 [0 k# T3 `- S( r# q+ {
  24.         srv_addr.sin_port                         = htons(8888);
    5 h  L% Y. Y- Y6 y
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");2 ^% Q: ]6 r8 L0 e# k
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    . s# j! [& r$ |$ ^$ r
  27.         {* v0 c. Y+ `' T& K4 z! M" m/ M
  28.                 perror("connect");& h2 {4 Z. ]0 p7 ~8 e/ D1 A
  29.                 return -1; //exit //pthread_exit
    ! B2 h- y- N6 h7 [4 l# N: W1 q2 s, u
  30.         }
    ) y2 F  u. ]5 R5 `  @$ r
  31.         printf("connect..............
    ( H$ @' t" U0 [8 U: M; j" s% v% o& [
  32. ");
    " W7 z( u7 s# H! d: ]9 K7 V
  33.         char buf[100];
    4 n" x) o- v; N
  34.         int ret;1 O# e  E6 i& B, D
  35.         while (1)
    . z7 [$ k2 F- x( r3 s
  36.         {
    , u9 Y4 [7 [- ?, T+ T1 X3 E
  37.                 printf("send: ");
    & ]4 e" M3 b# q5 g
  38.                 fgets(buf, sizeof(buf), stdin);8 n- b! K5 V' C+ n1 U1 L* C4 a, T1 e
  39.                 ret = write(sockfd, buf, sizeof(buf));
    1 h. S- \8 o# I5 E7 m0 T+ C
  40.                 if (ret < 0)
    - o: k' [  g2 b/ I5 O/ X4 |! }
  41.                 {
    $ ?1 }: \  K! v) d) V, a  k4 B, e) B
  42.                         perror("write");! r% x8 u6 n; L
  43.                         break;
    7 Z7 L* D& j: z) |2 B; N2 D) o
  44.                 }: h# H+ B8 _( {  A  x
  45.                 if (strncmp(buf, "quit", 4) == 0)/ o- D" s3 B: |, y9 _
  46.                         break;
    1 w2 M7 w5 D: f; W4 n
  47.         }
    * r! k, e' W; \( Y1 M! \6 _  o
  48.         close(sockfd);
    9 V/ }% o/ [: W  {3 L* q' p
  49.         return 0;
    & r+ r: I3 I5 N# b2 S+ ^: F
  50. }
复制代码

) }  r/ J. F7 @5 J! z1 c! Y* _- y5 G2 @& z( C
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-19 22:31 , Processed in 0.062374 second(s), 23 queries .

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