相关的接口函数
(1)创建套接字
#include
#include
#include
int socket(int domain, int type, int protocol);
返回值:成功 返回套接字的文件描述符
失败 -1
参数:domain --》地址协议
IPV4地址协议 --》AF_INET或者PF_INET
IPV6地址协议 --》AF_INET6或者PF_INET6
type --》套接字类型
tcp套接字(数据流套接字,流式套接字) --》SOCK_STREAM
udp套接字(数据报套接字) --》SOCK_DGRAM
protocol --》扩展协议,一般默认设置为0
(2)绑定ip和端口号(自己的ip和端口号)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); //通用性更好,兼容ipv4和ipv6
int bind(int sockfd, const struct sockaddr_in *addr, socklen_t addrlen); //局限性,只能使用IPv4地址
int bind(int sockfd, const struct sockaddr_in6 *addr, socklen_t addrlen); //局限性,只能使用IPv6地址
参数: sockfd --》套接字的文件描述符
addr --》(重点,重点,重点)存放你要绑定的ip和端口号
struct sockaddr //通用地址结构体,兼容ipv4和ipv6
{
sa_family_t sa_family; //保存地址协议 2字节
char sa_data[14]; //存放ip和端口号 14字节
} 16字节
struct sockaddr_in //IPV4地址结构体
{
short sin_family; //地址协议
struct in_addr sin_addr; //存放要绑定的ip地址 4字节
short sin_port; //存放要绑定的端口号
sin_zero; /保证struct sockaddr_in大小跟struct sockaddr一致
}
struct in_addr
{
in_addr_t s_addr; //最终存放要绑定的ip地址
}
struct sockaddr_in6 //IPV6地址结构体
{
}
addrlen --》地址结构体的大小 sizeof()
(3) 转换函数
字节序–》数据在计算机中是如何存放的
大端序 高字节存放在低地址,低字节存放在高地址
小端序: 高字节存放在高地址,低字节存放在低地址
网络字节序:计算机网络中数据采用大端序存放
主机字节序:linux系统中数据采用小端序存放
小端序ip—》大端序ip
in_addr_t inet_addr(const char *cp);
返回值:成功 转换得到的二进制大端序ip
参数:cp --》点分十进制字符串ip
小端序端口号—》大端序端口号
uint16_t htons(uint16_t hostshort);
h —>host
to -->转换
n -->network
s -->short
返回值:成功 大端序的端口号
参数:hostshort --》小端序的端口号
(4)连接服务器
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:addr --》存放服务器的ip和端口号
(5)监听
tcp协议允许多个客户端连接同一个服务器
int listen(int sockfd, int backlog);
参数:backlog(重点) --》最多允许多少个客户端同时连接一台服务器
listen(sockfd,6);
(6)接受客户端的连接请求,愿意接听
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:(重点重点重点) 成功 新的套接字文件描述符
失败 -1
特点:(重点)
每连接一个客户端,accept都会产生新的套接字
新的套接字用来跟客户端通信
accept如果没有客户端连接,会一直阻塞,一旦有客户端连接成功,accept会解除阻塞,返回新的套接字
参数:addr --》存放目前连接成功的客户端的ip和端口号
注意:该变量不需要初始化,accept会自动将目前连接成功的客户端信息存放到这个变量地址中
addrlen --》地址长度,要求是指针
(7)研究read和write
文件IO: read的返回值 返回0表示文件读取完毕
网络编程: read的返回值 返回0表示对方断开连接了,read也不会阻塞了
(8)遇到的问题
第一个: 绑定失败,原因是地址被占用
真实原因:你绑定的端口号不会随着程序的强行退出立马释放,所以你再次运行提示这种错误(本质上就是端口号没有彻底释放)
解决方法:
第一种:更换新的端口号–》虽然可以解决问题,但是麻烦
第二种:调用系统提供的函数setsockopt()修改套接字的属性,修改成端口号可以重复使用
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
参数:sockfd --》套接字文件描述符
level --》SOL_SOCKET
optname --》SO_REUSEADDR
(9)send和recv
ssize_t send(int socket, const void *buffer, size_t length, int flags); //类似于write
返回值:成功 返回发送的字节数 失败 -1
参数:flags --》默认设置为0
ssize_t recv(int socket, void *buffer, size_t length, int flags); //类似于read
返回值:
参数:flags --》默认设置为0
MSG_WAITALL //recv在接收信息的时候必须按照length的大小接收,一个都不能少
TCP单向通信
客户端
#include "myhead.h"
int main()
{
int tcpsock;
int ret;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr("192.168.24.35"); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(10000); //绑定客户端自己的端口号
//定义结构体变量存放对方(服务器)的ip和端口号
struct sockaddr_in serveraddr;
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.24.201"); //服务器的ip地址
serveraddr.sin_port=htons(20000); //服务器的端口号
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//连接服务器
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1)
{
perror("连接服务器!n");
return -1;
}
//发送信息给服务器
while(1)
{
bzero(buf,100);
printf("请输入要发送给服务器的信息!n");
scanf("%s",buf);
write(tcpsock,buf,strlen(buf));
}
close(tcpsock);
return 0;
}
服务器
#include "myhead.h"
int main(int argc,char **argv)
{
int tcpsock;
int newsock;
int ret;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr("192.168.24.201"); //绑定服务器自己的ip地址
bindaddr.sin_port=htons(20000); //绑定服务器自己的端口号
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
int addrsize=sizeof(clientaddr);
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
printf("旧的文件描述符:%dn",tcpsock);
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//监听
ret=listen(tcpsock,8);
if(ret==-1)
{
perror("监听失败!n");
return -1;
}
//接收客户端的连接请求
newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
if(newsock==-1)
{
perror("接收客户端的连接请求失败!n");
return -1;
}
printf("新的文件描述符:%dn",newsock);
//接收信息
while(1)
{
bzero(buf,100);
ret=read(newsock,buf,100);
//ret=read(tcpsock,buf,100); //错误示范,用错了文件描述符
printf("read的返回值是:%dn",ret);
printf("客户端发送给我的信息是:%sn",buf);
}
}
TCP双向通信:
客户端
#include "myhead.h"
int tcpsock;
void *recvservermsg(void *arg)
{
char otherbuf[100];
int ret;
while(1)
{
bzero(otherbuf,100);
ret=read(tcpsock,otherbuf,100);
if(ret==0)
break;
printf("服务器发送过来的信息是:%sn",otherbuf);
}
}
int main(int argc,char **argv)
{
int ret;
pthread_t id;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr("192.168.24.3"); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(atoi(argv[3])); //绑定客户端自己的端口号
//定义结构体变量存放对方(服务器)的ip和端口号
struct sockaddr_in serveraddr;
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr(argv[1]); //服务器的ip地址
serveraddr.sin_port=htons(atoi(argv[2])); //服务器的端口号
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//连接服务器
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1)
{
perror("连接服务器!n");
return -1;
}
//创建一个子线程,接收服务器回复的信息
pthread_create(&id,NULL,recvservermsg,NULL);
//发送信息给服务器,主线程发送
while(1)
{
bzero(buf,100);
printf("请输入要发送给服务器的信息!n");
scanf("%s",buf);
write(tcpsock,buf,strlen(buf));
}
close(tcpsock);
return 0;
}
服务器
#include "myhead.h"
int newsock;
void *sendmsgclient(void *arg)
{
char otherbuf[100];
while(1)
{
bzero(otherbuf,100);
printf("请输入要发送给客户端的信息!n");
scanf("%s",otherbuf);
write(newsock,otherbuf,strlen(otherbuf));
}
}
int main(int argc,char **argv)
{
int tcpsock;
int ret;
pthread_t id;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr(argv[1]); //绑定服务器自己的ip地址
bindaddr.sin_port=htons(atoi(argv[2])); //绑定服务器自己的端口号
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
int addrsize=sizeof(clientaddr);
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
printf("旧的文件描述符:%dn",tcpsock);
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//监听
ret=listen(tcpsock,8);
if(ret==-1)
{
perror("监听失败!n");
return -1;
}
//接收客户端的连接请求
newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
if(newsock==-1)
{
perror("接收客户端的连接请求失败!n");
return -1;
}
printf("新的文件描述符:%dn",newsock);
//创建一个线程专门发送信息给客户端
pthread_create(&id,NULL,sendmsgclient,NULL);
//接收信息
while(1)
{
bzero(buf,100);
ret=read(newsock,buf,100);
if(ret==0)
break;
//ret=read(tcpsock,buf,100); //错误示范,用错了文件描述符
printf("read的返回值是:%dn",ret);
printf("客户端发送给我的信息是:%sn",buf);
}
}
多个客户端连接同一个服务器
服务器
#include "myhead.h"
int main()
{
int tcpsock;
int newsock;
int ret;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr("192.168.24.35"); //绑定服务器自己的ip地址
bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
int addrsize=sizeof(clientaddr);
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
printf("旧的文件描述符:%dn",tcpsock);
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//监听
ret=listen(tcpsock,8);
if(ret==-1)
{
perror("监听失败!n");
return -1;
}
while(1)
{
printf("阻塞在accept位置!n");
//接收客户端的连接请求
newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
if(newsock==-1)
{
perror("接收客户端的连接请求失败!n");
return -1;
}
printf("新的文件描述符:%dn",newsock);
}
}
client1-client4
#include "myhead.h"
int main()
{
int tcpsock;
int ret;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr("192.168.24.35"); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(10000); //绑定客户端自己的端口号
//定义结构体变量存放对方(服务器)的ip和端口号
struct sockaddr_in serveraddr;
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.24.35"); //服务器的ip地址
serveraddr.sin_port=htons(10086); //服务器的端口号
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//连接服务器
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1)
{
perror("连接服务器!n");
return -1;
}
//发送信息给服务器
while(1)
{
bzero(buf,100);
printf("请输入要发送给服务器的信息!n");
scanf("%s",buf);
write(tcpsock,buf,strlen(buf));
}
close(tcpsock);
return 0;
}
内核链表实现多个客户端连接一个服务器
服务器代码
#include "myhead.h"
//定义内核链表
struct clientlist
{
int sock; //存放套接字
struct list_head mypoint;
};
//初始化链表头
struct clientlist *init_list()
{
struct clientlist *head=malloc(sizeof(struct clientlist));
INIT_LIST_HEAD(&(head->mypoint));
return head;
}
void *routine(void *arg)
{
char buf[100];
struct clientlist *p=(struct clientlist *)arg;
while(1)
{
bzero(buf,100);
printf("某个线程阻塞在read了!n");
read(p->sock,buf,100); //接收某个客户端发送的信息
printf("客户端发送过来的信息是:%sn",buf);
}
}
int main()
{
pthread_t id;
int tcpsock;
int newsock;
int ret;
char buf[100];
//初始化表头
struct clientlist *myhead=init_list();
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定服务器自己的ip地址
bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
int addrsize=sizeof(clientaddr);
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
printf("旧的文件描述符:%dn",tcpsock);
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//监听
ret=listen(tcpsock,8);
if(ret==-1)
{
perror("监听失败!n");
return -1;
}
while(1)
{
printf("阻塞在accept位置!n");
//接收客户端的连接请求
newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
if(newsock==-1)
{
perror("接收客户端的连接请求失败!n");
return -1;
}
printf("新的文件描述符:%dn",newsock);
//把文件描述符存放到链表中
struct clientlist *newnode=malloc(sizeof(struct clientlist));
newnode->sock=newsock;
INIT_LIST_HEAD(&(newnode->mypoint));
//插入到链表的尾部
list_add_tail(&(newnode->mypoint),&(myhead->mypoint));
//立马新建线程接收这个客户端发送的信息
pthread_create(&id,NULL,routine,newnode);
}
}
客户端代码
#include "myhead.h"
int main()
{
int tcpsock;
int ret;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(20000); //绑定客户端自己的端口号
//定义结构体变量存放对方(服务器)的ip和端口号
struct sockaddr_in serveraddr;
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.24.3"); //服务器的ip地址
serveraddr.sin_port=htons(10086); //服务器的端口号
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//连接服务器
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1)
{
perror("连接服务器!n");
return -1;
}
//发送信息给服务器
while(1)
{
bzero(buf,100);
printf("请输入要发送给服务器的信息!n");
scanf("%s",buf);
write(tcpsock,buf,strlen(buf));
}
close(tcpsock);
return 0;
}



