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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
7 O- G' m% {' s% B4 ~6 y
; `4 s8 u" K0 s! {0 d0 I
$ A* L* |! a. s' N
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ ^  z, @& m. f9 P4 `/ l4 I& A6 }% W9 V4 U
$ X% n9 z. y" K7 {; Z# a5 l% G9 h
TCP协议0 f4 \( ?8 R4 y: k
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
+ }" _! z, E9 M' K  X
# i6 Y/ f: ]' B( P) t1 ^
7 G& H5 b# y/ b5 J( ^8 q- r, R
关键词:三次握手,可靠,基于字节流。
9 B, f, S: N4 Q% H7 a
9 g/ `3 Y7 L) ]& E! _
, b  [: d8 h3 ^" @
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
* w3 |+ x7 l4 h' T4 S: |! A6 B0 y
* W) M! P6 Z  E& q" i# h8 g/ hTCP服务器端和客户端的运行流程5 J% R$ H7 I0 c" z  A9 {

: m. |+ a/ W" \2 \
9 S5 M* y; o9 W8 d* W4 D0 ?, X# N
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
$ m% c' t6 q$ r6 E! R) ^1 I8 n. x5 q; h7 D7 N6 T7 c8 a" W! r

& B" {9 q5 j% X; ~1.创建socket
3 y( l  _9 |3 W1 ~ socket是一个结构体,被创建在内核中- z2 ]9 [9 N- I
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
4 S2 i0 J- k% y7 @; }) I6 i) k! q6 R( S3 K2 b# A2 g
( ?- H: M3 D% E1 h# N( Q
2.调用bind函数8 C, t- r0 `" @+ b; Y
将socket和地址(包括ip、port)绑定。  H% i* u7 @7 Z6 r6 s! J/ ?
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) c0 _, v8 O: v* f. t
struct sockaddr_in myaddr; //地址结构体
4 V) \1 v, u: y8 h# b bind函数
) E% b6 i& a0 ~0 A' x1 A; V, I bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* T0 C/ y; v* I2 o# C: S9 S
6 b' o" _6 ~1 }5 W* }& @

! e! N3 \: e1 ^  q" `5 R7 T% Z' I3.listen监听,将接收到的客户端连接放入队列6 F: W" j$ K0 u2 ~" @$ t: U
listen(sockfd,8) //第二个参数是队列长度
. k; W7 C) e# h* ?: s$ v+ r$ j. E# K

