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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。! p, a+ y- p9 ]8 e+ a1 z
, }1 z1 }. w& w6 W
9 P( d5 g4 U: F5 X& d# N$ J. l
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
" W& p, g1 ], V6 {7 C* E! f3 |. a% }/ ~' f% Z
/ I  d7 p8 c/ y+ _' A: x$ T
TCP协议
8 ^- N4 L/ A3 t  w. y* \TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
' d! V! ^" v' [) @/ W  ?
* e1 q" c+ W+ X/ F3 l: C& S
+ I$ b- n5 U) ~. w+ S, O) T
关键词:三次握手,可靠,基于字节流。
& D# H0 s- C/ B- S0 a8 d( F1 V
+ f) f7 w! J5 {5 N. `3 I0 F" D

% J5 f$ y; f) T: r8 z- C' W可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
+ S1 T0 f" p" r1 g2 m% R! {
) u! {9 I0 `% t/ K* oTCP服务器端和客户端的运行流程
" Y' G- s' \+ P6 [
" l, A! d; }# X8 d6 q$ R
5 ^, D( j( |# H4 k
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
" @. E3 t9 |6 B4 b* n2 k5 I1 H& [

" ~7 Z. p& m7 Z+ s2 x1.创建socket6 T5 r5 f2 Q, t& [
socket是一个结构体,被创建在内核中* u4 `" a" N: z" o. A% E, d
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议0 a  m; |- W5 B8 h, {' o1 ]7 u9 n
: r# _- Y2 [2 s) g  L0 W
5 a# Y6 o5 D% e& u
2.调用bind函数
- C. Z3 d4 z* n  V8 O9 E 将socket和地址(包括ip、port)绑定。
  `. z5 i( W- U  I& W 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序7 ]- W/ c1 t1 v9 ?
struct sockaddr_in myaddr; //地址结构体* M! y% W( N6 Q' K( g1 G; _2 k/ e
bind函数
  h4 V% n5 V, a( W bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
) O1 d, `* M, l  j; \* L& o0 H9 M
; m% Q; }/ p- C5 V9 F
3.listen监听,将接收到的客户端连接放入队列9 q! Q7 }6 Y! ?; R" F* w
listen(sockfd,8) //第二个参数是队列长度
& b( ^$ E# l( g) j! O. B
4 F3 ]" o% u+ j5 \9 s

' T9 _! q' t& [+ ]5 _# I: I4.调用accept函数,从队列获取请求,返回socket描 述符
- X1 t5 [7 a$ d/ G  如果无请求,将会阻塞,直到获得连接
: Z' R# n$ U3 U9 k# \0 e5 W% S, S9 Y  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
. F( a1 K) Y9 [& {# R
. e' G! e3 D* T, q* ?. k' P
  N! E! K2 ^8 J
5.调用read/write进行双向通信
" _' i) h8 `+ m2 e' n' v; U3 R$ q8 n2 X7 ^
6 o5 U7 p& u7 K# C
6.关闭accept返回的socket
4 M( l! v% q4 ^4 \, I4 Y" d  k  close(scokfd);! j! M+ e; D! Q) I

+ `9 K) G  w0 k3 m! g5 t2 K

2 E' z% q! p) f# N7 w& S+ l; [: d8 w- T$ s' a( K

$ v" Z3 y9 o; L下面放出完整代码
5 j! H. B; g( O) w
" W: k# P. Q6 }/ {5 A1 Y
  1. /*服务器*/
      j2 @" q: A# ^! I/ u1 m! L
  2. #include <stdio.h>
    2 i5 L+ A1 R+ D* Z( ~
  3. #include <string.h>  r* V. ]+ `# P8 C1 U
  4. #include <stdlib.h>
    # t* z. ~$ T$ B5 k" U: c4 J8 x
  5. #include <strings.h>+ Y: d6 T9 O! q' T% ]1 y
  6. #include <sys/types.h>' _% U; v2 x9 N6 u2 F% z8 G
  7. #include <sys/socket.h>
    * a. D& P+ c9 ?- }1 a% Q
  8. #include <arpa/inet.h>( {) H3 W0 o3 T$ q& l: ?
  9. #include <netinet/in.h>
    ) Q* Y8 w- ^( d7 |9 a
  10. int main()
    " _# K$ {+ K3 r! ~0 N0 ~2 ^
  11. {# Y! E3 t* K  E9 u# j, g
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    " Z# Q# ]' z" Z/ I
  13.         if (sockfd < 0); Z' a  S" r0 ~( G
  14.         {
    ! ]3 y% }+ m/ Q! r
  15.                 perror("socket");
    & Z9 O+ L* P1 @) {4 U) X8 X
  16.                 return -1;
    6 v$ L6 P) P( d2 v+ o+ ~+ g2 @
  17.         } //创建失败的错误处理
    ) ~% l& A% I# C' B1 |
  18.          printf("socket..............
    % e8 p; ]* \) R9 o' i) w& R. P
  19. "); //成功则打印“socket。。。。”
    % {7 e# r: h% @3 X
  20.          
    0 F, j" l3 B! w+ Z" o' K
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体# Q) M2 J& K1 V; V* i4 J
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    6 f+ e6 r; I) a& S
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    3 P5 f/ e" [' u8 c3 I6 _2 E& ]
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    & L+ z& o' d1 Q6 g7 Z/ X1 P  B
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    4 Q3 N3 A4 A+ w- Q6 c1 v5 i
  26. , @9 |$ o9 O. M
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字# z; z$ k' r% p/ q$ w" S( M
  28.          {. K9 U5 W: e0 r% o% C8 `
  29.                  perror("bind");$ |  C# O& d6 ?9 |
  30.                  return -1;7 _1 r0 Y. j, u) o8 ~3 E
  31.          }+ k( }# J7 b  f! H) `
  32.          printf("bind..........
    9 b9 ?' s$ R* m  J
  33. ");6 g  d9 e3 t- N- K+ m8 u9 O
  34. 5 M9 G2 Z( U4 Z. U, Z5 @. b
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    / S# B% @3 z# t) |
  36.          {
      J/ z+ C) i  o- Y. j
  37.                  perror("listen");% f% n( b2 ^  K  L& H- P+ V' _
  38.                  return -1;
    & b! E4 s4 H5 C6 T8 I' R" b
  39.          }& a* z) @) _# o$ d
  40.          printf("listen............: p6 v1 Q0 I+ |0 G( p
  41. ");
    2 x  a9 U4 A' H+ K
  42.          
    1 g7 O5 D, _8 ^
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    4 k! A% w. I- {, ~. A/ L
  44.         if (connfd < 0)
    3 g) P. \( B& z
  45.         {
    4 m( t) W: I( ]+ Z7 l+ J7 |" O4 [
  46.                 perror("accept");" @0 n4 T6 M) s% K8 u2 O
  47.                 return -1;) d) ?3 [: R( b' f
  48.         }
    6 H  J! ~. m" v5 Q5 ~) i9 `
  49.         printf("accept..............
    5 P2 b; V0 F: d2 \* s. l2 p
  50. ");
    / g1 n& t1 E. ~) ]' M2 a3 l" O
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    " `; x; E6 }; e7 J
  52.         int ret;5 `* q" F- n9 Y
  53.         while (1): T. Y+ K2 p; S/ Y$ r- ?
  54.         {$ s) j9 J/ N$ m; c- |
  55.                 memset(buf, 0, sizeof(buf));
    7 F+ M% b9 d! J
  56.                 ret = read(connfd, buf, sizeof(buf));
    7 ^8 Q. t8 Q, R3 H  F" L! H
  57.                 if (0 > ret)1 Z/ z3 t' C1 i: W& A7 u
  58.                 {* {" D( K% _: C% e+ Q2 Q0 B! u
  59.                         perror("read");
    - O/ @% t/ V2 O
  60.                         break;+ t+ I8 U4 ]4 I3 y7 S/ H; v
  61.                 }//执行while循环读取数据,当: ?: i) h: C6 f9 w" K  F
  62.                 else if (0 == ret), \7 x. s* ~: `+ {" }7 O4 _* B
  63.                 {( w2 P2 L0 ~+ \: m0 e
  64.                         printf("write close!8 A8 b* \% `2 u" z, t# `
  65. ");
    ; R0 {2 C0 {; Y; y7 K
  66.                         break;" F5 w  L* ?5 M! ?
  67.                 }
    ' W3 {" [/ L! q( _' w
  68.                 printf("recv: ");
    7 f+ `- I' O! w: i( U; H
  69.                 fputs(buf, stdout);//打印接收到的数据
      v/ X4 k4 v# @% T; S; ~
  70.         }/ x1 R2 z! ?4 t1 ?, }
  71.         close(sockfd);//关闭套接字, b- i4 v- H7 p: s
  72.         close(connfd);//断开连接
    * H. g) \& ^+ H0 J5 z
  73.         return 0;9 N: @3 I! s2 Y$ s
  74. }
复制代码
+ \  R9 i4 J: v6 k( y& z5 P; f

) K% x* Y. p2 z  W8 x
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)8 k% O: W& W1 J6 C
  2. #include <stdio.h>  c, \2 k, q6 i
  3. #include <string.h>
    # s4 X% q9 g$ m+ ^/ V8 F8 T) k3 c
  4. #include <stdlib.h>3 x/ i+ t2 n9 S8 T/ b! ?3 J; X3 q
  5. #include <strings.h>
    / ]1 q: A) H6 ^2 A% w! U
  6. #include <sys/types.h>4 T6 k% U- k* N7 d
  7. #include <sys/socket.h>' C1 {0 Q% z4 \: M
  8. #include <netinet/in.h>
    ) E1 a0 u8 B% H4 k. k+ w+ k
  9. #include <arpa/inet.h>
    ) _2 s/ F& f) [. K0 c! f
  10. int main()3 ~0 l# y  x* e0 a9 \& r
  11. {
    - N; J" {5 G$ c0 z
  12. int sockfd;$ ]) K- W+ g' u/ c; `0 t. @5 p
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ! o/ B( t# |' r# {2 \  B; Z4 B
  14.         {1 L8 `2 P+ j5 B9 U; I$ @
  15.                 perror("socket");; v- y+ N) e3 I& ]  a% e0 @
  16.                 return -1;
    9 J8 t. H  j# ~- g
  17.         }
    0 E; e" g8 J8 r5 y9 a
  18.         printf("socket...........
    9 ]( `+ v+ {% G+ q
  19. ");9 s; q2 m! I& i' ], u
  20.         : R4 a; ?* {+ V7 b1 q* s7 {
  21.         struct sockaddr_in srv_addr;
    * B  K) Z+ y" w" O# t+ P7 {6 H
  22.         memset(&srv_addr, 0, sizeof(srv_addr));6 K5 I3 P3 {. e+ C: w& O
  23.         srv_addr.sin_family                 = AF_INET;
      c* v2 a$ f9 _# `& F  _" k# r
  24.         srv_addr.sin_port                         = htons(8888);! m/ k, v0 R- z4 M* p
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");; c. D9 U; o. }2 g; g' B2 ~. u
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    7 i5 I' c1 k, S; y" x
  27.         {# e0 p  q6 x1 }  h0 m! K' [: s
  28.                 perror("connect");+ |- B* o5 [% \
  29.                 return -1; //exit //pthread_exit& `" _2 a" @- n
  30.         }
    & f$ P% ]2 s/ m, f$ D1 c  w
  31.         printf("connect..............
    6 e& ?1 h1 N: z3 n& t, {
  32. ");9 }  k7 n* O4 t
  33.         char buf[100];( A; r4 m7 K7 v
  34.         int ret;
    7 i; ~# @- S2 z% e
  35.         while (1)! i) ?5 V. n4 ~1 b+ {/ K
  36.         {) e/ w4 {8 A5 v) U7 P
  37.                 printf("send: ");
      m) F) y  Z- a% }$ j6 R& F) y; i* F
  38.                 fgets(buf, sizeof(buf), stdin);
    / n( v$ h; f1 l+ U' O
  39.                 ret = write(sockfd, buf, sizeof(buf));
    2 e8 ^$ K" b8 g  ^& t" b% a, E
  40.                 if (ret < 0)3 ~* V, ~) h0 ?. R; v+ p, X" Z& X
  41.                 {! w: s& J0 t, ^9 o- E
  42.                         perror("write");, V, w- e" R5 r6 a) c$ B, c
  43.                         break;, U4 D7 Q& b8 c% y2 h; }
  44.                 }
    ( c( V% X/ f; ]; Q  R
  45.                 if (strncmp(buf, "quit", 4) == 0)
    7 h( d9 U2 o7 u% K* I
  46.                         break;
    1 q3 p8 I! J0 r2 ?& l1 m
  47.         }3 |- l+ a9 ~7 K6 o5 R
  48.         close(sockfd);+ |' e; D; L& o; |0 L
  49.         return 0;* J, J- E  r; \
  50. }
复制代码
% W# p$ D% d" i- Y/ Y1 j

5 W% C( Q& F% M: r9 I4 U
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-16 13:36 , Processed in 0.058764 second(s), 23 queries .

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