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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
' V2 L$ P4 ^5 T5 A* e: X; h* ~
* g; `5 c" |# f1 o
7 Z9 }8 b3 h5 K, O( h, d$ Y
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 t; j6 s3 D) K0 A& W# s3 _
3 H& r. Z+ @( w4 K0 v1 r+ C

; `9 H4 X' z' r) q! S* RTCP协议
# J/ ?$ B' v3 I4 a6 HTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
2 |7 I9 q( E( Q9 p7 L; b, }: F
. m% W/ y6 [! ]3 m

3 T: n# C) k8 F. F7 I3 k4 A1 S& a5 x关键词:三次握手,可靠,基于字节流。, r# |) `1 v4 [* p% r
+ B! I3 w& K- w0 a% T* H) O" j& e- P
- `* [* A3 Z1 Z7 B9 c
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
; P9 C& V# \6 E+ i4 v' K: N- f / [8 [7 _1 X* i) p
TCP服务器端和客户端的运行流程7 I$ i0 F; u/ q+ [

; b/ E" x8 Y- i; i4 Q
6 |& T1 d* M, r( y9 D  S
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
1 i9 A) O. K4 U* s% u0 E; G: O& C4 ^* i' a+ a; h
- _2 r* Z) U7 g' F& w0 S  k
1.创建socket+ L. Y6 b. i1 j( q
socket是一个结构体,被创建在内核中( Y3 g0 i9 M( I6 d" {# P- j. m( V5 M
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议% b, Y% l* N; B, }0 T

, D: R( ?2 F8 M6 |- B; N  l
- o0 m2 j) N* w5 H
2.调用bind函数. K2 j. P5 m7 k! D
将socket和地址(包括ip、port)绑定。" S% M  u9 Y% Z2 L+ h/ a- H+ _
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序' y  A/ A5 N# |1 x+ m+ {- u/ \/ s3 A
struct sockaddr_in myaddr; //地址结构体' M3 w5 O; x' F7 C% l
bind函数. q" V* [% n- J, P% T/ n4 r
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))) f3 X& D" f: @( Z  y

( }, L7 G3 H' R

* d  ?. w, A: r8 k! A" u: @3.listen监听,将接收到的客户端连接放入队列+ E# _7 l+ e1 I* r9 S5 j/ \
listen(sockfd,8) //第二个参数是队列长度
, {2 Z' f3 X6 k5 X9 O$ \* K$ c+ A) i& g/ K$ C" Z% \* J7 O, n+ M
7 x! [* f2 Z* K; R! E& {
4.调用accept函数,从队列获取请求,返回socket描 述符
& d$ T, u' @  G6 L. ?  如果无请求,将会阻塞,直到获得连接& N1 _& e2 H0 q+ Q- Z) [
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数% L  M- O3 c0 p9 X3 r: H# k7 r6 L

/ N/ V! Q/ _4 Y& ^1 Q

* \4 ?; `' w: A7 Q1 ?3 C5.调用read/write进行双向通信9 ?" S  H/ J* T# s. x

0 b/ R# A) f2 ~' B. Q% ^