2 w1 Z, T: U; d& E6 S; F4.调用accept函数,从队列获取请求,返回socket描 述符
. N. K* ^+ Z9 ?, M+ Y# |  如果无请求,将会阻塞,直到获得连接0 [) u5 d. O$ F  h, x
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数2 \) a# s/ U* _
. k* ~% c, j1 y# c/ i0 v6 ?5 Y
8 `, C( v. {9 z' x" T% y& m
5.调用read/write进行双向通信1 k% S; K8 {" o6 J; ~
2 b6 I. u: v, u, L

0 m9 \& r1 v3 W. I6.关闭accept返回的socket
( ^2 V( Y& E) l$ u# k- V  close(scokfd);
- z; a" \. S6 l! V2 O# O9 H0 k( k# H1 {

; t$ e3 H; ]+ w" x. b: K; N" G# }) M, i% x
, c* W0 o# C8 D6 h4 K: y' @6 P( n
下面放出完整代码3 n  \! n& @2 {) Y: o/ w) O* J
# ~! S0 U1 Y$ t8 v: M" {; A
  1. /*服务器*/9 R9 k! j" X7 r2 G( [0 ]
  2. #include <stdio.h>8 p/ a, T1 `4 k7 Z, N; N
  3. #include <string.h>
    & I/ z/ a: E9 p- ^
  4. #include <stdlib.h>
    + o5 B6 {5 I1 h6 l5 r
  5. #include <strings.h>
    ' |0 J& m5 z7 b7 r* W. o
  6. #include <sys/types.h>
    7 \0 L  v! {9 t( D6 }* J
  7. #include <sys/socket.h>
    ! ]$ x; _" ]5 u' F
  8. #include <arpa/inet.h>
    : P8 w; L( `8 z. i' i# o
  9. #include <netinet/in.h>
    % m  ]9 T/ A& u; C/ L5 }: \
  10. int main(): K) l9 o: X) T) b
  11. {8 W* {" o' j5 e
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字# `% r" S0 H4 ~- `9 F
  13.         if (sockfd < 0)
    7 X) V: {, g( n8 e" L% z5 b+ a- @
  14.         {8 g- b( o% M' d: W# |( [: p
  15.                 perror("socket");
      V4 W5 X8 X9 E: R+ J, w# S, O
  16.                 return -1;
    . ~8 K* k% o. `: y
  17.         } //创建失败的错误处理, `6 z! E6 |, V9 N2 s
  18.          printf("socket..............
    $ D1 [+ p3 f2 R9 {  q
  19. "); //成功则打印“socket。。。。”
    + Z+ @. E1 ~7 R" D
  20.          
    . C) z& a. ~0 [" A# G
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体; ]: [1 R3 D( v2 a' }! z5 u5 \
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ; _5 y! {4 H) u3 p- n6 e; j3 S$ y
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    + {/ L/ h8 W3 F  ?& g  K0 ?6 j
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    + |' [1 T- G: S+ W4 w" z* T1 }
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址! B* j2 R% e4 j0 [7 c& r

  26. - m1 t' K+ S& g- V- T) n% E
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    9 }6 \. W/ k3 q% U8 f8 d3 K& `
  28.          {- T. `! h2 {4 B' P) \
  29.                  perror("bind");
    - _. f# ~9 k3 C% A; [& R: g/ F
  30.                  return -1;8 T/ o8 y! u% O% y4 Q
  31.          }
    ( r. l1 e* z; [; q- g9 z2 z
  32.          printf("bind..........$ N2 W0 [# q3 _2 |) ~& t4 o# i, u
  33. ");
    & o! e  o, g( r1 x. T

  34. 3 Z8 H! @: Z0 z# a, Z( |$ \, k
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听6 n, o) A0 @- A# `
  36.          {/ c1 Y* v: P7 S; r
  37.                  perror("listen");  @* }& N6 k+ E2 i
  38.                  return -1;9 x& W1 d* d: y( t  f+ B
  39.          }  Y" B2 ^; C& F
  40.          printf("listen............
    ; l% P5 m/ S4 M& C. L! n% t2 C
  41. ");, r4 f7 y; s* w5 W. `/ [3 T  i
  42.          
    6 a/ b  \0 h% ?$ h- s% K- P" ?/ K
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求  H' B! T# j* e/ ]% o
  44.         if (connfd < 0)
    ' V1 }& G  d  b  Q6 K
  45.         {
    , B( @. M' `" T! _
  46.                 perror("accept");9 Z/ b$ N5 F) v. }! F
  47.                 return -1;7 p; O) W9 E. W7 m. Y! a
  48.         }' I. j' T" p- o/ C7 h5 ?
  49.         printf("accept..............4 W. B& r% i3 K5 b, u% V
  50. ");
    # ?  Q2 C( z5 R6 ?) W
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    9 y6 z# M" x1 [7 z
  52.         int ret;
    # x  w1 \" z: M6 A) s. b; `
  53.         while (1)
    - o3 F3 h$ ]' G4 D  _) Q
  54.         {
    4 S: w& i% {' X1 p, \% V! H9 X
  55.                 memset(buf, 0, sizeof(buf));
    + _5 X! I9 U/ Z+ L% h! Q+ m2 }
  56.                 ret = read(connfd, buf, sizeof(buf));
    ) b4 \4 j2 \. S
  57.                 if (0 > ret)
    9 E7 \1 P6 ^3 P  |5 W1 |
  58.                 {
    9 r/ P5 n( d+ {
  59.                         perror("read");( U' e) k/ f/ C$ K$ z* r. j
  60.                         break;
    5 l  D5 r# p) R7 M& c
  61.                 }//执行while循环读取数据,当: m) E% e) [9 Q
  62.                 else if (0 == ret); l, \  L9 r; D2 ^
  63.                 {
    9 G( G' e+ F( v) f5 J! A+ l& ^
  64.                         printf("write close!
    $ h5 t5 ~( u* @8 r" G
  65. ");
    1 ?/ n9 W+ I& m( x5 u( s
  66.                         break;( j8 {/ p! e/ \7 ^3 }. B! W8 @% l. M
  67.                 }
    * ^7 S# L3 C- L
  68.                 printf("recv: ");- f$ m& `& N- T/ h: X4 w& K
  69.                 fputs(buf, stdout);//打印接收到的数据! H( M& m7 b% k7 }4 T' o- {
  70.         }
    " O4 w- B/ s5 \& e, K
  71.         close(sockfd);//关闭套接字
    2 ^- u. \% N% v0 Q* n
  72.         close(connfd);//断开连接
    3 h0 \7 G$ T9 j- |- m" F
  73.         return 0;
    : `  N- i$ n; b: I$ O6 b! z1 Y
  74. }
复制代码
" z4 e% Y7 h  b- P% E

) r, w) T* B( Z2 e  m
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释). q6 s+ l8 g& e
  2. #include <stdio.h>
    ! `8 s. [" f" K4 f" i9 ^  p
  3. #include <string.h>
    # W; m; B1 a. L  e( [4 x8 E4 Q
  4. #include <stdlib.h>+ f  ^; M- n/ [, Z7 O
  5. #include <strings.h>
    0 O5 ~9 F6 @5 D8 t  L
  6. #include <sys/types.h>
    * L! e; ?7 m# V0 {- _0 Q6 F
  7. #include <sys/socket.h>
    ) ^" Q- V& J- j8 t  ~4 ]; Q
  8. #include <netinet/in.h>6 A! _. y; b6 o8 {2 N# B0 G
  9. #include <arpa/inet.h>& ]9 G9 \2 m" H9 N, O
  10. int main()0 h1 u+ J( |, A& }
  11. {
    0 I: R' M" ~0 ]0 @) |6 ]  P
  12. int sockfd;0 \' U; m8 o+ s1 |: g
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))): ]* j$ k! g. \9 @% X
  14.         {
    8 h: Q& M8 K5 ?/ I8 F2 f
  15.                 perror("socket");
    ' }8 _, z- j: [% [  {
  16.                 return -1;
      @. k" a6 d, K' i0 y; b
  17.         }
    3 j; H+ r! |$ j& m! _5 U
  18.         printf("socket...........5 L; \) ?+ ^* ^& z: q  K
  19. ");  J4 o$ J: o6 `: n' }1 j5 M
  20.         7 c& O' {1 \/ z) S" g, a8 x  S
  21.         struct sockaddr_in srv_addr;; b) K$ |6 S1 F" T% `
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    & j3 N6 Y) }! k8 W8 M$ ]' y) s
  23.         srv_addr.sin_family                 = AF_INET;
      s, k5 m; }2 e' ~& B% ?7 h
  24.         srv_addr.sin_port                         = htons(8888);  B4 X" E* G( b' S* }* h
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");$ E2 m% b; W" o5 _+ }
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    % s- M: I/ |; m0 K5 y8 U
  27.         {% T% v1 k% _) {1 S$ y
  28.                 perror("connect");
    3 B2 I. N; M/ n
  29.                 return -1; //exit //pthread_exit+ _2 _- {2 H, N/ R+ }- t& J% J
  30.         }
    1 T2 ~7 E2 q9 J& N% I. e; i
  31.         printf("connect..............# j' ]5 `+ R4 ~+ @' S0 d
  32. ");! `1 j; u, w& _$ O
  33.         char buf[100];
    4 S# H+ t+ n; ]& ~
  34.         int ret;$ |3 P. @. K& g+ w$ Y
  35.         while (1)' ]/ k! j- S+ T
  36.         {
    & {; s6 z) O. O( \
  37.                 printf("send: ");
    6 @) ^# C3 D# O
  38.                 fgets(buf, sizeof(buf), stdin);) j2 \/ ^7 O) W. N
  39.                 ret = write(sockfd, buf, sizeof(buf));% _: ]# T( G0 f- |9 W9 ~
  40.                 if (ret < 0)
    0 [* a" P2 b- r0 n
  41.                 {  s$ L1 G+ R2 I! B) Q
  42.                         perror("write");
    5 N8 P5 K8 q8 g3 _  e
  43.                         break;
    1 J' E2 E( w3 U$ y
  44.                 }& y- T/ x: T+ p# j
  45.                 if (strncmp(buf, "quit", 4) == 0)0 Z' e! q+ f! o
  46.                         break;. D. _4 W4 t4 y* r( z& g) D% M2 U
  47.         }: H4 t6 x/ q: s5 `
  48.         close(sockfd);
    6 ~0 d: X6 e* V+ a
  49.         return 0;9 ^  s4 i2 W' }; E* B! h5 H
  50. }
复制代码

) F3 Y" Z1 _' s. N$ g! m& c
8 m, V" n+ O4 M5 z$ P
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-7 19:45 , Processed in 0.149500 second(s), 25 queries .

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