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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
) B3 {! I6 J3 ^/ j, `2 R8 M1 M
! S6 u  q! g& f" Y
0 Z% _" Z0 R: F
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。" `: h; m: f, F; _7 I

# f$ U8 S- g" e
( L9 X( b, c  ?- S
TCP协议* J) G( X, m5 b) ^# L0 C# H# S. d
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。* S/ d2 p( @6 x3 |( l  ?3 M' J

7 z. M- \1 X, M; J( s
; A5 Y6 V! i- D% j
关键词:三次握手,可靠,基于字节流。" u$ S; t8 t* z4 t" Z7 t

. n5 l1 `" z7 b. j4 ^% |+ l

/ X  h  c, V" Y! |可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。( L, Y" Z) B9 J
! P0 t6 B' y0 D) {" W
TCP服务器端和客户端的运行流程
% X" _0 n# G6 N# E' c
! a1 N4 `  g2 u  j

. _3 n% ]3 ?9 w( Q3 ^' `如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 O0 @3 H: i% D& J
$ i0 u& t- U$ D

& O& S& m5 j& {8 S9 l' n1.创建socket
* m8 O$ t3 b2 i5 U6 J$ _ socket是一个结构体,被创建在内核中
0 u8 |) c! A- Z8 v sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
9 x* X3 c" V3 G$ P- T. Q) t, o; X  k0 z0 P" j* z( l5 n

# I+ n& j9 H* `& ?/ U2.调用bind函数& e* ~( h' G! P- _1 l( i
将socket和地址(包括ip、port)绑定。* S( b& N4 C' W4 r
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
9 ]: L) S; T9 r4 e# Z1 i/ z, ~ struct sockaddr_in myaddr; //地址结构体
2 Q$ L* \  p1 Q, Z) l1 v9 C bind函数
2 _5 f. |  H% A bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* P3 J& ?! v+ @3 [, [6 }- ^" g7 h$ O# H  z2 H2 c8 C

. |# X! [" t' P- ]% Q/ T( I1 r3.listen监听,将接收到的客户端连接放入队列
; W0 ]7 T5 f+ a* ^" C# U; E; q listen(sockfd,8) //第二个参数是队列长度
: z. ^$ R9 D( g0 \2 J5 j
# u6 A# O  w! Z6 R4 E

  f' @# A# b+ _/ H3 d3 y4.调用accept函数,从队列获取请求,返回socket描 述符/ n' j* e. D8 G5 T4 L, S' M
  如果无请求,将会阻塞,直到获得连接
1 y' t- v9 D- p5 I  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数: W% |0 D- E$ e( ^! e7 k' {
9 Q1 D# `6 }1 E2 `  q6 |

5 q3 Y! Y* l# i5.调用read/write进行双向通信
2 i- A  I: ~2 G. C
1 J9 P. m7 ^  ~3 Q- W5 k% j
8 R: w1 z2 T7 m0 a
6.关闭accept返回的socket8 P; w+ o2 G* g4 j; R$ W% G3 A
  close(scokfd);
+ F# J5 e* M" I8 E: ?4 i: ]2 y. P/ X/ O, ^0 ^7 `6 e) E

3 x2 |0 J9 J3 S* r0 H2 G0 X0 a+ |8 @$ H0 o- k0 e( e& n
& L2 ^! t9 l' |3 B
下面放出完整代码
" _! y% c4 ?  q9 V. b; e( Z( A# D. A+ s) j- ]1 v6 a0 h
  1. /*服务器*/5 J& w* C' S" J- B0 ?5 p+ l
  2. #include <stdio.h>$ W9 n, d$ i$ ?/ O0 [
  3. #include <string.h>3 n& ~6 T' F. N. _, F- I6 Q" J3 l
  4. #include <stdlib.h>6 l$ ^  @! ?$ |
  5. #include <strings.h>
    7 |" Q8 K! X" x( @
  6. #include <sys/types.h>
    ! [, U6 i3 R) G% o, ~8 ~; d5 n
  7. #include <sys/socket.h>4 m$ A- Z' Q8 Q* K; I
  8. #include <arpa/inet.h>$ V5 w5 J/ a, K& m
  9. #include <netinet/in.h>
    / C+ f: c) x& V6 w+ {" V; w& {
  10. int main()
    : m3 ~, Y2 A% M! E
  11. {
    # ~' }( R, ^' H+ F
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字9 |" p& f1 G/ r  }' L' n1 Y
  13.         if (sockfd < 0)8 l9 g, h3 D6 y2 `! f; {) r
  14.         {2 o5 J; O5 j8 |7 ^1 y
  15.                 perror("socket");2 K7 v1 N  c1 t& T
  16.                 return -1;
    , @( ^% ?" L, _! X  O4 s( \; ?$ q2 a
  17.         } //创建失败的错误处理
    ( ~4 I* W" w0 o6 P  }7 Y
  18.          printf("socket..............0 `7 I  h+ K2 D
  19. "); //成功则打印“socket。。。。”
    0 [/ R4 y4 K* L# z5 f, H# E3 H* g
  20.          
    & r; b0 E- d- r7 Q
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    4 S9 Y+ y$ Y& k# k8 f5 C5 C7 P
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    , R% V, P0 N/ x8 f+ R$ ]
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    1 `7 G$ {+ S* f  U' m6 O1 ^2 a
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ' t1 N9 T) b  e( @$ J
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    5 V: O+ m* z" X

  26.   \2 M0 F% z" L
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    0 H' R* q) F# P0 T
  28.          {2 n, Z' b% l9 y* w3 h: l" e) N6 `
  29.                  perror("bind");
    * w3 A6 k" u! {
  30.                  return -1;
    ) ^9 g8 C+ {- m7 r+ M
  31.          }
    4 w# x+ }, K( c6 d4 R; q. l8 b8 F
  32.          printf("bind..........
    0 C( u! G+ N5 |4 g! }2 T
  33. ");9 \5 |% g6 x7 a' ?6 o
  34. 4 i; T- x. c' D0 f% q
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 t% k. e# B. ~, J+ Y7 s
  36.          {9 x3 F! O4 n8 z1 D9 X" u% [0 A7 j
  37.                  perror("listen");. d5 T$ x% @- {* J* A) N- |
  38.                  return -1;- Y2 U; r/ @; J5 _5 n- g
  39.          }
    2 |! u( ]9 C) _1 U  l, ~% ^
  40.          printf("listen............5 A/ T7 N! p* u
  41. ");" ]! O5 P: \7 d
  42.          
    # L, ^7 ^6 n! d0 \6 H
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求  F8 N* S% x% e% O
  44.         if (connfd < 0): k% l$ r- b7 ~) B, l
  45.         {
    8 M( z* r) ~7 _' ^& }& G6 |3 A: e9 H
  46.                 perror("accept");
    + \# y: c7 @1 f2 L/ r2 d
  47.                 return -1;" s! }/ N9 r" p, P! F
  48.         }$ P+ \& A/ |5 ~0 |
  49.         printf("accept............... x; c  v. `) D" {) q
  50. ");5 P  ^& z  A7 N* ^
  51.         char buf[100];//定义一个数组用来存储接收到的数据. n4 ]0 `4 r2 o" O/ [1 c$ i# S
  52.         int ret;$ @8 d* F9 t3 P  s( {
  53.         while (1)
    ( _8 A2 ?* M. s
  54.         {6 s0 S" i, A5 h7 z* a9 k$ `
  55.                 memset(buf, 0, sizeof(buf));
    - D, Z& p9 A- J2 U
  56.                 ret = read(connfd, buf, sizeof(buf));. ^( n, d& M' q# j
  57.                 if (0 > ret)
      i: c( N- I2 k4 k2 @9 F
  58.                 {9 X6 R, n( J8 r4 y8 e7 H, W
  59.                         perror("read");
    " K! u( K8 S$ L! N: @' j* O
  60.                         break;( {5 B4 D8 Z+ U$ A
  61.                 }//执行while循环读取数据,当
    4 v3 S6 ?) X0 A0 Y8 k4 f
  62.                 else if (0 == ret)# D" a; F1 L/ i4 d# ^
  63.                 {0 m' h$ k2 e# U2 [, O3 j
  64.                         printf("write close!
    ' V' C; V$ M" i+ D5 B. @7 v" b
  65. ");( d+ ~+ n" K) i$ N5 J
  66.                         break;; u) V" s" h7 I* I# j) o: `
  67.                 }
    6 v9 S, Q* o5 i! p* h
  68.                 printf("recv: ");2 u$ u- S$ N5 ]& v
  69.                 fputs(buf, stdout);//打印接收到的数据! ]& g: n; ^; V% ]
  70.         }
    0 x) d) N4 ~" @9 e% Y4 h4 R7 x# R6 M
  71.         close(sockfd);//关闭套接字. y. s6 |( E* E9 X! b
  72.         close(connfd);//断开连接
    ! x1 p8 A" `; Y8 @( E
  73.         return 0;
    % @  F  y, O- k" p( a1 }; d
  74. }
复制代码

, C' [4 r" e: v1 a
' J# a8 Q% h, F: s$ t3 F
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    + c- w5 s% W1 [) r
  2. #include <stdio.h>  @! `5 {+ F7 N
  3. #include <string.h>
    8 l% E; o9 B, X0 r: O' X
  4. #include <stdlib.h>9 `5 }" Y4 g  b
  5. #include <strings.h>" t1 W6 _! b5 q5 t
  6. #include <sys/types.h>
    1 y# u. j) {  t9 q, T, K
  7. #include <sys/socket.h>, @; K: g2 Z3 d& j7 j( E1 a
  8. #include <netinet/in.h>, S& O4 E1 h2 c; |! A
  9. #include <arpa/inet.h>/ @- R. n" Z" p
  10. int main()
    ' L; l6 V) l. l1 B( A
  11. {% N) S2 f9 q8 _8 _1 e
  12. int sockfd;3 A/ z& q" Y' |$ D* h0 n7 \" R4 l
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))0 P( X6 h4 T9 T+ z
  14.         {
    ) s; N- H' w: s7 I
  15.                 perror("socket");
    . u. y4 m7 A. F. N
  16.                 return -1;+ {) D- y- U& A7 e) [) [2 {
  17.         }
    / b6 c5 x' i$ C- n! X
  18.         printf("socket...........
    ' J) v* ~3 Q. f" U+ b  ]
  19. ");
    4 v: M. q$ a. a" Q9 j
  20.         
    : ]! d7 i) b/ B3 }) v8 y
  21.         struct sockaddr_in srv_addr;
    7 q2 t+ H' Y! c& }- H, @
  22.         memset(&srv_addr, 0, sizeof(srv_addr));+ Z3 [5 |/ F; m) B5 K# w% g
  23.         srv_addr.sin_family                 = AF_INET;( |3 F' r# M4 V+ s, A0 I' A0 x
  24.         srv_addr.sin_port                         = htons(8888);
    ; j) |+ Z4 w: s- u6 L8 k8 t4 C! K
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");! @8 U' q) j0 Z, A! Q# ^- F/ S1 h
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ; P8 ]1 F5 E" `
  27.         {/ X& v2 y7 {4 [+ ~: X7 p5 Z
  28.                 perror("connect");
    6 r; d) j/ ~3 p" _2 x
  29.                 return -1; //exit //pthread_exit2 B- V* [: L9 d1 [! i$ O
  30.         }
    : z8 C8 G$ X8 z, E9 }
  31.         printf("connect..............& w! u- }" u/ I5 {% K, j( f
  32. ");# @# o& F; I* }: J  T" ]( j; e
  33.         char buf[100];
    : T4 \- a1 X2 h% R- V* h- D7 j. t
  34.         int ret;+ O( s7 [  G. J) n! ]% V8 B$ b
  35.         while (1)3 V' a+ x9 C# ~( ]. e
  36.         {& B/ p, B' [) a3 K4 x" V: O
  37.                 printf("send: ");# S" q2 W) r4 n& p
  38.                 fgets(buf, sizeof(buf), stdin);
    1 Z* g: A/ u' ^) x* R1 ?: Z
  39.                 ret = write(sockfd, buf, sizeof(buf));
    " p  S0 l' _# s6 N" e! l' l8 [( l4 i
  40.                 if (ret < 0)
    4 m+ u: @2 Z+ U+ Z( n
  41.                 {
    4 M! ^3 Q2 k; I) u+ d1 P
  42.                         perror("write");
    . z0 u: c4 f5 p
  43.                         break;" c2 S8 G( a8 E
  44.                 }
    ; c# B8 k8 E1 q
  45.                 if (strncmp(buf, "quit", 4) == 0)5 P; r8 f/ a$ \
  46.                         break;
    ! p6 a# s% v' S! }0 l: r( ~
  47.         }
    % F2 w5 ~5 L* q  Y
  48.         close(sockfd);: n% D. c! C- t: K: P* d5 ?
  49.         return 0;
    8 P2 G8 B' G" R, P% H; [
  50. }
复制代码
3 K3 ~. u- C1 l, P, M: u4 A( }
7 z4 f8 {; H# G# o
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-17 11:47 , Processed in 0.141189 second(s), 24 queries .

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