使用TCP协议的socket
1.网络字节序
由于在主机存储为小端序,网络传输为大端序,并且在网络中需要读取IP号和端口号,所以发送端要将小端序转为大端序,接收端将大端序转为小端序
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
表示host,n表示network,l表示32位长整数,s表示16位短整数。
2.IP地址转换函数
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
3.构造一个sockaddr
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//本地任意IP(由于网卡可能绑定了多个IP)
servaddr.sin_port = htons(8000);
这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP地址,端口号为8000。
4.socket函数
int socket(int domain, int type, int protocol);
参数: domain:1. AF_INET (ipv4) 2.AF_INET6 (ipv6)
type: 1. SOCK_STREAM (数据流) 2.SOCK_DGRAM(数据报) 3.SOCK_RAW(ICMP使用)
protocol: 0 默认协议
返回值:成功返回一个新的文件描述符,失败返回-1,设置errno
5.绑定函数:bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
将构造的sockaddr和创建的socket绑定在一起。
参数:
1.sockfd:socket文件描述符
2.addr:构造出IP地址加端口号
3.addrlen:sizeof(addr)长度
返回值:成功返回0,失败返回-1, 设置errno
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络地址和端口号。
6.监听函数:listen
int listen(int sockfd, int backlog);
参数:1.sockfd:socket文件描述符
2.backlog:排队建立3次握手队列和刚刚建立3次握手队列的链接数和
返回值:listen()成功返回0,失败返回-1。
7.accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数: 1.sockdf:socket文件描述符
2.addr:传出参数,返回链接客户端地址信息,含IP地址和端口号
3.addrlen:传入传出参数(值-结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小
返回值:成功返回一个新的socket文件描述符,用于和客户端通信,失败返回-1,设置errno
三方握手完成后,服务器调用accept()接受连接
accept()的参数listenfd是先前的监听文件描述符,而accept()的返回值是另外一个文件描述符connfd,之后与客户端之间就通过这个connfd通讯,最后关闭connfd断开连接,而不关闭listenfd,再次回到循环开头listenfd仍然用作accept的参数。accept()成功返回一个文件描述符,出错返回-1。
8.connect
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockdf:socket文件描述符
addr:传入参数,指定服务器端地址信息,含IP地址和端口号
addrlen:传入参数,传入sizeof(addr)大小
返回值:成功返回0,失败返回-1,设置errno
客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。connect()成功返回0,出错返回-1。
代码实现:
该代码实现了客户机连接服务器的时候会主动的向客户机发送you are NO.(第几台)连接进入服务器的机子。
server
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
int s_fd;
int c_fd;
int n_read;
int n_write;
int mark=0;
char msg[128] = {'\0'};
char readbuf[128] = {'\0'};
char writebuf[128] = {'\0'};
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1) {
perror("socket");
exit(-1);
}
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr, '\0', sizeof(struct sockaddr_in));
memset(&c_addr, '\0', sizeof(struct sockaddr_in));
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));
listen(s_fd, 10);
int clen = sizeof(struct sockaddr_in);
while (1) {
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
if (c_fd == -1) {
exit(0);
} else
printf("conncet %s\n", inet_ntoa(c_addr.sin_addr));
mark++;
if (fork() == 0) {
if (fork() == 0) {
while(1)
{
sprintf(msg,"you are NO.%d:",mark);
write(c_fd, msg, 128);
sleep(3);
}
}
while(1)
{
memset(readbuf,'\0',128);
n_read = read(c_fd, readbuf, 128);
if (n_read == -1) {
perror("read");
} else {
printf("get message form client:%s\n", readbuf);
}
}
break;
}
}
return 0;
}
client
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
int c_fd;
int n_read;
int n_write;
char readbuf[128] = {'\0'};
char writebuf[128] = {'\0'};
char msg[128]={'\0'};
int mark=0;
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1) {
perror("socket");
exit(-1);
}
struct sockaddr_in c_addr;
memset(&c_addr, '\0', sizeof(struct sockaddr_in));
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &c_addr.sin_addr);
// bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
// listen(s_fd, 10);
int clen = sizeof(struct sockaddr);
if (connect(c_fd, (struct sockaddr *)&c_addr, clen) == -1) {
perror("connect filed");
exit(0);
}
/* `` int c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen); */
/* if (c_fd == -1) { */
/* exit(0); */
/* } else */
/* printf("conncet %s\n", inet_ntoa(c_addr.sin_addr)); */
/* */
while (1) {
if (fork() == 0) {
while(1)
{
memset(msg,'\0',128);
printf("input msg:\n");
gets(msg);
write(c_fd, msg, strlen(msg));
}
}
while(1)
{
memset(readbuf,'\0',128);
n_read = read(c_fd, readbuf, 128);
if (n_read == -1) {
perror("read");
} else {
printf("get message form server:%s\n", readbuf);
}
}
}
return 0;
}