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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
4 d7 ^3 I* T# U8 w" |1 _. z& K6 |/ F" a, L5 `; I/ i

7 ]. u) {; H# B3 E) G( }, Q% `6 rsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
# _8 d( v8 P$ M7 `6 ~5 {7 F, r' ~# v/ V4 T

- a. N: j$ t! t* Y# Z$ eTCP协议% d0 \/ a! @# w, k4 L. o& ?7 [
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
/ B3 J' y" D3 k/ j' g- S* V, H! T+ V6 m

# h$ F' w2 g5 B' ]2 s/ n& D关键词:三次握手,可靠,基于字节流。
, B) C8 ]# ]: N' B% f3 O% v( F
: g, T) ^; l5 T. O: h1 Z

' j3 q0 B6 ^( _4 J& b* ^可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。4 l  p, \, w+ c% _  f% H: R2 n
1 ~& C" p+ @5 T, f8 i% C' A8 ?
TCP服务器端和客户端的运行流程2 V1 \. F5 Y8 S5 L$ R! }- k

- }- @3 p6 ~  l, b
6 ~8 c! u1 w8 e' [5 f) B. Z7 l" P% A& {
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?% r# F7 }$ ?" \7 P# O; ]/ N- C

) v, W. w# N9 S3 ~  Z9 x0 [( h6 @

