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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 10609|回复: 0

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

[复制链接]
发表于 2020-5-9 02:09:24 | 显示全部楼层 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
, S/ a+ N4 U" }: Z1 {0 @; _
1 D! ]# K7 P" Y' R. ]

1 R( l* N, t: X- [socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。; ]% e0 r5 ]$ }$ ?' y6 R) H6 x: B+ ]
" m: |' ~; d- C& r& I2 j3 z4 }
3 u' v  L: B* Y1 {& P
TCP协议9 u0 u& I4 s5 K1 J; P) p7 l8 a$ j) U
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 X" f# \6 g. B
" s. j( k! K# U; h3 E& Z

& n# Q( s) B  b8 P. u- q关键词:三次握手,可靠,基于字节流。" h! f/ m/ r8 b/ h. Z. c
+ c% [% q* P* F( Y" _4 z/ |
; n5 F( h6 y. d7 g; _+ B
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
# o, b: N( I; P 微信截图_20200509015654.png
. ?+ N& u$ L, S; P! o! K* wTCP服务器端和客户端的运行流程
* U2 B. R! ]6 r6 a. q5 d! w
3 m3 x. X# }$ S7 p' y3 y. S7 J( v
6 Y$ `) f( W8 Z) j; Q# k' i
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?5 t! C( r7 G3 [" q
: G* ?6 p2 G& s% `) B7 {' e
3 Y) v) d: O+ j; A7 T7 A
1.创建socket
8 a+ p* P  i( X& c9 H socket是一个结构体,被创建在内核中
2 E% g' C) y! [. S) V sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
) u$ |. U" f6 l' o2 p; d: j' g' |/ b7 A; z( T4 S% i# ?
$ ?, Y1 `% v' v2 @$ |- q
2.调用bind函数! C8 d4 g  m; C0 c$ ]1 l/ q( q
将socket和地址(包括ip、port)绑定。' A7 f$ f9 d! c7 u: g# C$ P5 G$ ~
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序& x+ U% L7 c4 [/ f
struct sockaddr_in myaddr; //地址结构体
$ a5 G+ b, S' l* q$ v( ^: o bind函数5 C; M& W4 v7 q9 z4 m
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* W$ V9 r$ L2 `6 K

9 Z. ]; Y. t# a7 i- ~* ~6 |, A

: n. ~# {3 ?8 v3 }& u7 x9 y  N6 S3.listen监听,将接收到的客户端连接放入队列  j  w" H9 n2 B& R
listen(sockfd,8) //第二个参数是队列长度- I4 c& D8 l& Y& e( M6 R. k

