下面的代码主要实现两个功能:
1,通过bind使本机指定端口如2012端口与远端服务器通信,不指定的话会随机分配
2,然后使用该指定端口创建监听服务,接受其他客户端的连接。
主要方法用到端口复用,通过调用setsocketopt函数设置标记位实现,不进行端口复用的话程序在第二个步骤bind的时候会出现端口已被占用的错误信息
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
//远程服务器端口和IP
#define PORT 1987
#define HOST "192.168.197.118"
//本机服务器端口和IP
#define LOCALPORT 2012
#define LOCALHOST "10.0.2.15"
//线程提供TCP服务器服务,绑定2012端口
void serve_proc(int port)
{
printf("server start listening on port %d\n",port);
int sock_fd;
if ((sock_fd=socket(AF_INET, SOCK_STREAM, 0))==-1){
printf("socket() error\n");
exit(1);
}
struct sockaddr_in server, client;
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(LOCALHOST);
int reuseport = 1;
//端口设置重用,不然会bind出错
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseport, sizeof(int));
if (bind(sock_fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0){
perror("bind error");
exit(-1);
}
if (listen(sock_fd,5) < 0) {
perror("listen error");
exit(-1);
}
char recv_buf[100],send_buf[100];
int conn_fd;
int client_size = sizeof(struct sockaddr_in);
while(1){
conn_fd = accept(sock_fd, (struct sockaddr *)&client, &client_size);
if (conn_fd < 0){
perror("accept error");
exit(-1);
}
while(1){
int recv_num = recv(conn_fd, recv_buf, sizeof(recv_buf), 0);
if(recv_num < 0){
close(conn_fd);exit(-1);
}
recv_buf[recv_num] = '\0';
if(strcmp(recv_buf,"quit")==0){
break;
}
printf("server get %s\n", recv_buf);
sprintf(send_buf, "server got %d bytes\n", recv_num);
int send_num = send(conn_fd, send_buf, strlen(send_buf), 0);
if(send_num < 0){
close(conn_fd);exit(-1);
}
}
close(conn_fd);
}
}
int main(int argc, char *argv[])
{
int sock_fd;
struct sockaddr_in server,client;
int flag = 0;
int reuseport = 1;
int local_port = LOCALPORT;
if ((sock_fd=socket(AF_INET, SOCK_STREAM, 0))==-1){
printf("socket() error\n");
exit(1);
}
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseport, sizeof(int));
//连接其他服务器并发出数据
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr(HOST);
//使用本地指定端口建立TCP连接
struct hostent *he;
he = gethostbyname(LOCALHOST);
bzero(&client,sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(local_port);
client.sin_addr = *((struct in_addr *)he->h_addr);;
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseport, sizeof(int));
if (bind(sock_fd, (struct sockaddr *)&client, sizeof(struct sockaddr_in)) < 0){
perror("bind error");
exit(-1);
}
if (connect(sock_fd,(struct sockaddr *)&server, sizeof(struct sockaddr)) < 0){
perror("connect error");
exit(1);
}
struct sockaddr_in local;
socklen_t local_len = sizeof(local);
//获取TCP连接的本地IP端口信息
getsockname(sock_fd, (struct sockaddr *)&local, &local_len);
char local_ip[100];
inet_ntop(AF_INET, &local.sin_addr, local_ip, sizeof(local_ip));
printf("local host %s and port %d\n", local_ip, ntohs(local.sin_port));
local_port = ntohs(local.sin_port);
//功能一:绑定2012端口提供TCP服务
pthread_t id;
int ret = pthread_create(&id, NULL, (void *)serve_proc, (void *)local_port);
if (ret!=0) {
perror("create thread error");
exit(-1);
}
int send_num, recv_num;
char send_buf[100],recv_buf[100];
LBL:
//功能二:使用2012端口充当客户端访问远程TCP服务器
printf("Input MSG: ");
scanf("%s",send_buf);
send_num = send(sock_fd, send_buf, strlen(send_buf), 0);
if (send_num < 0) {
perror("send error");
exit(1);
}
goto LBL;
}
主要使用端口复用,就让程序在监听2012端口作为TCP服务器的同时充当客户端使用2012端口去访问远程服务器的1987端口。