! L" k+ R% ~7 t* F" F& Y$ n/ J1.创建socket
+ K& Y- z0 |  W  L  ^9 d) ^ socket是一个结构体,被创建在内核中' e& x0 s* O( p/ U! f
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议; X& k- \2 Y1 ]( L* w( v" |
5 S. c8 h+ g6 c# K0 a1 M, f2 n: p9 E. H
: b* u; p5 e7 n0 h& h
2.调用bind函数" A* _9 M2 U& m: k4 M! G8 P) H5 [" m
将socket和地址(包括ip、port)绑定。/ |1 F/ ~: Q$ t/ N  J5 t; h) h
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
* c' [8 J8 B5 o; M, \- k struct sockaddr_in myaddr; //地址结构体2 Z4 U' E8 ?7 a; g* y
bind函数% f2 t8 `- T8 U% q3 p, p
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))  H( s$ u2 P- Z, H. ~3 j. v
5 F: [/ I- C$ |

; _, f# x5 E  [! J: C  l3.listen监听,将接收到的客户端连接放入队列% ]) _- R7 Y* f# I; X
listen(sockfd,8) //第二个参数是队列长度
3 {& a4 a( v6 S- G  B
1 K' z8 m4 d1 ?+ }7 T+ X
7 b+ e) G5 a5 H3 T
4.调用accept函数,从队列获取请求,返回socket描 述符9 a: o8 s& q3 E9 W
  如果无请求,将会阻塞,直到获得连接
3 q- R# d8 N0 W2 ^- C6 n& {. n  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" ?+ ^# s: m( `! q0 h  P1 [- n9 P& A; t7 g  [
4 J: R6 n. s) f: a% u! F* K
5.调用read/write进行双向通信( v/ j+ V4 Q. X/ ?* x3 b. e) S* ~, @

" m5 H0 q7 [6 m' M9 p+ r
. D/ J( u5 l; c7 w9 K
6.关闭accept返回的socket
3 a% t3 x; P/ @: J! H  close(scokfd);
6 x$ j, O: h" h2 T+ E' S  E
9 q* E- J( q/ b: i$ x

& P/ A" N* b8 A
6 q* F- m! t7 M% H

% B2 f* s- X5 J下面放出完整代码! y. T7 k7 v1 I2 w* b' r
- ]6 m: p5 X) \- v  u( }
  1. /*服务器*/
    ) ~0 O% P1 Y  Q# a
  2. #include <stdio.h>0 p! r0 S% E$ |: s$ E
  3. #include <string.h>
    8 R. ]. P* z- ?
  4. #include <stdlib.h>
    + k! O9 |( _! e0 ]2 G  U& ]: T6 M
  5. #include <strings.h>
    + [8 d  V% P# t$ m; x
  6. #include <sys/types.h>8 E" ?0 t$ N; d1 h* d. s. b
  7. #include <sys/socket.h>) M& \* [% ]$ E, j
  8. #include <arpa/inet.h>
    7 _1 K9 T5 w. N7 z
  9. #include <netinet/in.h>+ i9 w- @7 h) C' G, y; ]
  10. int main()
    1 d* l3 l" h% b3 X" R9 Q; l# g
  11. {4 \9 v# B% y" f+ Z$ O" F( C
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- O3 O8 p7 s* d9 P+ x  B9 ]
  13.         if (sockfd < 0)
    5 G+ f  F) Z: T! l6 G
  14.         {
    0 y4 F6 d# D" w+ ?9 S' d2 k
  15.                 perror("socket");
    5 R$ m% c. P/ m! M* P$ [
  16.                 return -1;/ p" k  b$ q  W. l# d
  17.         } //创建失败的错误处理! P) }5 W0 @9 N5 c, I. N9 f
  18.          printf("socket..............
    7 G8 E+ M* g9 ^4 k# r3 G
  19. "); //成功则打印“socket。。。。”
    9 m: a+ s; n% P% \6 q
  20.          
    3 J* z) f. K8 q* \7 Y4 o
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    2 Y2 t0 e( x8 @! E! ]
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)$ T/ x7 V; u0 ^, |) }
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型# a3 G/ H$ L. u; S6 I; Y- I$ w
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    2 b' a+ c# E$ [- ?% i7 d: N8 E
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    * L$ v4 e8 a$ W" [0 p2 b# e
  26. - S3 i9 M& X8 {0 ?
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字' a6 f! {! O- {. P
  28.          {" T" h$ K- v0 b3 X
  29.                  perror("bind");
    ) X* w: z# a$ x# a
  30.                  return -1;
    & l5 Q: W, j) H5 ]/ A! ^! ]0 w
  31.          }- r4 U$ M& N7 x1 Y* {2 w$ z
  32.          printf("bind.........." ]6 q1 [0 o, @* e
  33. ");; ~' s. h% ?1 [6 T+ R4 A" K8 Y
  34. # @3 l3 X2 V: ]# F  o/ c9 d
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    * M6 b! u! X; q* G
  36.          {9 j, D5 R% {# D- K
  37.                  perror("listen");4 d* z# J& ?, m( M; O' v5 z1 M- U
  38.                  return -1;
    & v. J0 Z* x, E4 F# u
  39.          }6 s" k. v9 R  R: g$ ]
  40.          printf("listen............
    6 C+ C& P" [& {2 l+ b+ \& a( h
  41. ");1 t9 H& W( Q* ^- Y  T
  42.            @, H. E$ m3 o, ^; |
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求) W+ c' x! W4 g* X/ T9 m4 @
  44.         if (connfd < 0)  o- P4 v7 K! y8 |+ U" A2 W
  45.         {
    ( D' k, G- c9 m, e
  46.                 perror("accept");* C0 U& Z, b. Z6 g
  47.                 return -1;
    : M% O7 q) L) r- L2 I
  48.         }& L, E+ V* R' V( _& K5 n
  49.         printf("accept..............
    ( v% H/ p/ Q. H& Y% K
  50. ");; P- ?5 O- z, j
  51.         char buf[100];//定义一个数组用来存储接收到的数据) o/ D. e6 W% P" u
  52.         int ret;/ E/ H" m* k+ D% i
  53.         while (1)
    ' |8 }: F, r$ A' H
  54.         {, c' V' {- H' q+ `
  55.                 memset(buf, 0, sizeof(buf));$ r4 r3 o# B$ J0 J# C$ R
  56.                 ret = read(connfd, buf, sizeof(buf));
    2 k# X: z: N) s0 H' B
  57.                 if (0 > ret): `1 ?4 g. E% v8 T
  58.                 {
    5 S3 [. f. f% [2 ~2 a9 H
  59.                         perror("read");4 {" x& X- [0 u6 P; g8 P
  60.                         break;
    ( Z9 ]$ I$ u" ?( o; _) q$ u
  61.                 }//执行while循环读取数据,当1 [4 o8 _3 k" C3 Z
  62.                 else if (0 == ret)
    ( @! B) G8 Z0 R7 @$ L# B. O
  63.                 {
    # o5 q& \$ Z0 c* |
  64.                         printf("write close!; n* R9 L/ u! K. ]+ n
  65. ");
    . c$ C) |1 a& s
  66.                         break;9 E, `' J3 v  S) c4 [2 i
  67.                 }
    0 ^" a0 o0 Y4 ]+ R
  68.                 printf("recv: ");
    % C0 Y* q; s. k. {
  69.                 fputs(buf, stdout);//打印接收到的数据& z; S" X1 J* x# D) U8 Z4 s; R1 J1 \
  70.         }
    " Y6 x8 n! {4 S! C: t. _
  71.         close(sockfd);//关闭套接字  `" I) k! Y/ N: g! S, x2 K- `; v
  72.         close(connfd);//断开连接- N! x' ^3 H7 J! F0 {6 p
  73.         return 0;
    8 u3 ?1 J! @/ n; m4 p! v2 P
  74. }
复制代码

( ]# e9 {& @4 J* Z! U4 f# d+ y6 n# s( `
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    1 H' {% L# {& J
  2. #include <stdio.h>
    7 n- j" {, o/ P/ D* v) c
  3. #include <string.h>/ b' B" f# Z5 V9 D+ S
  4. #include <stdlib.h>
    2 l) C8 l# _" d0 ]; A4 L; w, Q/ Y
  5. #include <strings.h>3 g6 W1 [# }1 Z* f  I
  6. #include <sys/types.h>
      L' `6 q$ R0 ?# S/ O, l
  7. #include <sys/socket.h>
    + c+ r0 H' b# @) }+ L5 x( S0 V
  8. #include <netinet/in.h>+ g& k; O* R6 e6 S" ]+ B
  9. #include <arpa/inet.h>+ _: o1 J1 t6 N0 g1 H
  10. int main()
    ( |1 \" [9 B! N: t: z8 G
  11. {+ N" S* S* g3 E* N: w6 p
  12. int sockfd;  R' ^5 j+ |2 g" F9 f
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    4 X9 b5 g; [, P, }
  14.         {
    : |0 |( h. Y0 S' P- l9 R
  15.                 perror("socket");9 y' q/ h9 ?  p% X- m4 v
  16.                 return -1;
    & v/ L1 @, _% N
  17.         }* j5 h# |/ q/ f# g8 {0 M
  18.         printf("socket...........
    * |9 U& P4 u% e4 x6 i
  19. ");
    ) b2 M  j! J+ I! H/ {% _" y1 \
  20.         6 {0 I. h6 ^8 Z
  21.         struct sockaddr_in srv_addr;5 P4 ~9 B. }( h# u4 z# M4 v5 A
  22.         memset(&srv_addr, 0, sizeof(srv_addr));  r* C5 N% Z- o9 }# C) q( z: w
  23.         srv_addr.sin_family                 = AF_INET;+ J# c4 m3 v- L2 {( q
  24.         srv_addr.sin_port                         = htons(8888);) t7 k8 p1 G7 J% W6 t
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    & C: O1 V3 O% z" m
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))), K* Z7 D$ K# R- q
  27.         {
    2 S0 r( |, G4 H8 ^; E8 a
  28.                 perror("connect");
    * e* k( J5 j$ x  r% S* |8 h  A8 k+ W; a
  29.                 return -1; //exit //pthread_exit  a4 w! r# h0 I3 s
  30.         }
    0 L" j0 R7 V6 I
  31.         printf("connect..............) u2 G8 a: ]& `- Z* n6 s
  32. ");+ c! M$ u: X) R% W& b( l) Z
  33.         char buf[100];( Z; l" T2 M* R( V, E
  34.         int ret;2 W, p3 l- a; {$ G" R; o; i
  35.         while (1)& Q$ X( g9 p* n9 A
  36.         {7 r" @4 c& Y3 N1 Q; A4 ^
  37.                 printf("send: ");
    ( A% r4 @  }, x# t1 V
  38.                 fgets(buf, sizeof(buf), stdin);/ S3 }0 n! Z- t( V
  39.                 ret = write(sockfd, buf, sizeof(buf));9 Q7 K5 }& X) ?* G) K4 \# |( }
  40.                 if (ret < 0)' c4 s( n0 a- q
  41.                 {4 v( B* L. H( \5 B9 R& Z
  42.                         perror("write");
    1 G- V. w0 {6 y3 n
  43.                         break;% x6 P( @- b( R7 T- D* k( [
  44.                 }1 |* Q4 Y; p: k$ u
  45.                 if (strncmp(buf, "quit", 4) == 0)
    6 M% ?6 E5 C- L7 @( ^+ m
  46.                         break;4 W3 t- s2 z! K/ r
  47.         }$ ?' _: j+ f" S% |( F' z7 \' r
  48.         close(sockfd);
    ! u2 v% e0 [' d8 S2 H2 p
  49.         return 0;
    + P2 A4 D) t4 C; k0 s) |
  50. }
复制代码

: n3 J& }- z  Z
- v# N! c; F3 l- P) @
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-19 21:00 , Processed in 0.058906 second(s), 22 queries .

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