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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
6 X. h% P5 m1 q' ?: \* }
" J; Z' m/ u; S" a! Y

. }% h, U* o! G$ v3 Msocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。% w7 g8 `$ }- r6 V" C
9 ?, I; J; r: A1 ~0 d

* o8 b, j% ]% q+ d& KTCP协议. E2 L6 V! y' F: [
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。0 Y7 Q7 m9 h8 ~3 I0 J

5 R! z5 ]% ]7 W6 J$ y1 S" q) Z2 o

1 [: [6 g6 H- j5 o; C( K) d关键词:三次握手,可靠,基于字节流。3 L9 l+ U7 V. R- t4 v0 X% ^

, M$ x7 J. ]# z+ s
; T5 ]' }4 i; ^( L: S$ b6 X
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
7 Z! F$ H" r* c& S$ ~9 G
, K0 x$ W0 L" F" t9 ?0 f% {5 Z- m7 uTCP服务器端和客户端的运行流程! i) r! z$ @0 U

! ?* m3 g2 a" M6 \5 {1 {
( d7 i1 Y! \( g+ o3 D
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
8 M; b) Z' e+ y3 o" W1 z! Z/ ~6 R1 P/ i
- g7 \8 W6 }3 W! F! k8 l6 T
1.创建socket- A- [$ l" w! @8 A% g  i, M8 R, G; |" ~
socket是一个结构体,被创建在内核中
4 \$ ^0 |& Y3 |8 H sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
: M! U+ F/ t, V
1 s1 c; W* c" p/ j$ ?# a8 k: a  V, q

3 E) k! x' F* V0 l/ @7 f% J2.调用bind函数/ I" t. h2 [6 s
将socket和地址(包括ip、port)绑定。
3 B+ ~! i. s+ M" R9 z 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
9 ~, i; T; |2 `* ^ struct sockaddr_in myaddr; //地址结构体7 ?; Q; a# M* h
bind函数
$ M2 q2 `9 B4 f8 o: P bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* t: Z- B1 U9 x5 L. |' ~8 i  U

$ Q9 }  d8 {- `6 I& D
1 ~% S( e( ~  R, c+ l# v
3.listen监听,将接收到的客户端连接放入队列
! j3 g: u5 q" ?2 f' q! U4 U listen(sockfd,8) //第二个参数是队列长度6 c9 o* O  |( j5 Y

9 Z, j' F3 F4 m5 k/ T5 y- S
: B' v: w9 s: N) U
4.调用accept函数,从队列获取请求,返回socket描 述符
/ ]0 m% q) ?% Y6 M$ u  如果无请求,将会阻塞,直到获得连接& x+ r3 n$ M' G- N) {. n1 ^
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数6 V9 p% r% V7 f4 A
& A  z- S+ a) b7 W% H2 `  R
9 |! W6 W0 C/ _& v7 W. D4 p+ F; g
5.调用read/write进行双向通信
0 x' }. @3 ~& o5 W$ _3 M+ L
7 k: E8 M0 F4 d& \& e$ c. q

& C" I7 m4 K! d9 Y& O; |# D: r6.关闭accept返回的socket
( [8 s" B5 o4 ^2 U  close(scokfd);3 i  n3 N8 J8 J/ T
7 _( U8 W6 `* x. y, p7 O) x

+ Y" S& w% V' F- }
* h7 I' [2 l& `/ l$ X" G8 j, G

0 R" I2 B$ v  l- z下面放出完整代码- L6 ~$ l. \. U, h

