套接字地址结构
\include<netinet/in.h>
struct in_addr
{
in_addr_t s_addr;
};
struct sockaddr_in
{
uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr[8];
}
TCP
总结函数用法
socket函数
#include <sys/socket.h>
socket(int family,int type, int protocol)
参数:family 指明协议族,type 指数据类型,protocol 指协议
返回值:成功为非负描述符,若出错则为-1



bind函数
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr * myaddr, socklen_t addrlen)
参数:sockfd指socket套接字描述符,myaddr指向需要绑定的网络地址结构,addrlen是网络地址结构的长度
返回值:成功返回0,出错返回-1
listen函数
#include <sys/socket.h>
int listen(int sockfd,int backlog)
输入参数:sockfd是socket套接字描述符,backlog可以理解为连接队列容量大小(已连接+未连接)
返回值:成功返回0,出错返回-1
accept函数
#include <sys/socket.h>
int accept(int sockfd, const struct sockaddr * clientaddr, socklen_t addrlen)
输入参数:sockfd是socket套接字描述符,clientaddr指向客户的网络地址结构,addrlen是网络地址结构的大小。
返回值:成功返回非负描述符,出错返回-1
connect函数
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr * servaddr, socklen_t addrlen)
TCP客户端用connect函数发起与服务器的连接
参数:sockfd指socket函数返回的套接字描述符,*servaddr是指向套接字结构的指针,addrlen是套接字结构的大小
返回值:成功返回0,出错返回-1
sockfd是客户端套接字描述符
servaddr套接字结构必须含有服务器IP和端口
send函数
ssize_t send(int sockfd, const char *buf, size_t len, int flags );
参数:sockfd套接字(已连接套接字),buf是发送缓存区,len是缓存区长度,
返回值:copy的字节数
recv函数
ssize_t recv(int sockfd, char *buf, size_t len, int flags)
参数:参数:sockfd套接字(已连接套接字),buf是接收缓存区,len是缓存区长度。
返回值:copy的字节数
close函数
close(int sockfd)将套接字标记为关闭
参数:sockfd指socket函数返回的套接字描述符
read函数
#include<unistd.h>
sszie_t read(int fd, void* buf, size_t nbyte)
参数:fd文件描述符,nbyte
返回值:读到的字节数;若到文件尾部,返回0;若出错返回-1
write函数
#include<unistd.h>
sszie_t write(int fd, void* buf, size_t nbyte)
参数:fd文件描述符,buf待写入的缓存
返回值:若成功,返回写入的字节数;若出错返回-1
socket(), connect(), accept()三个函数之间的关系
- socket函数创建的监听套接字(listening socket)描述符,随后用作bind和listen的第一个参数
- accept函数返回值为已连接套接字(connected socket)描述符
- 一个服务器通常仅仅创建一个监听套接字,在服务器生命周期中一直存在;
- 内核为每个已连接客户端创建一个已连接套接字
服务端处理流程:
- 调用socket函数,建立套接字描述符
- 创建网络地址数据结构,指定要监听的IP和PORT
- 调用bind函数,将套接字描述符合万罗地址数据结构绑定
- 调用listen函数,将套接字描述符转为监听套接字,表示该描述符是用于从指定地址和端口接收连接的
- 阻塞等待,调用accept函数来获取连接
- 得到连接后使用read和write函数往描述符中读写数据
- 完成后调用close关闭套接字描述符
客户端处理流程
- 调用socket函数,创建套接字描述符
- 创建网络地址结构,指定要连接的服务端IP和PORT
- 调用connect函数连接服务器
- 连接成功后调用read和write函数读写数据
- 完成后调用close关闭描述符
server代码
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <iostream>
#define PORT 7000
#define QUEUE 20
int main()
{
fd_set rfds;
struct timeval tv;
int retval,maxfd;
//创建socket
int listener =socket(AF_INET,SOCK_STREAM,0);//ipv4,stream data,TCP
struct sockaddr_in server_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(PORT);
server_sockaddr.sin_addr.s_addr=htonl(INADDR_ANY);
//bind
if(bind(listener,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))<0);
{
perror("bind error");
exit(1);
}
//listen
if(listen(listener,QUEUE)<0)
{
perror("listen");
exit(1);
}
//connect
struct sockaddr_in client_addr;
socklen_t length= sizeof(client_addr);
int conn=accept(listener,(struct sockaddr *)&client_addr,&length);
if(conn<0)
{
perror("connect");
exit(1);
}
while(1)
{
/*把可读文件描述符的集合清空*/
FD_ZERO(&rfds);
/*把标准输入的文件描述符加入到集合中*/
FD_SET(0, &rfds);
maxfd = 0;
/*把当前连接的文件描述符加入到集合中*/
FD_SET(conn, &rfds);
/*找出文件描述符集合中最大的文件描述符*/
if(maxfd<conn)
{
maxfd=conn;
}
/*设置超时时间*/
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
if(retval == -1){
printf("select出错,客户端程序退出\n");
break;
}else if(retval == 0){
printf("服务端没有任何输入信息,并且客户端也没有信息到来,waiting...\n");
continue;
}else{
/*客户端发来了消息*/
if(FD_ISSET(conn,&rfds))
{
char buffer[1024];
memset(buffer, 0,sizeof(buffer));
int len = recv(conn,buffer,sizeof(buffer),0);
if(strcmp(buffer,"exit\n")==0)break;
printf("%s",buffer);
//send(conn,buffer,len,0);
}
/*用户输入信息了,开始处理信息并发送*/
if(FD_ISSET(0,&rfds))
{
char buf[1024];
fgets(buf,sizeof(buf),stdin);
send(conn,buf,sizeof(buf),0);
}
}
}
close(conn);
close(listener);
return 0;
}
client代码
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#define MYPORT 7000
#define BUFFER_SIZE 1024
int main()
{
int sock_client;
fd_set rfds;
struct timeval tv;
int retval, maxfd;
//定义 sockfd
sock_client = socket(AF_INET,SOCK_STREAM,0);
//定义 socketaddr_in
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(MYPORT);
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");//server ip
while(connect(sock_client,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0)
{
perror("connect");
exit(1);
}
while(1)
{
/*把可读文件描述符的集合清空*/
FD_ZERO(&rfds);
/*把标准输入的文件描述符加入到集合中*/
FD_SET(0, &rfds);
maxfd = 0;
/*把当前连接的文件描述符加入到集合中*/
FD_SET(sock_client, &rfds);
/*找出文件描述符集合中最大的文件描述符*/
if(maxfd < sock_client)
maxfd = sock_client;
/*设置超时时间*/
tv.tv_sec = 5;
tv.tv_usec = 0;
/*等待聊天*/
retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
if(retval == -1){
printf("select出错,客户端程序退出\n");
break;
}else if(retval == 0){
printf("客户端没有任何输入信息,并且服务器也没有信息到来,waiting...\n");
continue;
}else{
/*服务器发来了消息*/
if(FD_ISSET(sock_client,&rfds))
{
char recvbuf[BUFFER_SIZE];
int len;
len = recv(sock_client,recvbuf,sizeof(recvbuf),0);
printf("%s",recvbuf);
memset(recvbuf,0,sizeof(recvbuf));
}
if(FD_ISSET(0,&rfds))
{
char sendbuf[BUFFER_SIZE];
fgets(sendbuf,sizeof(sendbuf),stdin);
send(sock_client,sendbuf,strlen(sendbuf),0);
memset(sendbuf,0,sizeof(sendbuf));
}
}
}
close(sock_client);
return 0;
}
网友评论