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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。) X# o2 x; @0 B# o- Q

3 i6 @0 s# H) ^# i( Y$ v
1 l) Z0 g+ ]; S; H7 M2 n" }- _1 ^
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
& c( g, L! w  O2 B, `; Q  a* |; U1 A  A% F: v. O/ V6 j8 k5 p! O7 H* x

3 n4 d; P( J+ s6 x! s* mTCP协议7 T8 g7 T" y( c4 Q& Y
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。  A8 ^6 I4 B, j

4 A+ e/ Y$ c$ J  a1 m2 s* U2 o
1 _7 ]) c" s' S0 \8 K6 ?2 Y; h
关键词:三次握手,可靠,基于字节流。- O+ `: r! o) c3 d3 y" L8 P

: V+ ~" X% {9 d8 w! M
! B% N. h; ]2 P& y5 |
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
: q( J% w5 r+ R
8 n! P5 H$ c' ?$ ^* i4 i8 S. J4 `TCP服务器端和客户端的运行流程
7 l( s- z' \0 e, n7 n6 g+ \% @
! j' T' k, G, b$ D0 o3 l

/ w+ k2 h: `  {/ u# e& q如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?, g" n1 @( ]: m& X' T- Z

6 r" G# S& Q) e/ h0 }8 c( J

- V. O/ W. F7 z. Z4 v0 W$ y4 n1.创建socket
) y1 G0 k$ Y- K9 i socket是一个结构体,被创建在内核中8 _# B& f  p% u; G" ~8 P1 [+ T
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
! i7 _: F5 Q% ~, _8 S1 T3 t4 E4 q5 |1 ]4 e
) I. U) S4 Z2 N" b/ V
2.调用bind函数$ a! m0 ~6 p; {, X" t& @
将socket和地址(包括ip、port)绑定。
# U; @9 z6 P- q7 \/ F1 n 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
' {/ _/ g8 d8 K2 ?: } struct sockaddr_in myaddr; //地址结构体
$ _0 W" W7 M" J5 W; V bind函数# C5 j% P4 c4 Q5 a% `8 r" `
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)); g" n8 r& Q( l( U7 b