: z6 j4 [: M. X3 s, s! Q0 r
4 `( ^& X0 |0 W6 G. c5 }2 h' ~
4.调用accept函数,从队列获取请求,返回socket描 述符
: T1 `6 I) y0 l; \$ [  如果无请求,将会阻塞,直到获得连接
/ M' [) c* ]; V3 h  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
6 h  ~( ^: H$ U: `) ~4 o1 Z# E7 `2 f$ O
7 x5 m3 d6 ?+ s- H
5.调用read/write进行双向通信
+ o7 ~/ ^1 ^9 Z2 C( `  x( M5 ^) U3 {9 M* H2 S) K
0 v3 u; [5 f7 s/ X3 N. _
6.关闭accept返回的socket
8 k* P+ f' x& z" ?1 P  h6 y+ w/ z9 y# m  close(scokfd);
  [- r$ Y5 {" F
5 x/ {" R. c! c- A, P
; T# w4 G3 \' {7 h, z
( W. L5 h# F/ a

# f0 w$ n2 I. y5 Q5 {, U! {下面放出完整代码1 J! f# Y3 T/ A: _# Y3 Y5 f
# J' s, }+ \0 u5 X+ F/ [" }/ N
  1. /*服务器*/# E  I9 E( a8 x2 f
  2. #include <stdio.h>
    & s2 d& S* t( Q7 O1 `0 H
  3. #include <string.h>; a2 d, F$ J. q
  4. #include <stdlib.h>5 L; w0 ?/ Z) l" ~$ G% r) p# v# z" o/ f
  5. #include <strings.h>
    , |* z( U+ o( E
  6. #include <sys/types.h>, u, D, q, r' a/ D" ]& ^5 k( q$ x/ H
  7. #include <sys/socket.h>
    ; d% f/ Y* k2 p' K+ ~
  8. #include <arpa/inet.h>! i7 a+ R9 v- D! z; f  t  ^
  9. #include <netinet/in.h>( B* u$ W, X1 {' H
  10. int main()! K. ?$ H# I" J9 [' J5 Z# H
  11. {
    1 A. L* H$ p2 O6 l) s7 ^0 L
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字* z8 T7 ^# Z0 U! }. Q$ V/ v
  13.         if (sockfd < 0)
    & S$ p# O6 Z# R) ]1 c
  14.         {& \- O$ Z. O* M- U  }" u- w
  15.                 perror("socket");# O3 H7 I8 l9 v) q4 g  Z; [
  16.                 return -1;
    ! y/ l( S+ Z, f" O; I: |
  17.         } //创建失败的错误处理5 K4 e5 R3 N. o3 Q' E5 v8 s7 ?
  18.          printf("socket..............
      L! {: d0 g# F# F
  19. "); //成功则打印“socket。。。。”
    " ?% o# }# }+ m, L" X- {$ T4 q
  20.          
    + V- A6 I$ P2 z/ n. q: O
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    9 Y0 Z6 {) u3 |( F# J5 o8 x
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ' ^0 Z9 @4 ]4 `$ c7 X1 e
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    3 ^# c$ V+ K% m- v
  24.          myaddr.sin_port                 = htons(8888); //选择端口号5 v8 o. @. [/ x( r
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址1 {* ^4 X# t3 m: p( h5 G% N

  26. 9 b' s0 n4 ]/ C/ `7 f( P% g7 p
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字+ O/ z7 m" j1 o. {6 D
  28.          {# E  K/ j& E' _7 R7 x$ o* N
  29.                  perror("bind");8 s3 U: j: |4 Y3 s8 b
  30.                  return -1;+ O* u; p+ }% m0 H- j
  31.          }- S, B% b! r4 @
  32.          printf("bind..........
    % X3 B. P+ C( ~
  33. ");. \0 s5 V. P$ ?( g+ |! m7 m
  34. 4 x; F6 O- g3 ?/ l3 N
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听9 h- {+ M7 m5 q! v- G
  36.          {' E( Z0 L. y) Q. C" `4 E
  37.                  perror("listen");* x2 V7 f! M1 Z( q/ L3 a
  38.                  return -1;
    5 ^( v, `9 z6 o% Z: o- Z
  39.          }
    5 s0 M/ d8 O' d$ ~  D* j' n2 f2 Z& l
  40.          printf("listen............
    4 r7 @; Y$ a7 o0 }" x0 x# k# P; P
  41. ");
    ( |; l; q) n- E/ D4 u" t. B
  42.          ) p" W; B# P3 J: i; f4 q
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    # F: A( }2 ?: t
  44.         if (connfd < 0)
    ) w0 a3 u0 @' o" S$ G
  45.         {% L9 L* Y& ]" ?% x6 [6 L
  46.                 perror("accept");- u" i+ Q$ _& d
  47.                 return -1;
    3 Z7 I' H0 M9 S; d
  48.         }
    : \0 l$ z& M( o: O
  49.         printf("accept..............
    # l( z1 M# S" d: Z
  50. ");% p* Q8 O: X- v# T6 I  Y" U9 [
  51.         char buf[100];//定义一个数组用来存储接收到的数据0 b  b4 w) Q  |4 s. ^
  52.         int ret;8 N. l3 M# D+ K; O
  53.         while (1)1 m5 }3 h" {$ m6 V$ Q
  54.         {
    # L1 C8 Y% ^; G2 P' |, u9 _. X
  55.                 memset(buf, 0, sizeof(buf));! Z4 g5 }( \; H$ Y7 U7 ]9 N
  56.                 ret = read(connfd, buf, sizeof(buf));. g0 f. @7 P+ X0 O: X
  57.                 if (0 > ret)+ c& t# [' x8 x6 B- @7 _
  58.                 {
    " q, D' W6 e! T& z4 i9 Q5 u& M7 r/ s
  59.                         perror("read");
    % t# o" w% c. @( r! K9 e% a
  60.                         break;
      q) x& V" W$ D) T6 M
  61.                 }//执行while循环读取数据,当4 `( Z9 s) ?6 X$ w6 B, S; d
  62.                 else if (0 == ret)
    6 r+ ?1 h" g+ I) L' [" L' E
  63.                 {( F5 o; b. O, h, v: f2 n* Y6 A
  64.                         printf("write close!! R4 ^, T1 k8 B
  65. ");
    & {0 R( t1 m2 |3 s
  66.                         break;$ p4 g4 d& _( x7 _2 `" }" b5 r% R* {
  67.                 }
    & O1 R9 a1 Q0 M9 n! C* Z
  68.                 printf("recv: ");3 k" ~; C" K! M/ \; d( {) W; o+ H
  69.                 fputs(buf, stdout);//打印接收到的数据8 l6 u. |% A8 u* k3 e# e, g
  70.         }# D3 h- Q6 S" M! P& q7 h! U' v
  71.         close(sockfd);//关闭套接字
    2 e/ r) |5 y# ^- e+ m& P0 K
  72.         close(connfd);//断开连接
    2 ]/ F& p' J8 C4 C. [
  73.         return 0;
    2 m) c" L4 I. w/ y* e" a& w* k
  74. }
复制代码
' c( _" G- w% V. Y: y

( L% `; }8 a, n. M9 \. p  Y) z* A
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)- X. e) v* ^- p! x
  2. #include <stdio.h>
    % G/ o& E3 m! \: C: T
  3. #include <string.h>. l$ }9 H7 M5 w1 U$ u, E
  4. #include <stdlib.h>( p! b. L4 r6 {; T
  5. #include <strings.h>
    , W: J& b; @& z- ?8 c- e
  6. #include <sys/types.h>
    $ J( {! i, _/ t6 u) e( u
  7. #include <sys/socket.h>
    & O6 ]4 n9 t. z' h- x( l
  8. #include <netinet/in.h>
    8 M4 \# g% p8 p( F
  9. #include <arpa/inet.h>
    , d  \( l0 ]+ K% e
  10. int main()
    - g/ v( Y3 ]! X0 H0 g" H- P" g! z  N
  11. {! `5 u6 u3 K- w  D
  12. int sockfd;. @& y$ I/ M6 G4 Y
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))), q4 V; J8 c1 P! F
  14.         {
    7 A4 c9 d3 Y# w& t& _/ K* ?
  15.                 perror("socket");# x; f# @8 |' a3 q
  16.                 return -1;( \) l+ B3 a7 I" s4 q
  17.         }( W* i* g' {. _5 z2 X' s6 v
  18.         printf("socket...........
    ) r7 J& ~/ e0 r/ Q& w
  19. ");
    " N% V) {  F3 W7 Z  ^9 L
  20.         
    1 l" a4 k, e* q% W3 k
  21.         struct sockaddr_in srv_addr;
    : V9 R1 `$ e+ j9 g; D3 y
  22.         memset(&srv_addr, 0, sizeof(srv_addr));2 b% e- y% B: y, m) x, D
  23.         srv_addr.sin_family                 = AF_INET;; h3 i. o" r, u3 d+ b+ w7 m8 b
  24.         srv_addr.sin_port                         = htons(8888);
    5 `8 W# O: t& E, j: t- |6 D
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
      S6 f7 i9 k1 }
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))2 p3 e0 N4 E" J0 C
  27.         {- A' g7 Z, a5 D+ W! O" i( l
  28.                 perror("connect");5 Q. K5 s# T, H
  29.                 return -1; //exit //pthread_exit
    * n; o" i! b! A( }0 w+ R0 Q# E% O
  30.         }7 U3 R# D; W2 z1 H* W
  31.         printf("connect..............9 u3 I7 N# S! @& \1 R7 q# Y" j$ f9 p
  32. ");0 n$ E+ K5 J" K" d7 e" j
  33.         char buf[100];
    . U6 ]$ m0 u( h' b2 Y
  34.         int ret;
    + A: S: n' l- c7 K/ ?4 `( O
  35.         while (1)
    5 y1 N9 \% ]; u% Z
  36.         {) M: u' r! _, I' ?
  37.                 printf("send: ");* U' _3 G8 E6 c2 C1 \* c
  38.                 fgets(buf, sizeof(buf), stdin);6 M' X1 O5 G' u" B6 C5 V7 X
  39.                 ret = write(sockfd, buf, sizeof(buf));% N0 a9 ^/ K5 Y
  40.                 if (ret < 0)4 X. d6 n" A, L& G1 R
  41.                 {
    0 B8 @# e2 F; z5 R) T6 h: I
  42.                         perror("write");
    / X0 J6 T& M% O7 n7 a
  43.                         break;
      p, G8 i0 u% Q% z! v( D
  44.                 }
    6 ~; |6 H& P! q  z! v$ O: g
  45.                 if (strncmp(buf, "quit", 4) == 0)( ^' l, L% f: g0 e, t" }. x7 X
  46.                         break;
    8 U7 Y' D- y* t/ Q) h
  47.         }. ?/ U. A' X0 l  Q
  48.         close(sockfd);
    7 z5 U, k, R# N8 o6 e. ^- D
  49.         return 0;
    ) K3 k4 o- @7 I- ~! Q
  50. }
复制代码

( V+ \' f% ?: i7 F; m6 T* N$ l9 ~- K) l  _
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-3-2 08:30 , Processed in 0.164202 second(s), 33 queries .

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