1.socket服务器和客户端的开发步骤
2.socket函数 创建套接字
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
// 返回值: 成功返回文件(套接字)描述符,失败返回-1
2.bind函数 为套接字添加信息(IP地址和端口号)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
//返回值: 成功返回0, 失败返回-1
IPv4对应的第二个参数结构体是
struct sockaddr //此结构体不常用
{
unsigned short int sa_family; //调用socket()时的domain参数,即AF_INET值。
char sa_data[14]; //最多使用14个字符长度
};
同等替换为:
struct sockaddr_in //常用的结构体
{
unsigned short int sin_family; //即为sa_family AF_INET
uint16_t sin_port; //端口号
struct in_addr sin_addr; //为IP 地址
unsigned char sin_zero[8]; //未使用
};
struct in_addr
{
uint32_t s_addr;
};
地址转换API
int inet_aton(const char *cp, struct in_addr *inp);
参数 :
cp :十进制字符串形式地址
inp :转换结果空间的首地址 存放转换结果的变量的地址
//把字符串形式的如“192.168.101.123”转换为网络能识别的格式
char *inet_ntoa(struct in_addr inddr);
//把网络格式的IP地址转换为字符串形式
3.listen函数 监听设置函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
//成功返回0 , 失败返回-1
参数:
sockfd :socket函数返回的套接字
backlog :指定在请求队列中允许的最大请求数
4.accept (服务器)函数 处理客户端的连接请求
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//成功返回 与客户端连接的套接字, 失败返回-1
参数:
sockfd:socket函数的返回值
addr :用来返回已连接的客户端的协议地址
addrled:客户端地址长度
accept所返回的文件描述符是套接字描述符,该描述符连接到调用connect的客户端。
addr 存放的是调用connect与之连接的客户端的信息,地址族、IP地址、port, 如果服务器不关心,可以设置为NULL
如果服务器调用accept,并且当前没有连接请求,服务器会阻塞直到一个新的请求到来
5.connect(客户端)函数 客户端连接主机
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
// 返回值:成功返回0, 失败返回-1
参数:
sockfd :是目的服务器socket描述符
addr :是服务器端的IP地址和端口号的地址结构指针
addrlen :地址长度常被设置为sizeof(struct sockaddr)
以下代码实现简易多人聊天室
server.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(char argc, char **argv)
{
int mark = 0;
int s_fd;
int c_fd;
int n_read;
char readbuf[128];
char msg[128] = {0};
struct sockaddr_in c_addr;
struct sockaddr_in s_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
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1)
{
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2])); //atoi()把ascii转成整形数
//htons()返回网络字节序的值(大段字节序)
inet_aton(argv[1], &(s_addr.sin_addr)); //aton()把字符串形式的IP转换成网络能识别的格式
// 2. bind
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
// 3. listen
listen(s_fd, 10);
// 4. accept
int clen = sizeof(struct sockaddr_in);
while (1)
{
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
if (c_fd == -1)
{
perror("accept");
}
mark++;
printf("get connect:%s\n", inet_ntoa(c_addr.sin_addr));
//有新客户端接入时 调用子进程
if (fork() == 0)
{
if (fork() == 0) //又创建一个子进程来进行写入操作
{ //这样读和写就分开了 不会堵在一起
while (1)
{
// memset(msg, 0, sizeof(msg));
sprintf(msg, "welcome NO.%d client", mark);
write(c_fd, msg, strlen(msg));
sleep(3); //每隔3秒向客户端发一句话
}
}
//等待读取
while (1)
{
memset(readbuf, 0, sizeof(readbuf));
n_read = read(c_fd, readbuf, 128);
if (n_read == -1)
{
perror("read");
}
else
{
printf("get message:%d,%s\n", n_read, readbuf);
}
// 6. write
}
break;
}
}
return 0;
}
client.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(char 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;
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if (argc != 3)
{
printf("param is not good\n");
exit(-1);
}
// 1. socket
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1)
{
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &(c_addr.sin_addr));
// 2. connect
if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr)) == -1)
{
perror("connect");
exit(-1);
};
while (1)
{
if (fork() == 0)
{
while (1)
{
memset(msg, 0, sizeof(msg));
printf("input: ");
fgets(msg, sizeof(msg), stdin);
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("get message from server:%d,%s\n", n_read, readbuf);
}
}
}
return 0;
}