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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
+ f1 O6 [, P+ s# t3 R! ]/ P* m) g$ b" t4 d3 ?# f; x  [
7 ]9 d$ n. |+ y* D8 i
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
3 L, k; ^7 @3 R
& Q- b( I4 e; T: o; l! c5 n% z
, i& K. ~& K+ Y- u% j
TCP协议0 C+ g. R5 m& M1 R# L
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
+ S7 y  |* \: n. }1 b
5 I" K4 K; V# d! q4 D$ J
3 ]: M/ f$ `) M" j* f) j/ M1 e5 B
关键词:三次握手,可靠,基于字节流。! C) T3 H: m$ \

# O4 v  }  M; e& \4 [
0 l4 S* Y) m4 l3 h6 b
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
# j+ Y/ O& i. Y9 Q! D% ^  N6 f / K4 E2 }: K% C/ j' |
TCP服务器端和客户端的运行流程+ f5 M( |5 i# q+ e+ A

% `1 |2 b) |; r9 F' A" w

3 d6 ~* |% D3 K- ~: R9 z% T如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?) I. v2 \8 R" J. a9 h' ?& `" }
: E2 x/ _9 C6 R! t

9 F" Q7 M2 k* \& P5 \7 V1.创建socket9 A+ v2 L# q) H5 {3 b. M! N
socket是一个结构体,被创建在内核中
$ M# `" e. s/ H7 s sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
" G  h& T$ i! j: W
; {/ {' Z2 \0 Y* g
* ~4 p  l  R2 @( z5 f( d! J. Y2 Z
2.调用bind函数
) ]! J% ]$ V% P- I  l9 t 将socket和地址(包括ip、port)绑定。
: m4 D2 v- H3 j* |$ l$ Q 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序: ^% Z) n* J$ N
struct sockaddr_in myaddr; //地址结构体9 ]  ?; @" d" ~8 }! T1 m
bind函数; w# ^( F4 S+ C! v2 ^- F& p, B
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
9 Z- h( c& k$ ^" j/ D9 F) Y4 o9 |" |: J, r# j, h; U
2 U$ ^! x6 h. d4 p5 [
3.listen监听,将接收到的客户端连接放入队列
( T: V8 P0 G) Q, h" j$ Y8 O* ~+ P listen(sockfd,8) //第二个参数是队列长度
$ @( q! S/ a- R- K5 n' L9 H+ {& F/ \) ^
$ ^2 b0 f: z# x1 @- f) j
4.调用accept函数,从队列获取请求,返回socket描 述符+ \: P+ j- i$ t% O& J$ @
  如果无请求,将会阻塞,直到获得连接  U( [# B9 m3 [4 m1 v; V
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数' B9 _8 ~7 I2 t- B/ ]

' C* ~* T  R8 j+ g0 q. T6 }
1 Z- d" \1 H9 ~2 l7 u# f( i% r
5.调用read/write进行双向通信
  v7 n! n" g; r1 L8 L7 i! H$ g0 @( ]
* {8 W; O5 a: D4 d6 `
6.关闭accept返回的socket5 j7 v& C) x7 c: }+ O0 V
  close(scokfd);
) H" j# G3 g9 F6 M, U* I
# `" q1 W) W) V/ ?# @' ~
  B5 X5 ?4 e" A$ \4 J, R
5 M- k# \& o9 Q# @  ?7 k5 ~
0 Y) y8 q  |% }: X; a+ s
下面放出完整代码
+ H2 v1 t! n* b0 w2 X
/ w, p' l% _0 j, v7 g
  1. /*服务器*/
    1 j- i  {3 Q3 Q6 B. A( e7 I' f2 `
  2. #include <stdio.h>3 J3 K8 o! g7 W4 I' A
  3. #include <string.h>
    8 m% R7 ~5 ~4 i  A& I
  4. #include <stdlib.h>
    : _, i- m( }, F. C/ V* H/ \
  5. #include <strings.h># Q: K8 R2 g4 C/ \2 n
  6. #include <sys/types.h>
    " ]- k1 i/ x' a2 j) ]" N
  7. #include <sys/socket.h>
    $ ]) a. g/ z+ m# v$ v
  8. #include <arpa/inet.h>
    $ V& D  h; S6 U
  9. #include <netinet/in.h>1 g2 U( r2 K& B$ W& p8 c
  10. int main()
    . M# f: Q( q  u. O0 w; q
  11. {
    ! X# F$ f4 P" d
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字+ K1 s- K+ j  x' `8 m2 `
  13.         if (sockfd < 0), l6 J$ p1 u  q( p4 s  Z1 E1 |
  14.         {9 z. u7 T; i  ~  j! L; g' K, v% N1 S
  15.                 perror("socket");
    ' g& `6 b4 z: [: n+ y
  16.                 return -1;
    " {8 l' o- ?. M: p" c  W
  17.         } //创建失败的错误处理
    - [! Q  {3 V9 B1 q2 C# t
  18.          printf("socket..............2 o* b/ N$ \9 j3 X+ A0 y& E
  19. "); //成功则打印“socket。。。。”
    * e4 V1 {) h; B% }
  20.          6 X& o3 {8 s. W; A7 s
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    # ?; v7 s/ c* M
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见): O# ^% B! W4 h. |% a
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型8 ~$ i% V% u6 Q0 `1 i8 H
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    . B/ b9 U# V5 @/ ?5 ~; T7 {
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址4 O7 O: b% }! w1 j+ s
  26. ( m2 A. ~2 e) t
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字% o6 k8 k% C: }  ^; Z
  28.          {) X7 D! _9 S$ h  s" T/ n& Z
  29.                  perror("bind");
      h: N; i& F; T; }
  30.                  return -1;& B! T5 u' b: G  c# o- G- D
  31.          }
    6 ^2 B: \5 t3 U  x8 n
  32.          printf("bind..........9 ^( `  ]( a" m# ~
  33. ");( [( Y' o5 J; K' t- ]9 U
  34. 2 d7 M" _: Z- |. {: z
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听, A+ t5 `- y1 f( y. M( P
  36.          {
    . k' e, i6 q  @. @, M. E
  37.                  perror("listen");
    & U+ |  Z2 Z2 ]  G% {3 L
  38.                  return -1;$ Z% B5 L8 L- K( p5 C2 B% h! H* [+ J
  39.          }
    8 X1 r7 t% n  w6 o; L5 C( f
  40.          printf("listen............! j+ h( O% I+ e  a
  41. ");" Z/ ^& e: D! d% D8 H
  42.          " S. d: ]- b5 S7 l$ Q( E. ]- h
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求2 \" B3 J' B6 R. g- J
  44.         if (connfd < 0)- t+ L3 v% x, A2 r
  45.         {
    ) e# C$ ?3 F& x2 i- `! F
  46.                 perror("accept");: k9 A8 b+ A' J2 o
  47.                 return -1;/ |% N# l; h% B0 A$ `+ p" {
  48.         }9 U! v+ z- f, M1 h. D
  49.         printf("accept..............
      o7 E5 t* q! ]" ^& r
  50. ");
    " M/ _; T; E/ _3 P8 D
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    0 o6 C3 W- p% e. d
  52.         int ret;  n, n: [4 m5 M& }# C6 k( h  A) i
  53.         while (1)
    0 T: u) i* `2 Y. Y/ t: R
  54.         {8 I) H& R* v  x* h  a2 @
  55.                 memset(buf, 0, sizeof(buf));
    ) _. j, I9 F( X+ e3 u; z
  56.                 ret = read(connfd, buf, sizeof(buf));( x' S- m7 P" j3 m( W3 J
  57.                 if (0 > ret)5 L' s, y3 j) i7 \/ Z. c2 d& K4 S7 K
  58.                 {
    ) {4 p- ~% D  [: C; O  X! i# f
  59.                         perror("read");
    ; H& j! l# q" S9 l4 L  V0 U* P
  60.                         break;
    ) w# x; f2 x  P
  61.                 }//执行while循环读取数据,当: H4 T5 b0 e% A. i
  62.                 else if (0 == ret)
    & U" u  q, {- B# z- b1 J
  63.                 {
    / @5 K5 t, \! @+ ^- b
  64.                         printf("write close!4 Y6 l# P9 b2 e$ q! R
  65. ");7 X" J% ?' ~2 C. V1 Y! L# u: a/ @" I
  66.                         break;, d) i' o1 Z- j  T
  67.                 }
    3 T# g5 N, |/ C* m/ u2 c7 r
  68.                 printf("recv: ");
    $ D: W% Z! ?1 ^3 _" C
  69.                 fputs(buf, stdout);//打印接收到的数据; d) A6 C  R; b0 Q6 N; r. f, k
  70.         }, v0 {) [/ b* b$ m6 Y/ w! _4 m7 r  g
  71.         close(sockfd);//关闭套接字
    0 {" k; Q" L0 L. f% B! {% D
  72.         close(connfd);//断开连接
    / s- [+ C$ E7 w7 d
  73.         return 0;4 V" D4 S! s' C2 q7 I5 L: V' k
  74. }
复制代码

/ F3 R7 M% G4 b! k) c' `$ a1 ^$ `+ i% [& H, V0 H
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释). t- \- ^# h6 {1 ^
  2. #include <stdio.h>1 \+ l9 `- l0 ~: f* J
  3. #include <string.h>5 a8 c! W6 b  A& C  S3 G& v
  4. #include <stdlib.h>
    4 `; M  w- C- @$ F
  5. #include <strings.h>
    : ~) U' x& N+ I
  6. #include <sys/types.h>
      I, h! p! y- ~8 P- i
  7. #include <sys/socket.h>5 I7 m7 E, u0 D% ]* U
  8. #include <netinet/in.h>
    $ x: V9 f$ t6 }; T/ v- T: q. c8 m
  9. #include <arpa/inet.h>
    3 w  ], ]4 |; \( A' }
  10. int main()' p) y7 w* ]- g" I: O
  11. {) A- k9 y2 _% T1 k# [) V- _: C
  12. int sockfd;' ]1 e% W6 v6 s# R
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))1 s, W5 s! A4 \0 ?5 g
  14.         {
    ! \; y4 u: O) r4 e1 R
  15.                 perror("socket");3 c/ a) U# r/ X, p. R
  16.                 return -1;; C1 `/ n6 _' w
  17.         }# g2 l' G7 |! ^8 e" N8 I  x7 d
  18.         printf("socket...........9 M1 t; O# X8 I1 j/ Z- O
  19. ");- I7 w& F8 T4 V9 o- y7 q
  20.         . B' D! K+ ]8 B) \( h
  21.         struct sockaddr_in srv_addr;2 J% J/ u) N3 l9 m3 N
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    ( y8 {/ t$ y' r' X# Q6 N
  23.         srv_addr.sin_family                 = AF_INET;
    $ F$ h' y9 h  S* u8 q! E
  24.         srv_addr.sin_port                         = htons(8888);
    . `+ E# r5 H; Q2 P0 k# y0 v# f
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");6 @  o& L; D0 ]1 t0 C
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ; p& N3 R+ a# h) J' o
  27.         {
    3 L  a/ `6 [) w+ M+ G: H0 e
  28.                 perror("connect");7 l, s  W" g2 b9 l' t7 X
  29.                 return -1; //exit //pthread_exit$ n2 V) z# C, E: {
  30.         }2 L* [0 t, m- n0 |  t4 }2 b6 b0 H
  31.         printf("connect..............
    0 C2 u; {. V) e, M% v0 B3 I$ x
  32. ");
    6 |: P& P7 Z  x, [& [/ D
  33.         char buf[100];
    4 f  ?: p+ `+ ^$ i
  34.         int ret;& B8 G' X$ l- k* `& m( p
  35.         while (1)
    9 m4 ~: X5 o3 |7 c5 n/ y
  36.         {5 s* A; {" r/ k
  37.                 printf("send: ");
    2 m5 {& D4 R! {- }9 T# ]
  38.                 fgets(buf, sizeof(buf), stdin);3 E" C. a' @. n& a% n* N* M
  39.                 ret = write(sockfd, buf, sizeof(buf));
    7 z3 B- s- r9 N: }7 A. t8 i
  40.                 if (ret < 0)7 O+ @$ g2 v5 E
  41.                 {
    ! h9 r8 K, j* \$ x1 M
  42.                         perror("write");- b# L* x3 _/ ]' s$ F3 c. [  t
  43.                         break;
    ) A0 c+ W2 S% h. r  Y7 u* j- z
  44.                 }$ T& f# N# z# ~% C5 Z, X
  45.                 if (strncmp(buf, "quit", 4) == 0)5 N3 J2 v3 A6 P
  46.                         break;
    ' [2 u0 O  ~3 j; t% q0 _' [
  47.         }
    ; {6 e" l* J3 i% A) r
  48.         close(sockfd);4 I  m& J) c/ I" t3 q1 Z& C
  49.         return 0;3 o" q; m: ^1 J8 }4 J4 d7 I
  50. }
复制代码
- e8 \: Y+ t2 |! l! U' ], x7 @

' E) H2 }( |0 P3 _" C
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 13:19 , Processed in 0.080341 second(s), 22 queries .

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