一 TCP和client的关系和区别
TCP: 面向连接 是可靠传输 (服务器)
client :客户端 有独立的ipv地址 一个或者两个(对于linux来说)
区别:客户端是主动的 服务器是被动的
原因:服务器因为是先存在了 处于一种阻塞状态(被动等待) 客户端随时都可以连接 (主动连接)
二 创建流程
三 tcp 服务器
1 socket 函数
1 一般为ipv4:选 AF_INET
2:选tcp 对应的参数
3 :2不选 原始套接字 就填0
套接字类似 我们串口的波特率 只是一种交流方式
这里选择对应参数 就是 最合适的
2 bind 函数
TCP 为网络通信 所以选 下面 的struct sockaddr_in 类型的。
但是 bind 函数 第二个参数 是不一样的 也就是第一个结构体类型的
他在里面定义的数组就是存放 第二个结构体 的最后三个参数的
其实 就是 先 间接 用 第二个结构体变量的方式 赋值 最后转为 第一个结构体的类型
先写 bind 函数和 socket 函数
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <unistd.h>
#include<arpa/inet.h>
int main(int argc,char **argv)
{
if(argc!=3){
perror("argc");
}
int s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd==-1){
perror("socket");
}
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr,'\0',sizeof(s_addr));//网络编程用 memset 初始化 最好
memset(&c_addr,'\0',sizeof(c_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port =htons(atoi(argv[2])) ;
s_addr.sin_addr.s_addr =inet_addr(argv[1]);
if(bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in))) {
perror("bind");
}
比如
sin.sin_port=htons(5003);(端口号)
主机字节序 --》》网络字节序
并且为两个字节的 (双引号就是双字节)
sin.sin_addr.s_addr=inet_addr("192.168.xx.xxx");
ipv4地址---》》》网络字节序 二进制
二进制就是可执行的 通过这个inet_addr函数 生成 二进制 文件
3 listen函数
过于复杂 不要钻空子
先简单 了解 在这 主要功能是 把 主动 状态 转变为 被动 状态
参数二 :注意一些就行了
4 accept 函数
产生一个新的连接 因为是阻塞等待
socket.c (全部代码)
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <unistd.h>
#include<arpa/inet.h>
int main(int argc,char **argv)
{
if(argc!=3){
perror("argc");
}
int s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd==-1){
perror("socket");
}
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr,'\0',sizeof(s_addr));//网络编程用 memset 初始化 最好
memset(&c_addr,'\0',sizeof(c_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2])) ;
s_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in))) {
perror("bind");
}
printf("bind is success\n");
listen(s_fd,10);
printf("listen is success...........\n");
char readbuff[1024];
int len = sizeof(struct sockaddr_in);
int rts;
int c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&len);
if(c_fd<0)
{
exit(2);
}
printf("reveice is success client ip is %s\n ",inet_ntoa(c_addr.sin_addr));
while(1){
do{
bzero(readbuff,1024);
rts = read(c_fd,readbuff,1024-1);
}while(rts<1);
if(rts<0)
{
exit(1);
}
if(rts==0)
{
break;
}
printf("getmassage is %s\n",readbuff);
}
close(c_fd);
close(s_fd);
return 0;
}
四 client 客户端
1socket connect fgets 和write函数 配合使用
clinet.c (全部代码)
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <unistd.h>
#include<arpa/inet.h>
int main(int argc,char **argv)
{
if(argc!=3){
perror("argc");
}
int c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd==-1){
perror("socket");
}
struct sockaddr_in c_addr;
memset(&c_addr,'\0',sizeof(c_addr));
c_addr.sin_family = AF_INET;
c_addr.sin_port =htons(atoi(argv[2])) ;
c_addr.sin_addr.s_addr =inet_addr(argv[1]);
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in))<0)
{
exit(1);
}
char writebuff[1024];
while (1)
{
bzero(writebuff,1024);
if(fgets(writebuff,1024-1,stdin)==NULL){
//fgets 有 换行符结尾 所以只能1024-1个输入上限
continue;
}
write(c_fd,writebuff,strlen(writebuff));
}
return 0;
}
3 服务器代码先下载 后到 客户端 (重要)
1 如果 读取只能一次 交换终端编译
2 尽量 不要 while 循环 这么写
int xx=aa(xx,xxx);
把int xx 放在while之前
1 strlen 是函数 并且是 以'\0' 结尾 一般用于 发送数据 函数 计算 字符长度
并且能保证字符能完整发完 (因为有'\0' 结束符了)
2 sizeof 只是计算 类型大小 和长度
3 read 函数 一般 读取的字节却决于定义的数组大小
字节让他读满就行了