cncml手绘网
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
[打印本页]
作者:
admin
时间:
2020-5-9 02:09
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
, k9 C8 h2 A5 H! d! a+ M8 }. T: q
7 P2 K- x% t3 A7 Y/ a
1 Q! y- F" i8 O( s* u
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
7 H7 h- Z; S6 ?# M$ H# U
5 C8 g2 W: ~0 c# Y+ J% E
% h. L3 n7 i2 @% {
TCP协议
6 v0 X& Q7 ]- K. D- ~7 P+ ^
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
' g# o5 W+ x A: b. k
7 t' D Q) o; C% C
: H& s& M- \9 D
关键词:三次握手,可靠,基于字节流。
2 h0 ?! D4 K% J2 O2 J
' O' Q5 p6 a& F# o W
2 b5 K3 e" G# J2 X
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
- {6 ]% W- S4 X" s5 g
微信截图_20200509015654.png
(175.4 KB, 下载次数: 8402)
下载附件
保存到相册
2020-5-9 01:57 上传
' X8 R7 B( P" z2 E# W4 w- s: F
TCP服务器端和客户端的运行流程
7 K; ~, w3 `! k
' J% D3 `* ~+ K' y7 @& C5 a: C5 _
! h8 N ]% s5 `
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
5 e3 I: w7 |9 }, E
; X! z7 p4 E. T& [' m+ g( C
5 \8 Y1 {+ s" \, C- @8 x# F4 W
1.创建socket
3 I" N2 C# ~0 s
socket是一个结构体,被创建在内核中
9 D% z5 ?8 h! G" V( \" q
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& f# r, J0 @/ `; E/ l$ ^5 ~
7 {2 H: w3 B; ^" ^! d
! E7 a( y% n7 v: { v
2.调用bind函数
3 Z2 u$ D0 o& `8 R, ]* R
将socket和地址(包括ip、port)绑定。
7 E6 h$ M' ~: D, U5 X1 X4 j
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
0 l" }8 l E) M2 Q# i9 ]
struct sockaddr_in myaddr; //地址结构体
% j% q0 ~3 d5 d
bind函数
8 D; o+ l; g0 G
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
% V9 a; k) E" Z" I
, o. ^* s# L! D# Z& m( S' H- s* J0 Q/ e
4 G9 m' x! x C. E- |9 q
3.listen监听,将接收到的客户端连接放入队列
$ ?) `# o% W$ O5 s
listen(sockfd,8) //第二个参数是队列长度
* E z; ]" x0 K
4 F! ]5 R+ _, F, F# h
' n% K" D: z; \, F! `6 E* G1 b6 I
4.调用accept函数,从队列获取请求,返回socket描 述符
, S4 S1 m& S" }, q) X0 G* c
如果无请求,将会阻塞,直到获得连接
& a% C; g& f8 d0 ?
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
4 }+ u4 I3 M6 l4 \% H
/ ?5 Q) i* S2 \1 F8 u- D$ w
8 k: F y: y1 Q! h- C" A
5.调用read/write进行双向通信
A4 c; D! M! k3 ?3 S# m o3 V; `
1 ]8 a- c3 @. q; I K7 j
' f, h% R" t6 c) d2 y. s+ |
6.关闭accept返回的socket
& Q' ]+ z A2 B! w8 a
close(scokfd);
, M: R: w! O) f' E* j6 c
! b9 u4 M- C& Q9 b' ?: I
) y2 c: ], ?( P1 B+ T% I+ q
/ _% Q) A: F, Z$ r7 V
1 @% y% Q3 g, T8 O R" @# p
下面放出完整代码
: O1 G1 O. ~/ B9 g" `( l
6 V1 ]9 L9 M+ B5 m/ l) J
/*服务器*/
" n3 C( E& R/ n0 n
#include <stdio.h>
3 B, m8 E! S1 }( ?$ k) @
#include <string.h>
! a) Z5 N; {4 S% w
#include <stdlib.h>
4 v% R, x3 ^0 {" [# W
#include <strings.h>
( E) l- T1 ^9 o$ D. i( G1 r; E% w
#include <sys/types.h>
% V/ R% G+ y. Y% P8 m3 n- @( J
#include <sys/socket.h>
, q3 N$ W4 O# X) P5 O( s0 G
#include <arpa/inet.h>
8 e: L3 }" \* F. K
#include <netinet/in.h>
7 i) ^: A R, [
int main()
$ c0 X; ~: _0 w6 y: h
{
2 Z; O1 W3 y" u9 t/ B$ N
int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
3 {5 X3 ^7 l& g
if (sockfd < 0)
/ |* r- I7 j5 y, D9 |; u l- ~
{
2 \8 c, a5 }; ~9 q% p+ [
perror("socket");
% G" j/ B, S- f; f9 \3 M3 B
return -1;
: Z$ t5 d3 X( l8 s! N
} //创建失败的错误处理
) L# H4 \9 P3 U1 ]! M/ h( x6 ]* C
printf("socket..............
2 s0 h g2 X: {7 ~4 m
"); //成功则打印“socket。。。。”
" S* m, Z0 U9 I
8 ^- F# B3 T3 S8 D) l
struct sockaddr_in myaddr; //创建“我的地址”结构体
7 G" i4 u; T! e f
memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
; d; a7 X* L5 a/ j
myaddr.sin_family = AF_INET; //选择IPV4地址类型
' h- u9 q8 Z0 r
myaddr.sin_port = htons(8888); //选择端口号
/ q9 [. o9 s# W: T0 S' Y
myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
8 [ a" I% w+ ^/ ?! A
5 h, O: F$ {+ ]% z
if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
* j' J% I2 k! e0 z L3 X
{
& I4 b9 L5 i/ U5 O3 M
perror("bind");
- X2 T! B. Y9 c. h$ S V' F2 a
return -1;
/ y5 q( F+ l! n$ @5 a9 ]2 F3 @
}
" K5 ` _& b* w- z
printf("bind..........
; d2 k: Y. m. B# J
");
7 U q% ~7 @; ~5 m2 \+ Y; k
' E, B$ `3 F, Z
if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
9 ~' D! r1 K A+ B2 m( Y* J. E$ _- m
{
1 f% `8 l5 }4 f; T n4 I, j
perror("listen");
O$ l. D3 Z4 J7 f
return -1;
. @1 N, ?& ~1 B% o' r) p- X
}
. A$ E- ^; z2 | Y$ q
printf("listen............
* X. f4 h4 ]' x$ |. E: P2 Z* N
");
- P. n' H5 z5 m. X- |
* {' _ |! R' e! |
int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
6 q3 u, Q$ n3 N l) {# h
if (connfd < 0)
, X7 K( d9 m9 k! B/ W" V5 V
{
9 m3 Q$ V0 ]+ f6 s4 T
perror("accept");
" [# E; G" H4 D$ S! f
return -1;
( }4 x; j- ^) l8 d9 F0 v( A
}
# D8 x* J& O* g( [! X
printf("accept..............
5 @: [6 t/ W7 b7 U( b( j1 D2 G
");
1 m4 X& `: F( ~5 f
char buf[100];//定义一个数组用来存储接收到的数据
- q' Q% U# r' X% A! G
int ret;
* f4 @! H+ ]* {, Q
while (1)
4 I- w0 b. \. v0 o' R
{
! u' T, }% Z" E4 V! R7 q
memset(buf, 0, sizeof(buf));
5 m5 W3 P. c: P0 O9 ?
ret = read(connfd, buf, sizeof(buf));
9 q$ e1 P. o7 K; T0 z. k% c6 V5 O
if (0 > ret)
; K7 i% [; c8 b
{
) ~$ a' L- }( }
perror("read");
& o, ~0 ?: z% d, S
break;
& P, V0 A7 ^# v. R, O0 X
}//执行while循环读取数据,当
7 z j$ ?. Q' {' ]+ G( t
else if (0 == ret)
# X# @" y, w8 ]) ~9 ^5 b1 F0 v' c G; L. y
{
6 h/ m' [3 w# S9 [7 A2 R
printf("write close!
! q0 A1 L! i4 H8 f5 I1 Q
");
$ o7 u+ x) Z7 p" C1 _* |' T' X
break;
. @) C9 Y0 w. B8 f- C" T
}
4 o2 P& l2 ?) D$ h
printf("recv: ");
) h, C, K6 f. |( B" T5 |9 |
fputs(buf, stdout);//打印接收到的数据
& ` h+ [" `$ H8 o
}
8 a6 L8 N9 M# ?" K' g3 }6 j& {3 F
close(sockfd);//关闭套接字
6 u6 P( J) [, }, Z
close(connfd);//断开连接
! `6 L3 G9 J0 \* m8 k. G
return 0;
% E. B$ A2 i0 o z, f% k( ?0 n5 F5 F
}
复制代码
6 g# j; G6 ]; ^5 P
% B$ a! N* v. `" q7 Z/ \( J
/*客户端*/(具体功能和服务器一样,所以不再加注释)
( O, Z, h7 _' M
#include <stdio.h>
$ \! t+ F0 G; S8 r/ y7 ]9 z ^
#include <string.h>
* N. A) V6 [4 M& y& n6 y6 E
#include <stdlib.h>
2 r% z; T4 M {; T
#include <strings.h>
; j5 `5 h8 v1 H0 j7 F
#include <sys/types.h>
: S! Z0 V7 |* Y5 n: x9 d+ o
#include <sys/socket.h>
$ S6 h6 q7 w+ s
#include <netinet/in.h>
" E, h k6 {0 l/ n
#include <arpa/inet.h>
- c d6 s) @0 m9 ?
int main()
- E) P2 M0 I/ P/ D J
{
! E2 O+ K/ o/ p# y9 p
int sockfd;
( ]2 K) H5 l; X/ I6 v! }5 }/ x& @$ `: c
if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
o5 j& x$ x' W7 z
{
' |' A$ X2 ^' ~( R" B1 f! W
perror("socket");
* A) n' H& H" v1 b5 ?
return -1;
& ?) u7 l8 b3 f4 `$ J/ Q
}
' P/ O: Z8 ~7 ^2 k4 V
printf("socket...........
) u( _6 B( S6 r& i* C% K6 P
");
; C$ J+ ?7 F" j; u c1 U& v
" p1 K0 v, ~: Y3 ]! W6 p% A1 I; F
struct sockaddr_in srv_addr;
3 V+ d& I- ?& w/ t; h
memset(&srv_addr, 0, sizeof(srv_addr));
" v w1 d# n$ A, J5 Y4 u
srv_addr.sin_family = AF_INET;
! q4 \. C- f: }2 G
srv_addr.sin_port = htons(8888);
& n/ d, W' M/ N9 @: e6 _0 `# `
srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
( t4 f0 H8 G0 ^3 t7 A
if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
* y) B. H/ Y6 {2 y7 A' n% c
{
- s3 ]% l- i& V( _
perror("connect");
% j1 X# G, d" [5 u, T* h
return -1; //exit //pthread_exit
6 G5 u6 V d$ U( L! ?
}
/ g' C( o7 d& k
printf("connect..............
+ k/ V3 ?) t" S; e
");
0 z4 h& a- e% t/ l0 g
char buf[100];
; Q: j! c2 ]5 Y, n* `% g$ b
int ret;
5 i. C7 k8 d' y7 }- |. u
while (1)
9 P8 }7 F3 m5 M! |: F- ^% y% ~* [
{
& B* K: A; {. b4 }2 ~! m/ p0 e
printf("send: ");
; e2 _8 B5 h! a, i4 G1 z( C9 d
fgets(buf, sizeof(buf), stdin);
: O# J9 p4 \) u; I$ q! M( d
ret = write(sockfd, buf, sizeof(buf));
! D3 h7 R4 d: j0 k" Z
if (ret < 0)
% w c# s4 \% T
{
, \, ^; T! I9 |$ X1 ~* M5 P0 f6 m# V
perror("write");
: I+ X; R3 d% Z' h
break;
6 a, H% V2 v; w) V% M6 N' _
}
# K: {4 E# w- |2 u
if (strncmp(buf, "quit", 4) == 0)
, I5 D2 Z7 \" h: J
break;
8 S1 c& ?% I( P6 D. j6 b) A
}
+ b' G2 _8 o! A p4 ^! C. h) O
close(sockfd);
" \) W4 A8 `5 Y0 {% U) H
return 0;
% R6 O' ]0 B' ^9 e2 ^' b! h
}
复制代码
4 t Q' S: z! i6 y# C( e- l
2 `+ Z# n; ~ P
欢迎光临 cncml手绘网 (http://bbs.cncml.com/)
Powered by Discuz! X3.2