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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

! [( F8 n+ Q3 U/ h# ~# Wsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ }+ [$ `$ S1 c6 Z: f7 {
5 K$ }/ V8 {1 M* P$ _# B7 |

, y/ I5 e# v# v5 q1 u* v) b. `9 g0 dTCP协议: t; w. o6 T- `
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
/ N# P6 w' @& i  w$ f" q! N* h$ ?4 C, a% |3 X6 L$ t

( ]6 {, x  w4 W; Y. g关键词:三次握手,可靠,基于字节流。
: o, `" _; D( K9 t% O6 N" Y# A$ D( z' ~1 a1 R! J, c7 |
7 |2 ~- A, t1 j& s% h
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
  I; ~% X/ }1 A
* C; J# S! j5 hTCP服务器端和客户端的运行流程. W5 F, N; E+ F* p3 T  R
  b/ r6 l# G  V. P. }

- o( [& G& [0 d如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
' Y3 L! h6 w4 {2 f
6 T; j! ^: t& Y
: B# O) U7 T/ H6 r- }2 c, k; S
1.创建socket
2 e8 }" G" O' m socket是一个结构体,被创建在内核中! ?) J+ x1 k& X1 W& `( e0 ?1 y1 T
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
  l* v0 e( l# u5 B
+ Q, s: u9 V; v! z. S( ]+ H+ O' A

0 N7 |1 O& h0 r) P  i/ i8 z& O2.调用bind函数0 e" ^: F+ t5 {' X1 w
将socket和地址(包括ip、port)绑定。
7 u4 X; P; b  ~" `0 z( Q. D- ` 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序# {) U* r3 |4 O7 u3 Z5 V% u9 D! P5 ^& _
struct sockaddr_in myaddr; //地址结构体
+ S) s, V. Z6 B- D8 d  ?) N6 Z bind函数# W6 J6 |. a* ~5 n" J+ G
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
& T8 `  T  `* j( r) v
; _& O3 C7 v# i- d7 c/ |

: ?- Z1 E( p+ `5 W  P* D" H3 q3.listen监听,将接收到的客户端连接放入队列3 n2 J* G( W/ M: i
listen(sockfd,8) //第二个参数是队列长度8 K4 M: K+ m; e: _/ T7 Z7 X; M
7 y. J# Q& E- E; m3 b* r
/ M0 \* b' R0 B0 K% c
4.调用accept函数,从队列获取请求,返回socket描 述符
5 \# h0 |4 ^/ X  如果无请求,将会阻塞,直到获得连接
& {$ ?: U/ ^+ G( A: M  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数% m* J9 d4 l9 h7 E+ ~7 g

$ C# D# n. i4 y  [

5 L8 i# h" p: W/ h5.调用read/write进行双向通信( q6 n- `. u( o+ H+ P. f- P) h# {
  [8 J) Z2 l- l; h# T" H. N- M& |
7 c% G: q  ^8 [
6.关闭accept返回的socket
  E. @( M$ f; S6 R0 Q% q  close(scokfd);
  I( q) H& s6 o  f$ N# z  R8 A, a0 \% G* |7 Y
! X8 h0 K  j" e4 h- j
3 h2 I% e, {- A" u) t5 g
( W4 |& I0 L& i( @
下面放出完整代码' Q) m$ {- T0 s. |, }* D
, V# M) m2 W8 M9 [
  1. /*服务器*/
    ( i' s( }9 R0 r2 B% D6 d( V4 @, j5 g
  2. #include <stdio.h>
    9 I( S; V! C7 ~0 E
  3. #include <string.h>8 {  g; q! s2 D- ~. g
  4. #include <stdlib.h>& ]. f8 e/ I7 e) ^% `
  5. #include <strings.h>
    % D# I' n: u# q& _  @  p
  6. #include <sys/types.h>$ j& S- q8 U  g6 _4 l; Y. _# r& F
  7. #include <sys/socket.h>
    2 u3 o' V# x; J8 o" ?0 }; c  s' L! m$ {, V
  8. #include <arpa/inet.h>
    # G* L. r! t; ~  O/ g0 S+ {
  9. #include <netinet/in.h>( K- j- b# S7 A6 p( I# i/ T: t6 N5 p
  10. int main()' [. E1 n* L' q" J
  11. {
    8 ]1 }  m2 h& K- z, f" Y
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    0 S. a1 p0 w* z. s
  13.         if (sockfd < 0)
    & T$ t% F5 C; i% {. _: }' T
  14.         {* ]; T+ N1 u# V! u5 e
  15.                 perror("socket");  E* h( o* K1 d8 x8 Y
  16.                 return -1;
    - |3 s' _) a$ p; U. k
  17.         } //创建失败的错误处理: j8 y% W! m3 o$ W1 ~/ G
  18.          printf("socket..............
    / t1 i$ q8 N. i( B4 H  A( T
  19. "); //成功则打印“socket。。。。”
    6 J  h9 C9 }0 q3 X
  20.          
    6 L; N/ q) Z5 @7 G0 e
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体% M0 R% P; h) K5 ?& u
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)% D2 O5 P# b9 ]) i
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ( `% h: G& K& K( D- f3 D
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    # x$ X4 r( T) O! t, }; |5 @
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    - q* C2 |8 j1 y* e

  26. 6 V$ Y9 r( \& ]1 Q0 _
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字; v- n0 i: m. z0 k0 E1 s. X0 K
  28.          {; e0 y0 n# L1 n' @- p( A  ~; [8 t
  29.                  perror("bind");
    " J  d- I$ r- l4 W$ Y
  30.                  return -1;  I* h1 S" K, J/ ~, `* ]; n
  31.          }' I& W8 S$ p% E5 D; c. I! Z
  32.          printf("bind..........4 D* \7 Z: O% p$ ?$ U# s
  33. ");4 q  L- x: w+ r

  34. - Q; F/ |2 V2 X
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 f8 ^4 p  C( D& z1 g
  36.          {
    : j3 I5 S1 l3 W  Y4 L' ^. a
  37.                  perror("listen");5 X) \, ~. [8 X5 c! _% ]6 s
  38.                  return -1;& k0 I3 ~* A1 h0 M2 d5 m
  39.          }
    ; U, s! Z0 n& a* P2 \% I$ l3 A
  40.          printf("listen............
    + e. O' v. V: {7 I% H
  41. ");7 ]2 k" g6 P! u
  42.          
    2 c% H# o3 T6 Q$ d4 d3 l7 n
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ! L/ p3 I$ s( W; z8 @7 b
  44.         if (connfd < 0)
    $ l* W( M* G- v8 X% s
  45.         {) o; X+ E+ w5 g; r7 a# l
  46.                 perror("accept");, ?- q, s* \/ Z
  47.                 return -1;
    6 q' ^6 c3 C7 Y' q, C
  48.         }: X7 W) n5 B6 R$ ~+ u+ X3 }
  49.         printf("accept..............6 Y& y( O0 Y: R
  50. ");# A' b3 Z$ q& ~2 G7 r
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    2 ^1 ^6 o( Q' U
  52.         int ret;1 N8 X% X7 V" W& C. \2 j
  53.         while (1)
    9 i# Q- w& K7 p
  54.         {& s) |: a: J. F; D0 R/ G
  55.                 memset(buf, 0, sizeof(buf));
    0 X( b( X: L- {7 h, \7 n' _3 i3 p
  56.                 ret = read(connfd, buf, sizeof(buf));* h1 n6 G: ~5 v5 R  p# Z# k
  57.                 if (0 > ret)& O0 o4 s  |1 f1 O1 {4 T
  58.                 {
    6 l* `" q, |4 S( H, U' k
  59.                         perror("read");
    7 W" X! K- V: J
  60.                         break;* I" @* p8 K3 |$ q8 ^4 j7 \) @5 [
  61.                 }//执行while循环读取数据,当' S8 j  q' p% x+ _
  62.                 else if (0 == ret)
    9 J& P0 F3 V* w/ b
  63.                 {
    9 S$ Z! q# W5 B2 B6 T
  64.                         printf("write close!
    1 A- r4 m  t$ ~+ ]  i
  65. ");
    7 A1 C/ m8 c! d+ m& K
  66.                         break;& X" x, u# V" a! M* H2 y8 L
  67.                 }' [2 a3 e. T6 h( c7 }" _/ A( j
  68.                 printf("recv: ");: S. P) \3 B' g1 _$ D" y
  69.                 fputs(buf, stdout);//打印接收到的数据
    1 D! _' ?4 j$ K, Y/ K/ s# X& x
  70.         }5 h0 r8 Y" O$ ?2 M6 l3 E1 l
  71.         close(sockfd);//关闭套接字" w( G/ o) m8 E- d
  72.         close(connfd);//断开连接
    ; I. B% Q0 @- _6 F, O
  73.         return 0;+ `- a* v% W2 a9 z& P/ a% T
  74. }
复制代码
% R0 e) a- k0 F0 {% Z8 ~

: S9 p2 j: [! Y" [- r
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    + n: r9 R4 z7 G% G9 r# F% |# c
  2. #include <stdio.h>6 }3 p6 g9 o; q3 k2 \) e* r# h, L
  3. #include <string.h>
    / Z+ f7 H+ p' }1 t' z
  4. #include <stdlib.h>  w9 p0 O& o+ P1 E. E
  5. #include <strings.h>" T  O$ m  z4 c! R0 ^+ y
  6. #include <sys/types.h>- J6 z' o: F& x4 l4 K
  7. #include <sys/socket.h>
    ! y; @  x. F1 |: z. m7 \3 w$ W
  8. #include <netinet/in.h>  S, l: N/ }: y6 E6 S7 H" B
  9. #include <arpa/inet.h>
    * j3 z: \$ l5 ]' V
  10. int main()
    # c  m" q2 G! w* X
  11. {, }% R7 i( I, @3 R" r* i  y: r
  12. int sockfd;
    9 V/ m  @& W/ M( [$ \! w. s
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))). \1 e" G9 a0 D1 M. Z
  14.         {
    $ R& d3 p4 _- I( n
  15.                 perror("socket");
    + r' _9 Z% h7 R) N6 q: Y( d
  16.                 return -1;9 Q" P  c8 c5 d3 ^( Z9 l
  17.         }
    2 K0 I* B# T7 Q- \7 I3 x( p
  18.         printf("socket...........9 \# C6 v* W2 N: i1 A
  19. ");
    . u  r+ {) b  ?8 h# k# n
  20.         
    ( W8 {" |6 C/ I( _% |
  21.         struct sockaddr_in srv_addr;
    ; F; n' X( V+ J- h' Y3 g4 R: C
  22.         memset(&srv_addr, 0, sizeof(srv_addr));5 G+ h! ^. i, `! N, d+ g1 b% z9 }
  23.         srv_addr.sin_family                 = AF_INET;: P2 f6 }, s( d7 A
  24.         srv_addr.sin_port                         = htons(8888);* L' o. ?: G: Z, u0 J4 f  w. G
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");' v3 ]  s) Z9 C8 b9 M6 h
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))! g' E4 \7 S6 p: Y; \
  27.         {: B* R5 H: r5 e$ ^) D
  28.                 perror("connect");
    2 q. _( n! A$ e) Y  ]
  29.                 return -1; //exit //pthread_exit  v  C8 B0 Z; X$ }6 F
  30.         }
    0 V7 V# y* m6 b% M
  31.         printf("connect..............! k: E8 F& s9 ~7 x4 z+ g
  32. ");2 D$ D* i: `/ Y1 A5 _! C" K. a* q9 ~
  33.         char buf[100];8 b) {" E3 U5 f1 O4 ]
  34.         int ret;
    4 \# `0 s8 }7 B4 Y: W1 I- q
  35.         while (1)
    * i* v7 F; H; ]7 c6 N
  36.         {
    - w' f2 b, {3 u- U4 m
  37.                 printf("send: ");  t: X- |2 P2 p! L4 l: @5 U: D
  38.                 fgets(buf, sizeof(buf), stdin);
    1 \& o$ p- q* y3 W! R1 `# w& r
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ; @9 X* \' f, d
  40.                 if (ret < 0)7 n5 a! G7 H$ K0 r' S- ^0 z# \) ]  K
  41.                 {
    " l6 U% ?8 g, F. S( x: S* w
  42.                         perror("write");9 D* l* `  H& }" e, N- \! j3 a
  43.                         break;
    3 r; D( j# D5 S& f( O0 [
  44.                 }, L6 @4 _1 l1 u9 [# G2 }) u& ?; s
  45.                 if (strncmp(buf, "quit", 4) == 0)
    + W& R9 p$ y8 s4 Q
  46.                         break;3 c: t$ L. X$ [$ C; u' T- Y
  47.         }' N6 R. w: b$ t
  48.         close(sockfd);
    % l! T8 [$ q+ r
  49.         return 0;# c2 m& E  w* g, z3 @
  50. }
复制代码

. u. d  E- H; G( B* D4 x8 B/ x/ [) l. ^( {5 p3 L" g% R
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-16 18:12 , Processed in 0.061657 second(s), 23 queries .

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