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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
( Z& N0 \* l1 x$ I6 |
5 x) p: X+ a7 B& E: d
( r2 I% f2 u, R* o
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
& B, u; X; j0 H# z/ r5 f3 }6 u7 K8 T& g& X" k6 _" c

7 P9 V5 S1 c; r# @+ B# I, J# jTCP协议9 k/ Y5 H6 N7 O3 R
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
- x5 L3 j5 f$ Q/ b( x$ [+ z
% `0 ~; `7 G! t

/ F! T5 O+ J/ Q3 R关键词:三次握手,可靠,基于字节流。" c1 s1 m2 Y) o+ U0 T) p- K

  V+ K8 C' F% t; b
7 w$ k6 W+ y  n
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 O% S* ^) h7 h; z2 U

! Z- D- ^1 f% rTCP服务器端和客户端的运行流程
/ K5 i4 {( j- L9 _
: S5 G# |; j0 S3 k) k2 ^9 t
: e& X& c, N  z# M) }% H  V) i
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?' H% i; D" t2 n2 y4 J% P- I

2 t2 H! o' K' y
* u5 k" [# m+ q- P% G
1.创建socket
. L, q- Q- G* U1 Z2 ~& f) A3 i socket是一个结构体,被创建在内核中+ D1 ]% k1 p. C2 C
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
: p$ X* Z& W6 W1 I6 z+ ^$ k$ i2 U& b5 r7 J2 ^8 q

/ V% i/ g, f: J7 m% ^/ U2.调用bind函数
. O* R; |! U$ t7 ~$ T 将socket和地址(包括ip、port)绑定。
1 _; X2 e3 Z" L* v 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序4 x2 Y! R# s! d- C
struct sockaddr_in myaddr; //地址结构体" O' S' f0 c/ S& S9 d
bind函数: W3 [( ~; F( O2 V; S. Q
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
( Q6 a) g4 K4 p% B) ~; S* z3 E( ]! W, c# ?! ~* s. {
# P9 s" @3 i$ v, o' J
3.listen监听,将接收到的客户端连接放入队列
3 ~6 Y% p7 z. `/ t7 @; @ listen(sockfd,8) //第二个参数是队列长度2 a/ H! p# [/ Z) j. Q, r9 p

! i7 i% b* C1 z4 h3 G6 [

0 O0 \8 m  x1 F$ q+ ~! W) s' Y4.调用accept函数,从队列获取请求,返回socket描 述符) D1 d# w0 g. ^
  如果无请求,将会阻塞,直到获得连接8 T( p  h- p: K" A5 N5 }+ h
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
/ `) f% F5 H* X7 }4 c: P: u) v4 V7 [8 k5 G; d
2 Z5 p( r* J% ^8 y3 I& O
5.调用read/write进行双向通信
& o5 Y5 T0 E7 S1 _- N) o7 l; V1 x& r5 }4 i) O- C' o! i; k

, q3 b( ]  @( N$ s. |, M# d6.关闭accept返回的socket
3 N- g6 _: r9 |; o  close(scokfd);4 Q8 I1 x/ l$ }, N  p

