目录
1、Socket服务器与客户端的开发步骤:
2、基本函数原型
2.1socket()函数
int socket(int domain, int type, int protocol);//返回sockfd
- domain:
协议域,又称协议族,指明所使用的的协议族,通常用AF_INET(IPV4),表示互联网协议族(TCP/IP协议族)
- type参数指定socket类型 :
SOCK_STREAM:
流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性。
SOCK_DGRAM::
数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠的、无差错的,它使用数据报协议UDP。
SOCK_RAW:
允许程序使用低层协议,原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大,但使用较为不便,主要用于一些协议的开发。
- protocol:
故名思意,就是指定协议。通常赋值为0。
0:选择type类型对应的默认协议;
IPPROTO_TCP:TCP传输协议
IPPTOTO_UDP:UDP传输协议
IPPROTO_SCTP:STCP传输协议
IPPROTO_TIPC:TIPC传输协议
2.2bind函数
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:用于绑定IP地址和端口号到socketfd
- sockfd:
是一个socket的描述符。
- addr:
一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,
- addrlen:
地址长度。
通用函数类型:
struct sockaddr{
sa_family_t sa_family;
char sa_data[14];
}
(同等替换)ipv4对应的是:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order 2字节*/
struct in_addr sin_addr; /* internet address 4字节*/
unsigned char sin_zero[8];
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
ipv6对应的是:
struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
};
struct in6_addr {
unsigned char s6_addr[16]; /* IPv6 address */
};
Unix域对应的是:
#define UNIX_PATH_MAX 108
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
};
地址转换API:
int inet_aton(const char *straddr,struct in_addr *addrp);
功能:将字符串形式的IP地址转化为网络能识别的格式
char* inet_ntoa(struct in_addr inaddr);
功能:把网络格式的ip地址转换为字符串形式
字节序转换API:
#include <netinet/n.h>
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(四个字节),通过上面四个函数,
可以实现主机字节序与网络字节序之间的转换。有时候可以用INADDR_ANY,INADDR_ANY指定地址
让操作系统自己获取。
2.3listen()函数
int listen(int sockfd, int backlog);
listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。
2.4accept()函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //返回连接connect_fd
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。
- sockfd:
socket服务端的socket描述符。
- addr:
用来返回已经连接的对端(客户端)的协议地址。
- addrlen:
客户端地址的长度。
2.5connect()函数:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
客户机连接主机。用于绑定之后的client端(客户端),与服务器建立连接。
sockfd:
客户端的socket描述符。
addr:
是服务器端的IP地址和端口号的地址结构指针。
addrlen:
地址长度。
3、代码示例,客户端与服务端实现聊天通信
serve.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char **argv)
{
int s_fd;
int c_fd;
int clen;
int n_read;
char readbuf[128];
//char *msg = "I have connected";
char msg[128] = {0};
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket int socket(int domain, int type, int protocol);
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
//2.bind
// int bind(int sockfd, const struct sockaddr *addr,
// socklen_t addrlen);
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen int listen(int sockfd, int backlog);
listen(s_fd,10);
//4.accept int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
while(1){
clen = sizeof(struct sockaddr_in);
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
if(c_fd == -1){
perror("accept");
}
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
//5.read
if(fork() == 0){
if(fork() == 0){
while(1){
memset(msg,0,sizeof(msg));
printf("input: ");
fgets(msg,128,stdin);
msg[strlen(msg)-1]='\0';
write(c_fd,msg,strlen(msg));
}
}
while(1){
memset(readbuf,0,sizeof(readbuf));
n_read = read(c_fd,readbuf,128);
if(n_read == -1){
perror("read");
}else{
printf("read messege is:%d,%s\n",n_read,readbuf);
}
}
}
}
return 0;
}
client.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char **argv)
{
int c_fd;
int n_read;
char readbuf[128];
//char *msg = "msg from client";
char msg[128] = {0};
struct sockaddr_in c_addr;
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket int socket(int domain, int type, int protocol);
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1){
perror("socket");
exit(-1);
}
//2.connect int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){
perror("connect");
exit(-1);
}
//3.write
while(1){
if(fork() == 0){
while(1){
memset(msg,0,sizeof(msg));
printf("input: ");
fgets(msg,128,stdin);
msg[strlen(msg)-1]='\0';
write(c_fd,msg,strlen(msg));
}
}
//4.read
while(1){
memset(readbuf,0,sizeof(readbuf));
n_read = read(c_fd,readbuf,128);
if(n_read == -1){
perror("read");
}else{
printf("read messege is:%d,%s\n",n_read,readbuf);
}
}
}
return 0;
}