文章目录
-
-
- TCP/UDP对比
- 端口号作用
- 字节序
- 字节序转换api
- 套接字 socket
-
- 实现网络通讯服务端 逻辑思路
-
- demo:
- 满血版双方通讯/残血版多方通讯 (配合进程实现)
-
- 服务端 demo
- 客户端 demo
- FTP 项目实现
-
- sever demo:
- client demo:
- 局域网多方通讯 (配合线程实现)
-
- sever demo:
- client demo:
-
TCP/UDP对比
- TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前 不需要建立连接
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
- TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等) - 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP首部开销20字节;UDP的首部开销小,只有8个字节
- TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
端口号作用
一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等
这些服务完全可以通过1个IP地址来实现。那么,主机是服务器托管网怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址与网络服务的关系是一对多的关系。
实际上是通过“IP地址+端口号”来区 分不同的服务的。
端口提供了一种访问通道,
服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69。
字节序
Little endian 小端字节序 -低位在前
Big endian 大端字节序 -高位在前
网络字节序 = 大端字节序 -高位在前
字节序转换api
#include
uint16_t htons(uint16_t host16bitvalue);
//返回网络字节序的值
uint32_t htonl(uint32_t host32bitvalue);
//返回网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue);
//返回主机字节序的值
uint32_t ntohl(uint32_t net32bitvalue);
//返回主机字节序的值
h代表host,n代表net,s代表short(两个字节),
l代表long(4个字节),[据需求选择即可]服务器托管网;
通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。
有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取
套接字 socket
实现网络通讯服务端 逻辑思路
- 创建套接字 规定地址族议、TCP/UDP等协议;
- 绑定套接字到服务器ip+端口,ip+端口储存在struct sockaddr类型的结构体指针内,可以使用更好用的struct sockaddr_in结构体类型 转成struct sockaddr类型 即可;
- 服务器监听客户端接入;
- 接收客户端套接字 并选择是否接受信息;
- 根据客户端的套接字,进行 读/写;
demo:
// 可以使用linux 的 telnet 连接服务器ip,进行测试;
// 因为是测试demo,这里绑定的服务器ip需在代码内修改,
// 后续的双方通讯则在运行时代码时怎加后缀argv参数即可;
#include
#include
#include
#include
#include
#include
#include
int main()
{
//int socket(int domain, int type, int protocol);
//01 创建套接字,返回套接字ID;
int s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd
满血版双方通讯/残血版多方通讯 (配合进程实现)
满血双方通讯:服务器与客户端 流畅沟通
残血版多方通讯:多运行几个客户端,多个客户端争抢服务端的消息,谁抢到谁显示;
(残血解决思路:
1.有几个客户端接入,就创建客户端套接字数组arrayClient[mark++];
2.遍历客户端套接字数组并发消息,即可节解决多个客户端争抢服务端的消息bug;
3.且为了稳定,尽量使用线程; 具体实现可目录跳转:局域网多方通讯;
)
服务端 demo
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char** argv)
{
if(argc
客户端 demo
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char** argv)
{
if(argc
FTP 项目实现
功能:get put ls pwd cd lls lpwd
sever demo:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LS 0
#define GET 1
#define PWD 2
#define LPWD 3
#define LCD 4
#define LLS 5
#define CD 6
#define PUT 7
#define QUIT 8
#define DOFILE 9
struct sockaddr_in c_addr;
struct sockaddr_in s_addr;
struct MASSAGE
{
int type;
char data[1024*5];
char secondBuf[128];
}Msg;
char* getdir(char* comdata)
{
char* a = strtok(comdata, " ");
if (a == NULL) {
return NULL;
}
char* b = strtok(NULL, " ");
if (b == NULL) {
return a; // 如果没有空格,直接返回第一个字符串
} else {
return b; // 如果有空格,返回第二个字符串
}
}
int getNCOM(char* comdata)
{
if (strcmp(comdata, "ls") == 0) return 0;
if (strncmp(comdata, "get", 3) == 0) return 1;
if (strcmp(comdata, "pwd") == 0) return 2;
if (strcmp(comdata, "lpwd") == 0) return 3;
if (strncmp(comdata, "lcd", 3) == 0) return 4;
if (strcmp(comdata, "lls") == 0) return 5;
if (strncmp(comdata, "cd", 2) == 0) return 6;
if (strncmp(comdata, "put", 3) == 0) return 7;
if (strcmp(comdata, "quit") == 0) return 8;
if (strncmp(comdata, "dofile", 6) == 0) return 9;
/* 提示命令不存在 错误 */ return 10;
}
void doCom(char* comdata,int c_fd)
{
char dir[1024] = {0};
//获取空格后的部分命令,没有空格则返回原命令
strcpy(dir,getdir(comdata));
if(*dir == 0){
perror("why");
}
//将命令转成int宏;
int nCom = getNCOM(comdata);
int op_fd = 0;
//定义一个1个文件流指针放popen的文件流;
FILE * r;
char dir1[1024*2] = {0};
char dir2[1024*2] = {0};
switch(nCom){
case LS :
case PWD:
r = popen(dir,"r");
fread(dir1,sizeof(dir1),1,r);
write(c_fd,dir1,sizeof(dir1));
break;
case CD:
if(strcmp("cd",dir) >= 0){
chdir("/");
}
else if(chdir(dir) 0){
printf("client data:%sn",Msg.data);
doCom(Msg.data,c_fd);
}
}
}
}
close(c_fd);
close(s_fd);
return 0;
}
client demo:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LS 0
#define GET 1
#define PWD 2
#define LPWD 3
#define LCD 4
#define LLS 5
#define CD 6
#define PUT 7
#define QUIT 8
#define DOFILE 9
char* getdir(char* comdata)
{
char* a = strtok(comdata, " ");
if (a == NULL) {
return NULL;
}
char* b = strtok(NULL, " ");
if (b == NULL) {
return a; // 如果没有空格,直接返回第一个字符串
} else {
return b; // 如果有空格,返回第二个字符串
}
}
int getNCOM(char* comdata)
{
if (strcmp(comdata, "ls") == 0) return 0;
if (strncmp(comdata, "get", 3) == 0) return 1;
if (strcmp(comdata, "pwd") == 0) return 2;
if (strcmp(comdata, "lpwd") == 0) return 3;
if (strncmp(comdata, "lcd", 3) == 0) return 4;
if (strcmp(comdata, "lls") == 0) return 5;
if (strncmp(comdata, "cd", 2) == 0) return 6;
if (strncmp(comdata, "put", 3) == 0) return 7;
if (strcmp(comdata, "quit") == 0) return 8;
if (strncmp(comdata, "dofile", 6) == 0) return 9;
/* 提示命令不存在 错误 */ return 10;
}
void doCom(char* comdata,int c_fd,int* fd)
{
char dir[1024] = {0};
strcpy(dir,getdir(comdata));
if(*dir == 0){
perror("why");
}
int op_fd = 0;
int nCom = getNCOM(comdata);
FILE * r;
char dir1[1024*2] = {0};
switch(nCom){
case LPWD:
r = popen("pwd","r");
fread(dir1,sizeof(dir1),1,r);
printf("---------------------n");
printf("%s",dir1);
printf("---------------------n");
break;
case LLS:
r = popen("ls","r");
fread(dir1,sizeof(dir1),1,r);
printf("---------------------n");
printf("%s",dir1);
printf("---------------------n");
break;
case GET:
sprintf(dir1,"./%s",dir);
op_fd = open(dir1,O_RDWR|O_CREAT|O_TRUNC,0666);
printf("creat file:%sn",dir1);
if(op_fd sin_family = AF_INET;
//htons(8989) 把数字转成网络字节序 即大端字节序
c_addr->sin_port = htons(atoi(argv[2]));
//int inet_aton(const char *cp, struct in_addr *inp);
//这里的ip是服务器本机的地址 ifconfig查看即可
inet_aton(argv[1],&c_addr->sin_addr);
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//02 客户端连接服务器的ip和端口--在c_addr结构体里配置;
connect(c_fd,(struct sockaddr *)c_addr,sizeof(struct sockaddr_in));
//显示要连接的服务器 ip
//inet_ntoa() 把网络字节序转成int;
printf("connect sever:%s n",inet_ntoa(c_addr->sin_addr));
}
void checkSocket(int c_fd)
{
if(c_fd
局域网多方通讯 (配合线程实现)
进程开辟多 容易崩亏,所以能用线程就用线程;
sever demo:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//创建客户端套接字数组
int c_fd[10] = {0};
//创建线程数组;
pthread_t t1[10];
//read用的字符串变量
char buf[1024] = {0};
char buf1[1024] = {0};
int mark = 0;
int count = 0;
void *func2(void* marknum)
{
int num2 = *((int*)marknum);
printf("marknum:%dn",num2);
//06 write
while(1){
char dataStr[1024*5] = {0};
fgets(dataStr, sizeof(dataStr), stdin);
printf("now have :%dn-----------------n",count);
for(int i = 0; i
client demo:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char** argv)
{
if(argc
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
IT运维是企业信息化建设和业务运行的重要保障,如何有效地监控和管理IT基础设施,是每个IT运维人员面临的挑战。传统的运维工具往往分散、复杂、低效,无法满足现代企业对IT运维的高要求。 监控易是一款国产化、云化、智能化的IT运维管理平台,打破了采用多种运维工具对…