7 q. R9 E( l7 |& D! Z: s7 f
3 r8 ]" R; E2 p
8 u* m1 y0 G; q( x3 t  a7 z

, g! e% A$ ^6 z下面放出完整代码
+ ?- V+ d, x! j6 J) V
( Y! }5 h) u. O
  1. /*服务器*/
    ! n4 T0 ]5 |. v! ~
  2. #include <stdio.h>
    3 }$ h( F: q1 ~8 I5 r  z
  3. #include <string.h>: _; D2 _! ~- k5 S. y! _4 h
  4. #include <stdlib.h>
    8 C2 k. x* R# n7 w  M5 K9 @5 ]
  5. #include <strings.h>5 c- a# d; g7 Z! `6 u
  6. #include <sys/types.h>
    9 j2 f4 T2 X- T
  7. #include <sys/socket.h>7 P! Z2 K0 h! v: n( [, x- q1 x
  8. #include <arpa/inet.h>. [1 a. O% V' S! `
  9. #include <netinet/in.h>
    8 b# m) {: R" r
  10. int main()2 Z9 L( p& b+ o2 n+ k
  11. {& Z6 P" v6 v+ l1 y# h
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    . b* j3 g3 V% C! o7 z
  13.         if (sockfd < 0), S! L" {+ M/ [' z; ~. s8 [
  14.         {
    4 y& e2 ^# Q' N; @5 Y; g& U
  15.                 perror("socket");7 T$ s7 L1 D! q, @/ ?
  16.                 return -1;
    7 ~' W, f6 U9 Q; S( f/ k& f  k( ~
  17.         } //创建失败的错误处理- j& \( ?4 N" @" H! j
  18.          printf("socket..............
    8 c: [% P  I1 i, U# J3 R8 K
  19. "); //成功则打印“socket。。。。”3 ]9 i+ }6 k* G8 p# h/ t
  20.          
    9 G6 i$ x2 o" m8 @
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    + Q9 n7 z) ]8 R& G% e9 m
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)/ i' e2 R" R' E
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型+ q9 l9 v' a" B; t+ I' \  L
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ' q% m; X2 Y5 k7 h
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    - L. q- \3 Q5 x4 Q& k7 c
  26. 9 \2 q" z3 y( c* s& y* E
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字9 ^/ B; H/ F$ w/ @+ P
  28.          {
    # W; {# l2 b4 i/ G/ t0 o
  29.                  perror("bind");
    3 W) Q% l- X) s
  30.                  return -1;4 O1 v) O( q  F2 K' n2 a
  31.          }3 h/ E  a1 [6 N- s8 ~( w# t
  32.          printf("bind..........
    : x# h; K( j2 ?5 A; |4 ?1 R
  33. ");/ y2 t5 f/ p8 h$ N

  34. ' q, p0 z% |0 r9 {0 o/ I, b% p+ p
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ) S6 V$ Z3 r9 N0 A  O
  36.          {9 k. q5 J' B8 v( A) B1 x. H2 O
  37.                  perror("listen");5 |8 ^( P. O  z2 w% j
  38.                  return -1;7 f+ |- X1 [' i" {9 y) {6 d2 w8 m
  39.          }
    ; e6 r1 ?2 i* }( E+ n1 |3 B3 W
  40.          printf("listen............
    5 S+ s; _& C, y+ s+ Z
  41. ");
    " R1 @# A& t6 R' p7 N9 u
  42.          
    ) Q2 e- J; U5 H( J
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求2 U4 x# u0 B0 p; _# v& j
  44.         if (connfd < 0)* ?7 F, D8 ~! W% N/ h' R
  45.         {' G. Y* R* A. B
  46.                 perror("accept");
    , l6 z3 n( n' W1 N" V) Z
  47.                 return -1;' L3 E4 o' s% N* i+ ~/ e1 J4 Q
  48.         }+ T  k8 ?, F6 w% {
  49.         printf("accept..............
    # x4 Q8 |9 N+ q7 Y" j
  50. ");4 n) |; m+ m; q: d5 M
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    1 ]& q' b5 @; j8 f0 {
  52.         int ret;
    ; C  b2 ^) i2 d1 [
  53.         while (1)
    6 n  k+ W$ F; W2 K7 Z
  54.         {4 S1 G2 _' v! i) X
  55.                 memset(buf, 0, sizeof(buf));
    4 @8 U+ Q/ h, G6 p% m! F0 Z
  56.                 ret = read(connfd, buf, sizeof(buf));
    . S* Z! T- E/ y* }9 Y
  57.                 if (0 > ret)- C* {( _) y6 c- i9 N
  58.                 {
    ( \6 t8 v4 a6 i/ S
  59.                         perror("read");
    6 @7 Z: V' ~; K) O. x6 {' }* i5 g
  60.                         break;: J9 v% |/ a7 I3 A! y2 I$ X: M
  61.                 }//执行while循环读取数据,当
    1 b2 \; x3 a# U! L2 a
  62.                 else if (0 == ret)+ y( A) t8 O6 \3 |
  63.                 {
    ! @  I- D* H: b/ N9 d$ A) ~7 [
  64.                         printf("write close!
    , p. C8 G( X# B+ S& h
  65. ");
    " A' S: b* W8 o7 W
  66.                         break;! i; \* @9 O7 B3 t
  67.                 }& `/ a/ @' B% {/ h6 ]: ~; `4 Q4 N
  68.                 printf("recv: ");9 L& U1 q+ ?7 T6 ^6 P7 m
  69.                 fputs(buf, stdout);//打印接收到的数据2 Q( U5 d! e. c7 s, e  [
  70.         }. p6 o; |, q7 h3 \: d' a- r
  71.         close(sockfd);//关闭套接字4 l8 o4 G, A% f! O0 B/ g
  72.         close(connfd);//断开连接
    3 ?7 h! o( }' s7 u  F& u( n
  73.         return 0;* c* z- o9 c) w1 l5 N
  74. }
复制代码

# ?5 i  v9 X' k) n5 r9 H
& `5 s+ N  l+ W4 |' l
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)# \8 p1 c8 M( C. L6 G
  2. #include <stdio.h>
    # O9 N6 J2 Z! q* ?
  3. #include <string.h>2 c: v) l4 W& ~3 M. i9 r
  4. #include <stdlib.h>
    / ^# U& Q3 H# b/ o- ?* z0 J
  5. #include <strings.h>8 V8 Y9 ^1 f: _& ]5 ]7 H& w
  6. #include <sys/types.h>
    # m% M. [! O/ {* S. R. Q
  7. #include <sys/socket.h>, q0 ]$ k8 S+ F+ e+ K
  8. #include <netinet/in.h>
    * f) {0 K# g8 N* h
  9. #include <arpa/inet.h>( W8 p' J) d" a" X: d
  10. int main(); y/ x9 B7 R: r! j% W
  11. {
    + m2 C1 j8 J: W; ~
  12. int sockfd;
      l8 k; @& Q: O% r. W9 J
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    4 t# a9 n; Z5 t  ]( C7 v
  14.         {
    . E: U- f/ P( r, \) x2 I9 A# ]3 a
  15.                 perror("socket");
    / v$ h- s8 E8 {& o: h1 j3 U6 O, f0 U
  16.                 return -1;% i# t8 B$ ^& e* E! Q$ F
  17.         }
    # _+ P0 g' }  x# q. h/ Z6 y
  18.         printf("socket...........) g9 B; j, p! M6 E( V- C
  19. ");- S! c2 S* J5 x0 G3 V, t- @
  20.         0 Q: I# B0 \. k- x& n& B
  21.         struct sockaddr_in srv_addr;. @- O: `: j& T9 V" n
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    8 e: k% D0 l7 x' N% d+ _. V
  23.         srv_addr.sin_family                 = AF_INET;
    ! L5 z- T9 p- b: b
  24.         srv_addr.sin_port                         = htons(8888);
    " e0 I7 G6 I% v  p
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");. _( k7 a) O& M' n" w# S! g
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))7 V" j" n5 H. c) ?
  27.         {9 ^- G  k% W) E) H8 Y
  28.                 perror("connect");
    # r! O! L5 ?- `. g$ [1 W; i! D
  29.                 return -1; //exit //pthread_exit8 C. E* \' ^) \  i2 D
  30.         }. J  b. c( q$ q+ Y
  31.         printf("connect..............
    & C* P5 c$ L0 o1 |/ I
  32. ");- x/ h. i$ \( f& s2 D! x
  33.         char buf[100];
    8 w4 I9 {* E- g: @) O) B
  34.         int ret;/ C, T) J+ ]7 P
  35.         while (1)
    4 u  W/ E* A# }, ]9 _/ }2 H0 u
  36.         {8 F' B; `# w% w: G! a/ ]+ z
  37.                 printf("send: ");
    8 q* L0 q; e' `/ Y% ]0 M# c
  38.                 fgets(buf, sizeof(buf), stdin);% e: V$ \' D# j* Y$ j! E
  39.                 ret = write(sockfd, buf, sizeof(buf));
    . b' z& {: Y/ |, z: g+ J% a
  40.                 if (ret < 0)
    6 H8 {4 O; j  a- ]: z' V+ w: j
  41.                 {
    : m# d5 w( u/ R/ W/ e
  42.                         perror("write");$ L4 b6 `8 r7 }5 N/ t( K
  43.                         break;0 j, w+ ~- \7 ?  ^! c- |
  44.                 }
    * i6 \- |) L, O5 P* q
  45.                 if (strncmp(buf, "quit", 4) == 0)! o5 S3 X* m* m, K- ^
  46.                         break;
    ( Y) @. R8 j4 u8 ^
  47.         }
    ( x# F: }4 J; [* e) D& u
  48.         close(sockfd);2 T9 M! j, ~/ I. o6 @
  49.         return 0;' f0 O+ g7 h% Z" M
  50. }
复制代码

8 B1 \5 z  j$ V" \- C1 }8 r
$ Z8 P& i# S( A! P( V) }5 z+ w( g) q
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-21 20:22 , Processed in 0.139209 second(s), 22 queries .

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