6 ?! J, N9 P7 |8 b2 Y4 M
  1. /*服务器*/
    ! u4 Q3 u& w4 W) ]# H& N0 {5 z" e8 e1 N
  2. #include <stdio.h>$ J. `* A; j1 |( G9 f" K# {
  3. #include <string.h>
    % a9 g9 m3 K+ a8 Y
  4. #include <stdlib.h>. u  r$ h) ?; n7 P
  5. #include <strings.h>4 G& c1 y3 [0 w6 y# N
  6. #include <sys/types.h>% f) U1 ?3 n: E) d- V6 }
  7. #include <sys/socket.h>5 ?/ Z4 Y' R. F
  8. #include <arpa/inet.h>
    " q8 Q9 u0 U8 I9 V* z. @
  9. #include <netinet/in.h>
    ' ^, q/ \/ L; V% q0 n
  10. int main()
    9 Y9 q5 Y- A0 u) X! g9 [
  11. {
    8 x2 k& C% p1 m4 v
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字" P9 m2 [5 }& r4 f0 P
  13.         if (sockfd < 0)
    ( A) I0 O, ^+ A/ g- B2 Q
  14.         {* F4 p; _- A5 F+ |" A  Y& F& o; {1 H; [
  15.                 perror("socket");
    : ^2 `- R; ?6 u* o# Z
  16.                 return -1;
    + Q: m5 V9 m" Y0 U0 v
  17.         } //创建失败的错误处理
    " _' \- Z! g, J' T% n9 Z
  18.          printf("socket..............
    ' ?8 e% A2 H( I0 L- b
  19. "); //成功则打印“socket。。。。”/ p6 o- J, c& i2 M' Z
  20.          : v9 `+ r- P" J
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ' D5 K' S/ g- ~: G
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    . S$ q* U4 B$ N6 L& L
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型" K8 X: s% q8 {' A0 q% `
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    9 J$ B4 M, n( M  J0 b  ^6 E7 ]
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址# {7 U6 v' P1 R# N' S: m3 L0 s
  26. , H( e# W3 z4 H
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字' h( s% X  {* s# B/ j
  28.          {
    # f( p& Q! Q3 ^- X3 [7 ^* V
  29.                  perror("bind");( J+ s2 `+ T0 _6 c5 A/ K
  30.                  return -1;. N. P0 }8 w& O  ]. _
  31.          }( I* k0 @; z" ^- C9 F  w0 z: {
  32.          printf("bind..........3 Q9 L2 M2 @8 J9 Y4 L, T' A
  33. ");
    ( _2 o2 \) W- x( {2 c$ p
  34. 7 s) V" `4 H8 O& F% N
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听$ a8 q3 b8 x9 Y7 s5 `; ^) G4 g
  36.          {
    , f! c/ t4 ]( {) k1 d% a/ n
  37.                  perror("listen");( W& ], C0 ^' P( Q) f$ i
  38.                  return -1;
    5 b7 f% F# @' A, N
  39.          }
    ( n: M: B) X5 J! t* F
  40.          printf("listen............7 K/ J- [6 A3 M. ]! x/ b& L
  41. ");* U/ z( g5 D+ B/ z. F  C
  42.          - s1 O/ A- f4 i6 p
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    - c. j: @6 r2 g! s( W
  44.         if (connfd < 0)# D" N3 _; q& F' {4 U9 v2 ?2 Y
  45.         {
    5 ~; J4 W2 j1 F; f/ b) Y- }
  46.                 perror("accept");
    " Z& E0 d* x1 i( H4 o
  47.                 return -1;) A# e# {. u; J1 e" W2 s' o
  48.         }" L2 g5 B2 y- [* j0 @" P& b
  49.         printf("accept..............
    1 K  o% A- J9 e+ ^2 W! B, `1 m& c
  50. ");
    0 n  a; s( D: {' Q# O& ~
  51.         char buf[100];//定义一个数组用来存储接收到的数据: K0 P! H" M5 \
  52.         int ret;
    $ b! z/ Y' X) [& _
  53.         while (1)' x1 D: u5 z9 l, h
  54.         {% G$ H. c' i! q/ M4 n9 [% Y% t/ G
  55.                 memset(buf, 0, sizeof(buf));" I/ ~- ^* W3 R" j, ^. f( J' k
  56.                 ret = read(connfd, buf, sizeof(buf));; ?% ]! t/ C8 v/ P5 _/ o6 i
  57.                 if (0 > ret)7 `7 d. ^% M9 O; x
  58.                 {
    4 Y% ^- y4 J9 G; |. Y4 \! y
  59.                         perror("read");8 ?+ I: Q$ ^$ D9 Q5 Q
  60.                         break;
    $ u! C0 n+ w" ?$ |/ D1 k/ x$ b8 G
  61.                 }//执行while循环读取数据,当
    . \( n" t# q4 [5 }' q
  62.                 else if (0 == ret)2 F: [0 W1 K2 J
  63.                 {
    ( C% `- Y! B3 q. y( i9 E" c# X2 L
  64.                         printf("write close!  Z! e8 g  F, s/ ^7 ^, `
  65. ");: `  m7 S- V" b5 U
  66.                         break;
    2 P; q$ z% O& \% d
  67.                 }
    % Y# d7 L- {% C2 n3 f; r; g
  68.                 printf("recv: ");& Y  Q* [2 Z0 x( ~) \$ e9 u. G
  69.                 fputs(buf, stdout);//打印接收到的数据) {5 g. d1 N5 _" E8 t, w
  70.         }
    & z7 Q8 s5 D0 ]* e7 f
  71.         close(sockfd);//关闭套接字
    1 V2 p8 U" [  E" K/ m8 S: x2 q
  72.         close(connfd);//断开连接
    6 [% T* s- j/ h( S3 }& B
  73.         return 0;  u+ l/ y2 F$ ~2 ~
  74. }
复制代码

( l( E2 ?& P! i3 x4 H" H$ Z' @* z2 m, j+ N# `3 X: n+ K
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)+ `* }. _8 Y- h8 k* a; P  N7 F
  2. #include <stdio.h>$ k, [/ b; ^" P) [- Z
  3. #include <string.h>) O4 Z; P; D  f; P, ]! O
  4. #include <stdlib.h># j6 G) r" b+ s/ Y& u  I$ R9 j
  5. #include <strings.h>
    # k  b# b9 G  L. r) f/ L6 {) T
  6. #include <sys/types.h>
    5 E7 Y$ h7 a, C1 u
  7. #include <sys/socket.h>
    ' r& n* `0 X0 Z
  8. #include <netinet/in.h>) a/ i, l5 J4 U! t! D1 a3 G
  9. #include <arpa/inet.h>0 K* }  \; s' ~: h9 w: ?
  10. int main()3 G, A& S: k3 h: `
  11. {
    , [; Z( ?) _& y$ q8 `# j* A
  12. int sockfd;
    8 D, |4 w- h8 `* N& M
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    7 O2 M2 w5 ]( }" ?$ W
  14.         {7 _4 R) D0 g: Y. X: M+ P  X
  15.                 perror("socket");
    1 n0 L" q: e2 ]# g5 R$ _$ M7 @
  16.                 return -1;: v8 t$ l6 l* _9 K$ }5 I% w, d
  17.         }2 u; O8 B+ E$ F7 Q7 Z
  18.         printf("socket...........6 o1 e6 l/ `# a" H
  19. ");0 h8 s0 m2 J: X; i+ o  W3 L: p
  20.         ) k, \  v) D% G: W! C
  21.         struct sockaddr_in srv_addr;
    ! v' ?& p: h9 h! S1 ?
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    , L* G/ N' }" U# U9 v+ \
  23.         srv_addr.sin_family                 = AF_INET;
    1 B  I9 c  D: w* ?' @5 E- L8 {
  24.         srv_addr.sin_port                         = htons(8888);! J+ n0 w$ Y% Y3 {# L1 O. q
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
      c. w" u# O* r! k
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    8 [2 ~* q3 G- Y9 h( y
  27.         {, m: p0 l7 ^  `, A2 d
  28.                 perror("connect");
    9 }0 L" r! }+ B! ^+ O
  29.                 return -1; //exit //pthread_exit; C5 ]0 u& G) J, }+ t
  30.         }
    4 f; E0 W) T: I5 I9 b
  31.         printf("connect..............+ c  n0 C. o" [# x8 \
  32. ");
    2 v, s& i- W3 }
  33.         char buf[100];- D$ R. C3 L$ G, n
  34.         int ret;! r9 @4 x' ~2 y8 e( `% ~4 {
  35.         while (1)1 U2 D3 i5 K" X' L; [; R$ i8 E
  36.         {
    ; ~4 w+ \# X9 n7 E( c- D
  37.                 printf("send: ");
    # g  F8 O! H* y. ]; t' Z
  38.                 fgets(buf, sizeof(buf), stdin);
    5 k: y5 _1 N* f: t' I
  39.                 ret = write(sockfd, buf, sizeof(buf));
    / n9 v" w$ K  @8 _* u0 H
  40.                 if (ret < 0). |# L& V# Z" v! k' Z6 }
  41.                 {
    9 `2 D+ s6 a, u0 Y( m
  42.                         perror("write");
    4 ?( Q7 r  T, `
  43.                         break;
    ! b7 w$ C2 |9 o; C6 M
  44.                 }
    % r; `* ^' f; R+ e9 s7 _  _4 W
  45.                 if (strncmp(buf, "quit", 4) == 0)/ |+ Z7 }7 L) ^3 A
  46.                         break;- o% X% ]0 O6 ]$ P
  47.         }2 w  d0 X& k+ n% r7 V- M
  48.         close(sockfd);2 ]# Y  {+ s3 @* E1 N& `$ Q; n1 ~
  49.         return 0;3 }$ v. e9 t8 ~3 H
  50. }
复制代码
, H- E) b/ \) z- C' y
, ?: J% ~% J+ d. U7 w
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-19 21:09 , Processed in 0.059578 second(s), 23 queries .

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