一、基本解释
在之前的博客中我已经简单的介绍过TCP建立连接时的三次握手,以及在断开连接时的四次挥手,在此我就不再多说了,此次介绍基于TCP的套接字编程。在此类编程中会用到一些结构体以及函数,下来我简单地介绍下这些:
1.struct sockaddr_in
其结构如下
sockaddr_in(在netinet/in.h中定义):
struct sockaddr_in
{
short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/
unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/
};
2.bind函数
int bind(int sockfd, struct sockaddr* addr,socklen_t addrlen)
-
参数sockfd
- 指定地址与哪个套接字绑定,这是一个由之前的socket函数调用返回的套接字。调用bind的函数之后,该套接字与一个相应的地址关联,发送到这个地址的数据可以通过这个套接字来读取与使用。 参数addr
- 正如大多数socket接口一样,内核不关心地址结构,当它复制或传递地址给驱动的时候,它依据这个值来确定需要复制多少数据。这已经成为socket接口中最常见的参数之一了。 参数addrlen
- 是参数addr的大小
3.listen函数
int listen(int sock, int backlog);
sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。
4.accept函数
int accept(int sock, struct sockaddr *addr,socklen_t *addrlen);
sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。
accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字,大家注意区分。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。
二、具体编程实现
1.TCP-server
这是一个普通的TCP服务器,可以允许一个客户端进行连接
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <memory.h>
#define MAXN_STR 40
void usage(const char* proc)
{
printf("[%s],[localip],[localport]\n",proc);
}
int startup(char* ip,int port)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket\n");
return 0;
}
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=inet_addr(ip);
int opt=1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind\n");
return 0;
}
if(listen(sock,5)<0)
{
perror("listen");
return 0;
}
return sock;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
usage(argv[0]);
return 0;
}
int listen_sock=startup(argv[1],atoi(argv[2]));
char buf[10240];
struct sockaddr_in client;
socklen_t len=sizeof(client);
while(1)
{
int new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
if(new_sock<0)
{
perror("accept");
continue;
}
close(listen_sock);
printf("connect... ip is %s,port is %d\n ",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
while(1)
{
ssize_t s=read(new_sock,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]='\0';
printf("client say:%s\n",buf);
write(new_sock,buf,strlen(buf));
}
else if(s==0)
{
printf("client quit\n");
break;
}
else
{
break;
}
}
return 0;
}
return 0;
}
2.TCP-server多进程版本
可以允许多个客户端同时连接
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <memory.h>
#define MAXN_STR 40
void usage(const char* proc)
{
printf("[%s],[localip],[localport]\n",proc);
}
int startup(char* ip,int port)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket\n");
return 0;
}
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=inet_addr(ip);
int opt=1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind\n");
return 0;
}
if(listen(sock,5)<0)
{
perror("listen");
return 0;
}
return sock;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
usage(argv[0]);
return 0;
}
int listen_sock=startup(argv[1],atoi(argv[2]));
char buf[1024];
struct sockaddr_in client;
socklen_t len=sizeof(client);
while(1)
{
int new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
if(new_sock<0)
{
perror("accept");
continue;
}
printf("connect... ip is %s,port is %d \n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
pid_t id=fork();
if(id>0)
{
close(new_sock);
}
else if(id==0)
{
close(listen_sock);
if(fork() > 0)
exit(0);
while(1)
{
ssize_t s=read(new_sock,buf,sizeof(buf)-1);
if(s>0)
{
buf[s] = 0;
printf("client say:%s\n",buf);
write(new_sock,buf,strlen(buf));
}
else if(s == 0)
{
printf("client quit\n");
break;
}
else
{
break;
}
}
return 0;
}
else
{
perror("fork");
close(listen_sock);
return 0;
}
}
return 0;
}
3.TCP-server多线程版本
#include <stdio.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void usage(char* proc)
{
printf("%s: [localip], [localport]\n", proc);
}
int startup(char* ip, int port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0)
{
perror("socket\n");
return 0;
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = inet_addr(ip);
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
{
perror("bind\n");
return 0;
}
if(listen(sock, 5) < 0)
{
perror("listen\n");
return 0;
}
return sock;
}
void *handlerRequest(void* arg)
{
int new_fd = (int)arg;
while(1)
{
char buf[1024];
ssize_t s = read(new_fd, buf, sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
printf("Client: %s\n", buf);
write(new_fd, buf, strlen(buf));
}
else
{
printf("Read done..., break\n");
break;
}
}
}
int main(int argc, char* argv[])
{
if(argc != 3)
{
usage(argv[0]);
}
int listen_sock = startup(argv[1], atoi(argv[2]));
struct sockaddr_in client;
socklen_t len = sizeof(client);
while(1)
{
int new_sock = accept(listen_sock, (struct sockaddr*)&client, &len);
if(new_sock < 0)
{
perror("accept");
continue;
}
printf("Get a new connect, %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
pthread_t id;
pthread_create(&id, NULL, handlerRequest, (void *)new_sock);
pthread_detach(id);
}
return 0;
}
4.TCP-client客户端部分
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
static void usage(const char* proc)
{
printf("%s:[service_ip],[service_port]\n",proc);
}
int main(int argc, char* argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0)
{
perror("sock");
return 2;
}
struct sockaddr_in remote;
remote.sin_family = AF_INET;
remote.sin_port = htons(atoi(argv[2]));
remote.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sock, (struct sockaddr*)&remote, sizeof(remote)) < 0)
{
perror("connect");
return 2;
}
while(1)
{
char buf[1024];
printf("Please enter:\n");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf)-1);
if(s > 0)
{
buf[s-1] = 0;
write(sock, buf, sizeof(buf) - 1);
ssize_t _s=read(sock, buf, sizeof(buf) - 1);
if(_s > 0)
{
buf[_s] = 0;
printf("server echo# %s\n", buf);
}
}
}
return 0;
}