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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

! K( d3 ~8 ?" X' ysocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
4 T- S3 r8 X% Q0 t4 ^4 @( m9 N- G" V: b2 n/ g) G

, C2 v8 J! a8 N- _6 {TCP协议6 X0 N" Q3 M! [" m9 C$ I& b
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
9 s! e4 e4 D' m' d& ?& o) \+ W$ y
; h) I7 r7 f% l4 H& o$ X/ p

0 C4 O5 V. C- w8 j7 p关键词:三次握手,可靠,基于字节流。. m. X$ h5 D- r5 Z; Z/ n' E

8 @! M: f! d9 D' d/ G: R1 n; @

7 a  j+ Y& W. r9 r% I$ O- j可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
! I+ a) c& P( T  E! K1 ~1 H6 h& X
/ T) h; C/ f1 j$ D, xTCP服务器端和客户端的运行流程$ ]7 g. `, k5 ]0 m4 \: \
5 J- v3 q' W: y( O- g
! |/ M" @8 T  X8 z, H9 A
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?+ W3 N7 \1 g3 v3 N( P6 N

& ?! v% w$ Q; `# P

' v; B1 E6 j* ^; {3 J1.创建socket
" S; }6 F# E: B" J; `8 w5 b' j socket是一个结构体,被创建在内核中
4 m: ]& K  a7 n6 k' y. L# O sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
2 C7 D" f9 Y6 O
' M6 Y+ T( B3 t* g9 B0 `
8 p7 \. L# q" l, ~2 f; E) M- R# l
2.调用bind函数
/ W1 ^7 C9 j% Y* a 将socket和地址(包括ip、port)绑定。
; s/ P9 s2 I" @4 K 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序! I* d2 p" v: n* {8 S: [
struct sockaddr_in myaddr; //地址结构体& R, A; t1 i9 @
bind函数
. v# F7 K( k( ]: T$ t; Q bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
" I( T2 d/ b6 W! Y* @' \, O0 Q, ]/ H3 K* m$ `

( K9 d: [; x! D# [3.listen监听,将接收到的客户端连接放入队列' }/ [) O$ K% T+ L  O* W3 B# z
listen(sockfd,8) //第二个参数是队列长度7 D) |4 t& |% _; i& m8 R
* Z( y  A( I8 ]* e& L# q( }# r, c

4 E: P# H2 a% V2 @4.调用accept函数,从队列获取请求,返回socket描 述符/ s& V% G5 X% t: w
  如果无请求,将会阻塞,直到获得连接5 G2 Z) m  g6 y; t( s4 [
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数# S- I' e3 V- J
& {. f; x0 ]- @( l0 {/ v# ]- X
& y6 x* V$ g) h
5.调用read/write进行双向通信
* ^2 Z9 @' t6 a  ?: ^+ w$ t: v1 N5 [+ [2 w) y2 R
. e( }: {$ x) e) `
6.关闭accept返回的socket; e, _8 c8 p1 ]- O
  close(scokfd);8 t) N& G! ]: Z* P& {* q

4 l9 ]0 S  @# G9 y
: ?9 _0 P$ i5 x' h; o2 [

# F: V1 R) g3 p; O- K

: ]; H( W3 ^# u+ g- h下面放出完整代码# c& {3 x0 V/ ^! q, _
, Y. I$ P! p( ~. X
  1. /*服务器*/+ l  k2 x! V# G; U) o  V+ M
  2. #include <stdio.h>
    4 N5 S3 M7 A/ m8 ]4 a" N* d
  3. #include <string.h>1 W7 ?& i3 X% ?  p; I3 T. H! ]
  4. #include <stdlib.h>
    / x5 g, G8 O: {6 b
  5. #include <strings.h>
    , \- b1 e* g8 J
  6. #include <sys/types.h>
    , O$ E$ l% D% g: v# X: S8 |
  7. #include <sys/socket.h>: l4 i, n- K  ?% s* P3 r
  8. #include <arpa/inet.h>2 A- H  I+ ?" ?; K, F$ `2 a
  9. #include <netinet/in.h>4 l! w  B; ^+ B5 k3 e- H2 U7 R
  10. int main()- `* t  L, P2 a1 {. M! T) P- N$ _
  11. {
    5 o7 ]* M- V8 W
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字( l4 }( t7 A: x+ t6 R( F  J4 q' j
  13.         if (sockfd < 0)
    9 U3 r  o/ x0 f7 e! t, A% a
  14.         {4 m. ^0 c1 f! |! t
  15.                 perror("socket");; V: W2 d9 U3 P: B4 C8 [- F5 O
  16.                 return -1;
    5 s$ l) h, F$ l
  17.         } //创建失败的错误处理
    / n( P8 c: P% k# [3 y
  18.          printf("socket..............
    , Y+ l1 }  z9 E+ o9 ~( E9 `' J
  19. "); //成功则打印“socket。。。。”  L* m, }1 _. J  g! O$ S% n1 B; k
  20.          
    3 ~- e/ a. k- j" S7 Y
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    : ~. j5 g! l' D# w! d5 @4 t
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    $ t! i( y: X  J8 T7 a8 G
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    # N/ _/ w; a4 n5 G- @- A  }
  24.          myaddr.sin_port                 = htons(8888); //选择端口号" H9 p1 M$ p+ X" ]: _7 X
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址+ K1 V# D- \, o9 k
  26. - E" R  h3 E# a
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字  [  P: s0 p. @4 t
  28.          {
    1 x* Q- R7 L2 ^
  29.                  perror("bind");& [0 T9 i, C, F' G  }) O! C& y$ a  Y
  30.                  return -1;  h" R5 C) S# M3 E: g( T
  31.          }3 v; n- f& A* ^$ F- t
  32.          printf("bind..........( ]5 U& Q9 G( N
  33. ");
    3 z, a3 ?5 {& C7 y. e* R

  34. 1 K$ J, m) m) P& x0 O
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听! R& L; `5 O# `! {9 e( c$ t
  36.          {  ?0 X5 G8 Y$ P0 m4 p
  37.                  perror("listen");
    4 {( g0 |4 {3 l! I
  38.                  return -1;, i  ]' g! o9 _7 g: u  B# r
  39.          }; L% l, ?# x* C6 B. y- `1 P
  40.          printf("listen............
    2 H. h6 H4 |# w$ u- j; l" _$ U
  41. ");
    3 G% M( g* V  B
  42.          + G) Z3 _5 d+ J3 B* D- Q
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求+ e( n) a3 j6 L4 B3 S8 p& l
  44.         if (connfd < 0)
    6 b! j# w' n! V0 {( u7 N
  45.         {
    - U. X3 H3 z, @( b1 Y, U! E
  46.                 perror("accept");; w6 X  P* Y9 q$ C- Z4 T3 P
  47.                 return -1;% R3 p  i% c* P8 x* E& n
  48.         }3 z2 ]4 a3 n. t5 \
  49.         printf("accept..............; l8 Y6 [4 g+ n0 i& r) Y
  50. ");
    ) d, ~  J" b, x7 U& t0 }+ i( T
  51.         char buf[100];//定义一个数组用来存储接收到的数据
      Y; W* A2 r2 D- \9 I
  52.         int ret;( M, `7 _1 [$ j" M! o% X
  53.         while (1); K9 z8 p7 F$ Z0 Y
  54.         {
    7 v5 u1 f8 A- y" B3 U! h6 Z, l
  55.                 memset(buf, 0, sizeof(buf));0 N1 |5 M- l( Y5 F5 k
  56.                 ret = read(connfd, buf, sizeof(buf));+ J5 j+ h4 H* }) \% d( u0 S
  57.                 if (0 > ret)
    0 b+ }, W: z" K1 n
  58.                 {5 y" t3 \. r' I$ L' ~7 I: u
  59.                         perror("read");; {( U3 T1 U8 V  i2 b
  60.                         break;
    1 R' }" u+ D1 ~# h7 s" N. Y+ S
  61.                 }//执行while循环读取数据,当% C$ I  e/ L+ ^% G
  62.                 else if (0 == ret)
    6 h$ l+ ?  d9 V/ G. g
  63.                 {/ j( ]% a  Z+ f% T' H9 U5 C
  64.                         printf("write close!
    ; l0 q& \, M2 z  i  C
  65. ");  R" O" y- j. f2 w; z
  66.                         break;
    $ F0 _* E$ a6 t. J2 ]! f
  67.                 }; a6 B# F$ v$ b$ L( Z- ?& ~! B
  68.                 printf("recv: ");
    ! r9 u% W, M6 j" H! E* u. S9 ]
  69.                 fputs(buf, stdout);//打印接收到的数据
    . P) H; t* y3 x2 _! v" M
  70.         }0 m% \& L# p* x, p) \! H7 c, i; ?6 l
  71.         close(sockfd);//关闭套接字) l6 ]+ @2 s$ [% [! I* x# Z0 o
  72.         close(connfd);//断开连接* Y( h7 t. f# ?5 @( t/ p1 E" O
  73.         return 0;) P4 w2 D* u5 ]; m! g
  74. }
复制代码
' E  \2 ]9 O- i- y0 L

% [' F- n' c4 F5 g/ A
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)$ C' C1 A' D) C0 L5 G
  2. #include <stdio.h>! P6 J- ~3 K' M5 X
  3. #include <string.h>
    . K  Z" S$ S8 E% W
  4. #include <stdlib.h>/ q8 |( ^' f& p6 ]9 B3 M
  5. #include <strings.h>
    9 ?. C( Y. W; X, `' y* c* `' s
  6. #include <sys/types.h>3 @2 h! B. V3 e7 ^8 N* g* s
  7. #include <sys/socket.h>
    5 I% N9 X7 A8 v- [5 u
  8. #include <netinet/in.h>
    " s: @* `2 T9 F5 `
  9. #include <arpa/inet.h>. c0 E; `* D, e+ I' F
  10. int main()
    0 A# G+ f( c% v+ U
  11. {
    $ w) k" C! y: g0 w; Q6 c3 f
  12. int sockfd;# j* a5 b" a- L0 l" G
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))" \: n  K& O: }' o8 Y
  14.         {
    0 R0 f+ e2 s5 Q' P$ z3 k- X! R
  15.                 perror("socket");! J. r: ]5 i1 v" m/ f! C& ^' N
  16.                 return -1;
    7 {8 b% b' m; h3 v) C5 ]3 C
  17.         }
    3 @$ ^8 S+ e3 _% c: H! v
  18.         printf("socket...........0 h9 M/ ^( i, m( s
  19. ");
    # [! _" F# S8 N' r: o. J9 X: m
  20.         : N+ R' Y! G: l# g/ o: g& Z
  21.         struct sockaddr_in srv_addr;
    / N/ P- }  i5 [$ ?* L: n
  22.         memset(&srv_addr, 0, sizeof(srv_addr));/ P' O$ u" [3 n
  23.         srv_addr.sin_family                 = AF_INET;7 ~7 z* W' g9 G* Q
  24.         srv_addr.sin_port                         = htons(8888);7 e2 S, g) Q. z' J9 }( E
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");. D/ ]. v, Y% z( h
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))8 S+ w+ |+ L4 ^) o
  27.         {
      R3 Z3 ?8 E; \9 Z
  28.                 perror("connect");
    & x  t( X0 x$ z
  29.                 return -1; //exit //pthread_exit
    , R8 r' }0 J: f3 v
  30.         }
    * O0 _) D5 o& S: P" h
  31.         printf("connect............... _0 A0 n1 @/ ?7 ~2 X* C
  32. ");
    & t1 B3 ~3 p" o6 T) L, |8 ?3 f* R
  33.         char buf[100];
    0 m8 l* c) {! j# N7 `7 R$ z0 W4 i
  34.         int ret;
    6 D. l0 M$ q  L4 n
  35.         while (1)& M7 L4 g  _5 b" F* x8 h' b
  36.         {" j6 G5 W- c1 Z/ u( Y$ K
  37.                 printf("send: ");
    7 y: K' X6 X8 ^) Y! \2 ?+ U) \* Z
  38.                 fgets(buf, sizeof(buf), stdin);
    ; C7 o3 G; F  u2 t4 U: w
  39.                 ret = write(sockfd, buf, sizeof(buf));
    * W1 m/ _) N/ v$ G
  40.                 if (ret < 0)
    8 G, r% }( Q5 q5 {* O
  41.                 {
    # O/ f7 v- g) C8 \$ t6 x
  42.                         perror("write");
    " X) r7 M) |3 b( H" a1 u4 {8 s
  43.                         break;( |( q4 n1 _% V8 {( S# G/ \5 l9 Z
  44.                 }2 {( l  H+ R/ Z% m
  45.                 if (strncmp(buf, "quit", 4) == 0)4 Q: @; {6 m( Q/ S
  46.                         break;- m6 O, P& ^( h. d
  47.         }
    % [- \/ b# p0 }6 Z
  48.         close(sockfd);+ s4 u9 k6 |" J! w2 |* z
  49.         return 0;
    9 @5 z. i9 S% ]; s& M- m  ]
  50. }
复制代码

8 [$ a- |  p5 q* S0 ]6 V* t& g; E& G8 z$ _  y( s7 P
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 03:15 , Processed in 0.057630 second(s), 25 queries .

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