! n$ p" ^6 y6 k- f7 G# ~; }6.关闭accept返回的socket
0 [) {* s3 A( Q3 M1 H2 \  close(scokfd);, b' a. l, d$ i4 g7 g

' g. p, `3 |: S7 m( H

# f; k% H. w# i0 j# J! o( ?1 ~2 j& Z( {8 ?% x
" K+ Y: n" o( k& c9 e
下面放出完整代码! t+ I& ?7 r/ z4 V2 P( c) ?- k

+ E, R4 N8 n1 H6 x  o
  1. /*服务器*/
    ) b& k5 R6 d: ^3 h# H$ I: T( {4 s
  2. #include <stdio.h>6 F" t4 l/ z" h+ y
  3. #include <string.h>
    2 X" L+ P% q; p9 K7 ?  [
  4. #include <stdlib.h>
    - z: T. E5 a" t" H% O
  5. #include <strings.h>7 l- [; f: |: J
  6. #include <sys/types.h>
    3 Z  G) |& e6 [5 k
  7. #include <sys/socket.h>( }5 h. q/ l" E' m) m
  8. #include <arpa/inet.h>
    * l5 p7 u# z6 O, C3 F
  9. #include <netinet/in.h>/ @# y' f3 x, E: n
  10. int main()
    + g- C+ u% A% n. E" K; O! K
  11. {
      n% f7 e% h( t! J. I* h! C  h
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    7 C8 K6 R  k4 P9 l$ n& q; P
  13.         if (sockfd < 0)/ N3 y  S9 u. J6 L  g4 I( R
  14.         {% R9 w; ~  \- {) t# g3 U1 W& v
  15.                 perror("socket");
    : Q& S; D6 _. V0 j. o7 ?
  16.                 return -1;
    ( Y/ w& {2 Z5 c& }7 f( i! J1 z
  17.         } //创建失败的错误处理# e* w. @' s  p! a6 k. A3 n
  18.          printf("socket..............) h+ P3 z* Y) K
  19. "); //成功则打印“socket。。。。”( `) c: u. L, U' I8 q+ O
  20.          + ?( U% B+ V' |" o" s
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体; O" Q7 c4 h# c: O2 L8 ]% A
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    : V( h4 A/ n7 k3 v) u2 I) w
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型( ^. Z! B4 |+ X. f
  24.          myaddr.sin_port                 = htons(8888); //选择端口号* N! W5 Z+ k4 n. E. ]
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ! F# A  J/ ~' K$ R; M6 a. R' e  W
  26. 5 b. P! z7 ?8 L* N6 W7 G2 z* p
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    - b  R! j/ K7 m4 v( Z; F
  28.          {
      [0 x, v) l, P/ E5 q; O
  29.                  perror("bind");
    / }, x9 R  E- N% F  F+ O' z6 M
  30.                  return -1;& i% l6 F( V: b9 i7 B
  31.          }
    ' W! ~  b  D+ Q
  32.          printf("bind..........9 A# U# I1 z, V, z
  33. ");. E$ Y% z+ N" J0 e) h* \* X0 {6 C
  34. 4 t4 |) F; V: ?3 Q; S$ o* r. a
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听. z& C7 V# _9 {2 @  _. T4 |
  36.          {
    5 B1 e# V5 S4 |4 F4 g+ O5 Y+ U
  37.                  perror("listen");
    ! [/ a+ {+ o' A. b" o0 ~* P
  38.                  return -1;
    ' q8 m6 P: F. [* x. A
  39.          }% f8 p0 m3 T  x4 z: w8 {- C
  40.          printf("listen............
    7 D( D9 V! X& b; Y" I: K
  41. ");% G% G5 h/ r( r( a
  42.          , G$ C7 W- }$ W; ~; D( g
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
      s. t+ t+ T, i9 ?: a
  44.         if (connfd < 0)5 e- D6 l9 q5 f( o
  45.         {
    1 H+ k$ _, l$ D* @' _. e
  46.                 perror("accept");+ q* X2 t/ e- [7 o6 d
  47.                 return -1;& o- C/ f! w. P) `! s7 r
  48.         }  @- p, y* r* m3 j+ R
  49.         printf("accept..............
    - B/ u0 e+ ~" G# u# N
  50. ");8 B. z3 n6 D& G: n' y
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    4 G/ l7 K8 [# `% p) s( C
  52.         int ret;0 R, c7 b" H: h
  53.         while (1)4 I  T( w8 `7 L) n) i1 D
  54.         {( v. [+ t" K8 W( |" `1 |" C% T
  55.                 memset(buf, 0, sizeof(buf));1 B) o/ k- [( W8 J) u
  56.                 ret = read(connfd, buf, sizeof(buf));7 r9 V, P8 `7 X/ W; _- ]
  57.                 if (0 > ret)6 ]  @7 I+ r2 B  \6 D4 y( {
  58.                 {. e' ]- |4 u. \6 _; m3 S, e
  59.                         perror("read");
    2 C- `- v1 d1 j, V4 j* R0 J5 K
  60.                         break;2 ?( x! o. V2 s& }; @; l
  61.                 }//执行while循环读取数据,当0 Z0 j3 j& i; j% v
  62.                 else if (0 == ret)
    8 y3 X1 u. E- m0 P( E, |' ]& p
  63.                 {
    % t! Q( n4 Z# _
  64.                         printf("write close!
    ' X: s6 w2 ~$ \* n: Y
  65. ");
    - Q; d( ~% e. m6 y
  66.                         break;- c, ]5 [: G: G! E% c
  67.                 }
    - }" ~; G9 S, @- W, U
  68.                 printf("recv: ");8 E- g! i0 p- c- `' F% D
  69.                 fputs(buf, stdout);//打印接收到的数据
    * i$ Z) d" I' e$ F( o! a! B: G
  70.         }8 t7 N$ e; |/ I  q
  71.         close(sockfd);//关闭套接字
    / _2 a) j3 N6 m: b. u
  72.         close(connfd);//断开连接+ i, l" M5 z, V- Y, }
  73.         return 0;. C1 c5 \- D3 N& n) I" w6 e
  74. }
复制代码

; @4 p7 v- J* i) o/ w% c! L8 G' U- R7 @0 y  C
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
      z9 \0 A6 g: ?8 @* }
  2. #include <stdio.h>
    4 K5 b' t6 z/ y2 j
  3. #include <string.h>
    ' J* [/ z. Z; @1 a/ p
  4. #include <stdlib.h>
    # T" r$ I$ s9 U: J7 r" v
  5. #include <strings.h>' Y/ o$ v. V" p. c1 c& c) H
  6. #include <sys/types.h>% O( b" s" Y1 L7 [. k
  7. #include <sys/socket.h>
    4 c/ N2 s9 w, K. A* w
  8. #include <netinet/in.h>* O7 Q9 S' W+ s- A. @0 L" i. c
  9. #include <arpa/inet.h>) ^; T- ^& C( J5 J4 L
  10. int main()
    5 \' J8 |7 E8 N: Z
  11. {
    ' f7 O$ a  ?# y
  12. int sockfd;. L1 T0 j, O8 L' N7 X
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    * C0 Z$ _" B. V! f4 ^
  14.         {
    ; q5 j1 T0 E7 E. m: w2 Z/ H
  15.                 perror("socket");( z& I9 q- q( X% B' n" R
  16.                 return -1;
    " C6 [; @7 \) i6 X2 q6 c7 q
  17.         }
    " R; a, t3 m5 l$ ]
  18.         printf("socket..........." y* N" T  U9 V4 |
  19. ");* n" {- i4 M* N; T8 [
  20.         
    4 N9 s7 X# {6 l$ [( m* g0 B
  21.         struct sockaddr_in srv_addr;
    $ D6 A$ w! ]$ G
  22.         memset(&srv_addr, 0, sizeof(srv_addr));8 u7 X% I# }- d
  23.         srv_addr.sin_family                 = AF_INET;
    ) o( v8 j* k3 g. w0 H- V! |
  24.         srv_addr.sin_port                         = htons(8888);
    - B" a, P/ ~  s( Y* r, E
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");( k: _$ k" v& G: k5 U4 E
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))# [$ X" ?0 ]1 N
  27.         {# l7 x, e6 }: M7 P8 b# Z
  28.                 perror("connect");( Q; [# B& H* {. M) d
  29.                 return -1; //exit //pthread_exit8 K3 C/ j: U  S
  30.         }
    * k8 h* s' Z& S
  31.         printf("connect............... `% T+ Q1 n9 k; k6 m9 Q3 |
  32. ");
    ; l3 C" \* Q& H% C' y% C
  33.         char buf[100];) [4 x6 A2 E# Y' R1 G9 v
  34.         int ret;% u. P0 X; z# ]+ Y2 D0 p' j+ Z
  35.         while (1)
    ' E/ o% E% Y5 C8 x
  36.         {. t' J" }0 N+ n
  37.                 printf("send: ");
    6 w1 B/ r/ n0 J( R* c* e# W4 V4 r
  38.                 fgets(buf, sizeof(buf), stdin);
    5 ]& O9 K9 c! ?; B, y
  39.                 ret = write(sockfd, buf, sizeof(buf));2 |" I  k% s7 Q  b7 V
  40.                 if (ret < 0)) x  F8 l2 p9 B  q0 M
  41.                 {
    7 i8 V( _6 H- I2 z; n- I
  42.                         perror("write");6 p" J) c' _! G- z. \
  43.                         break;& e+ `5 ]4 O# |: v
  44.                 }
    - R' }% y5 a. ^+ J# F
  45.                 if (strncmp(buf, "quit", 4) == 0)
      [0 y) @  q, v: h8 e! e
  46.                         break;
    ' [2 Z  O) G2 z
  47.         }
    ( W& o0 ^0 f4 S( I. k7 `
  48.         close(sockfd);2 x9 P2 q- i" `5 A4 _9 i
  49.         return 0;9 a! Z9 g5 S- X
  50. }
复制代码
. f5 W- O$ i( N! w( p& ]

* S  U* E: K* t9 U" {7 \
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-2 21:53 , Processed in 0.129132 second(s), 23 queries .

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