4 C/ X: y; u8 P
( S( a. n6 {4 Z
3.listen监听,将接收到的客户端连接放入队列
3 n4 w6 x5 L6 T listen(sockfd,8) //第二个参数是队列长度, |- E# V4 s$ v6 |. W2 T9 \+ t

( ^& |# X/ A- \0 v. o' E) b# J" A
' v  q7 k9 M, {. ^0 u9 {# N
4.调用accept函数,从队列获取请求,返回socket描 述符
' A3 n* [" q  }3 X: i  如果无请求,将会阻塞,直到获得连接2 G' A/ b9 f* v8 U, {
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
2 `% S) c3 o% V$ h5 W5 q+ d5 h$ @! x+ e

. }% D0 M" }7 L, d" A+ O* f5.调用read/write进行双向通信7 q! P: ]7 e! U% P8 ?
: |& e/ V5 n; y/ \  s
( s* z5 r' P4 S! R/ H
6.关闭accept返回的socket* K8 H0 f' {' d  O+ j5 k3 U
  close(scokfd);
/ @4 w0 Q  r" |4 g
1 _( ?! y* D8 b  U

) q+ B% k  e4 g, x
' a) F5 d8 ?9 e# H# r
" U3 z% _# a6 J4 ?$ f
下面放出完整代码
' F. [$ _0 J- ^$ R1 P) o& i5 S( v) E  z* [
  1. /*服务器*/
    1 {$ m* ?/ i0 z
  2. #include <stdio.h>* p  b, f! D9 e
  3. #include <string.h>5 v  ~; z2 {) _* P7 l( O
  4. #include <stdlib.h>& O  }5 K/ {4 f
  5. #include <strings.h>. }! }. m4 F! E4 V
  6. #include <sys/types.h>* A# l6 X: o# K( @, X5 U+ t7 q  S
  7. #include <sys/socket.h>' k+ e1 y- K4 d0 a4 h' R" F1 F
  8. #include <arpa/inet.h>" d: `5 n, ^& ]
  9. #include <netinet/in.h>
    - {( r* n) K- ^( k& G
  10. int main(). C2 {; Z8 J9 H% u
  11. {
    + H7 f( ~+ a' T8 \  b0 A
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字( q7 c. e+ |. b" h/ x6 Q
  13.         if (sockfd < 0)7 {( w9 B; y, @; _# T- C, L
  14.         {9 K5 _0 N$ Z  r0 J8 |
  15.                 perror("socket");- `+ C' `* E: U0 R& n
  16.                 return -1;) @. D) {! G9 |- z' {
  17.         } //创建失败的错误处理
    5 B/ w% O$ I% y7 S
  18.          printf("socket..............3 K" |* @6 b( L6 P
  19. "); //成功则打印“socket。。。。”
    : u6 G" s/ ~% h5 I4 ^
  20.          
    & Y6 b" V6 B1 ?
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    & P& X: E# p; O6 _
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    0 ~' ]: c- x( f  G' k' X
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ; ?8 g. |5 U0 ?1 T2 U9 f. b) S  B
  24.          myaddr.sin_port                 = htons(8888); //选择端口号6 g4 {& j3 [4 N: Q7 w
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址9 R5 w3 p) o4 [+ s
  26. 2 F- K2 c3 ~; P+ E& q0 Z9 N' Q
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ! Q. j8 p; i& }5 O
  28.          {' `4 x! z5 t( f6 H2 L! n
  29.                  perror("bind");
    9 L) {1 J8 N( K* D; h* }: s
  30.                  return -1;
    5 V* m# ]7 B/ y4 b5 p
  31.          }
    9 |% }0 R$ e, r+ ^% k- R- E
  32.          printf("bind..........
    2 a* S$ @1 r, Y
  33. ");
    / Q* H) ~) o( M5 ^% t! ~' K8 |

  34. ! B- W6 u$ l, V0 U
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 D# V4 V* p/ q* D7 Q
  36.          {
    ; g$ K3 i. `8 S3 @$ E9 o
  37.                  perror("listen");8 n0 J/ @& }! J$ \/ _! o
  38.                  return -1;
    9 F/ m# p* f, v7 Y# @9 b" ?
  39.          }
    8 A/ w) Y7 }, c. D
  40.          printf("listen............. ]5 t( ?3 g% \4 z( j/ Q" }# {
  41. ");
    6 f* L( ]" P2 {4 o: q% D
  42.          
    + r6 |# |1 U& l0 n9 Q- ?0 ?7 b2 ?
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    6 y" A& S/ w+ f) _
  44.         if (connfd < 0)
    8 g/ \, I2 l! k7 w- {3 Z! x5 v3 `
  45.         {
    ' Y2 {* Q7 G) X, m
  46.                 perror("accept");9 [+ [8 f9 R3 i  s4 T, M5 d4 H
  47.                 return -1;
    ; O) @7 w% G# {
  48.         }/ z4 a, i  W' V9 C; k& |3 [4 t
  49.         printf("accept..............
    4 r3 z  j& l9 M" C+ Z4 p1 c
  50. ");& m5 ^% W) {: T% K3 ^* v$ X
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    - d( U# `  }# c3 T; c( o' ?
  52.         int ret;8 U6 L& v1 I/ p! u8 B& f
  53.         while (1)
    ) E+ P  K/ z  \2 ~4 [1 i
  54.         {1 @$ o( ?; H( ?6 ~4 w6 Y
  55.                 memset(buf, 0, sizeof(buf));
    , l) X4 N" m: ]6 B( w
  56.                 ret = read(connfd, buf, sizeof(buf));
    # V8 M, y) N, x/ p9 m" ~: q4 Q
  57.                 if (0 > ret)
    " D5 a5 v6 [# a  C/ C0 e: T, n
  58.                 {
    , @9 v% w6 W9 h+ c& v& |
  59.                         perror("read");
    2 r# f# `! p4 b  b! }$ K- ^* v+ K
  60.                         break;$ v' y! k) V9 W0 }0 d
  61.                 }//执行while循环读取数据,当
    . E4 M6 _- x1 ]( [
  62.                 else if (0 == ret): _4 \" b9 t% h' Q( L: b+ p& l; [
  63.                 {
    % B. ^1 F: ]' b, I
  64.                         printf("write close!( B; ~: P! m, b. w5 S
  65. ");, W" d( T5 r2 g- j
  66.                         break;) p4 y9 J9 U, B8 n
  67.                 }7 @0 t) ~$ ]+ B' v% n
  68.                 printf("recv: ");
    2 r. E" I5 B$ O& o# ^! i. B
  69.                 fputs(buf, stdout);//打印接收到的数据
    9 o5 k; f% H. s7 ?! f8 ^
  70.         }  n9 o6 R9 E7 k+ t, ~
  71.         close(sockfd);//关闭套接字) T  Y& A: l6 S! E& j
  72.         close(connfd);//断开连接
    / M& W/ w& ?0 e3 I' [% x- p
  73.         return 0;
    7 y& ?2 e4 t5 E; p, v+ C
  74. }
复制代码

7 Z, h4 o4 {# e# K& i) T* G5 a0 z( Z; j$ Y3 a) C: Q. ]
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    / _; p. c( J8 {2 n' P" L
  2. #include <stdio.h>% r) P4 [! Q. Y4 O' b; v! Z
  3. #include <string.h>
    9 ?2 s8 e" }, Z# M8 \$ Q
  4. #include <stdlib.h>4 i. q/ {2 s  [' ?3 `) b
  5. #include <strings.h>: }/ Y% g- R; |# T0 R4 ^, r2 D4 P+ e
  6. #include <sys/types.h>
    & w/ i1 Q+ t$ \* P$ H/ ~
  7. #include <sys/socket.h>: f2 Y; X) C; d0 N# R! O  w! j
  8. #include <netinet/in.h>; F9 ^# j* r) e  @  n7 r9 i" O
  9. #include <arpa/inet.h>% ~# ]+ ]/ ]3 L- s# M
  10. int main()$ x8 g% i' ?. x/ ]
  11. {8 Y" l6 \' ^& L
  12. int sockfd;
    ' E& K0 E) O: y" S- S. v0 j1 A% i
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))2 U+ d5 Q5 j4 ?: f  I9 q  W
  14.         {9 d# _8 N( I4 J3 [2 |
  15.                 perror("socket");0 D" H8 s, s; o8 D$ g
  16.                 return -1;
    / \# g! X4 }" }% \
  17.         }) ]* S' T/ I6 i9 n( [5 M3 M, c, w
  18.         printf("socket...........* R( |; W% P9 h8 R; H) V, W' X
  19. ");
    2 p2 d5 P+ o) B' y1 P6 n
  20.         
    1 u- H2 H: R, s. E6 F: d
  21.         struct sockaddr_in srv_addr;: o+ l+ i  @) u" K& ]
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    & ?& u# S2 Z$ l) r# E
  23.         srv_addr.sin_family                 = AF_INET;1 R' D2 m7 c) N  i2 U8 W
  24.         srv_addr.sin_port                         = htons(8888);5 H/ u. a8 F8 e- R0 `
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");+ B& w0 g  l) D3 ~2 E
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))8 W) E2 s4 r6 R+ L7 S( B
  27.         {6 S( [! v) H8 X" H
  28.                 perror("connect");
    ( `* }/ j, _, X7 O! ~! J& s+ s4 d! D
  29.                 return -1; //exit //pthread_exit. H8 F! }9 H/ t3 i; M
  30.         }. a! S; p* O+ P" i, y5 z
  31.         printf("connect..............+ e- g8 m+ n, ~8 ~5 {
  32. ");
    7 t+ K8 E, n2 i% a0 C# @0 F  ?- g
  33.         char buf[100];
    1 ?. l- }' o3 ?
  34.         int ret;- Z. o3 n) @0 A0 h- D- N
  35.         while (1)
    , l; N) Z( z- T/ b' \0 p
  36.         {
    8 ^. Q+ v. K+ h$ X
  37.                 printf("send: ");
    1 a# V" H  V+ g6 w% D- [. w2 s
  38.                 fgets(buf, sizeof(buf), stdin);  d& l8 M) l5 M- g* }/ Q" L) Z# _2 c
  39.                 ret = write(sockfd, buf, sizeof(buf));, d% V  j/ d9 I+ e+ q: r3 x( i
  40.                 if (ret < 0)
    $ b9 F* x2 S" s* o1 {+ J
  41.                 {; @& X5 x! g" a' W9 s
  42.                         perror("write");
    9 N' c) N5 k! V, k- @3 K4 u: F
  43.                         break;9 I1 f8 l7 x; k; u; I5 u: s
  44.                 }
    + k% }: b2 V# i2 T
  45.                 if (strncmp(buf, "quit", 4) == 0)8 i8 y: b+ ~. i  s6 S' z
  46.                         break;$ y3 V" t" _4 h. J- K1 h; `8 P$ ^
  47.         }
    0 E% @3 H$ k6 M/ t$ @# ~2 {. o
  48.         close(sockfd);1 r3 D* n5 y" k+ Z2 J
  49.         return 0;
    5 X" K2 ?4 H$ Q$ r2 H+ i  x
  50. }
复制代码

& P1 F0 l' \* [8 q* O2 C
* U, {9 T# N7 C+ r; k
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 10:12 , Processed in 0.116176 second(s), 23 queries .

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