Linux下多进程版本服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
//用于服务接口
void serviceIO(int fd){
//定义一个缓冲区
char buf[1024];
//开始读数据
for(;;){
ssize_t s=read(fd,buf,sizeof(buf)-1);
//读成功
if(s>0){
buf[s]=0;
printf("client# %s",buf);
}
//断开连接
else if (s==0){
printf("client quit!\n");
break;
}
//读出错
else {
perror("read");
break;
}
//服务结束,关闭文件描述符(不关会有浪费)
close(fd);
}
// ./tcpServer 192.168.X.X XXX
// 端口号:众所周知,且不能随意改变
int main(int argc, char *argv[])
{
if(argc != 3){
printf("%s [server_ip] [server_port]\n", argv[0]);
return 1;
}
//1、创建套接字,IPV4 协议 ,字节流
int sock = socket(AF_INET, SOCK_STREAM, 0);
//判断套接字创建是否成功
if(sock < 0){
perror("socket");
return 2;
}
//本地填充
struct sockaddr_in server;
server.sin_family = AF_INET;
//字符串风格IP转换成4字节整型风格
server.sin_addr.s_addr = inet_addr(argv[1]);
//atoi 字符串转整型
//htons 主机序列转网络序列
server.sin_port = htons(atoi(argv[2]));
//2、绑定
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0){
perror("bind");
return 3;
}
//监听 (建立连接,返回一个sock文件描述符,只负责数据通信)
if(listen(sock, 10) < 0){
perror("listen");
return 4;
}
//开始连接 accept (也会返回fd文件描述符,即新连接,用于服务)
for(;;){
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
int fd=accept(sock,(struct sockadder)&peer,&len);
if(fd<0){
perror("accept");
continue;
}
printf("get a new link....\n");
}
pid_t id =fork();
if(id <0){
perror("fork");
}
//子进程执行服务
else if(id==0){
//只进行服务,不监听
close(sock);
if(fork()>0){
//子进程退出
exit(0);
}
//子进程的子进程(孤儿进程被1号进程回收)
serviceIO( fd);
//服务结束就退出
exit(0);
}
//父进程继续获得新连接
else{
close(fd);
waitpid(id,NULL,0);
}
}
}
服务器还可以改为多线程版本:较简单
void serviceIO(int fd) {
//定义一个缓冲区
char buf[1024];
//开始读数据
for (;;) {
ssize_t s = read(fd, buf, sizeof(buf) - 1);
//读成功
if (s > 0) {
buf[s] = 0;
printf("client# %s", buf);
}
//断开连接
else if (s == 0) {
printf("client quit!\n");
break;
}
//读出错
else {
perror("read");
break;
}
//服务结束,关闭文件描述符(不关会有浪费)
close(fd);
}
void * service(void * arg) {
int fd = (int)arg;
serviceIO(fd);
close(fd);
pthread_exit(0);
}
// ./tcpServer 192.168.X.X XXX
// 端口号:众所周知,且不能随意改变
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("%s [server_ip] [server_port]\n", argv[0]);
return 1;
}
//1、创建套接字,IPV4 协议 ,字节流
int sock = socket(AF_INET, SOCK_STREAM, 0);
//判断套接字创建是否成功
if (sock < 0) {
perror("socket");
return 2;
}
//本地填充
struct sockaddr_in server;
server.sin_family = AF_INET;
//字符串风格IP转换成4字节整型风格
server.sin_addr.s_addr = inet_addr(argv[1]);
//atoi 字符串转整型
//htons 主机序列转网络序列
server.sin_port = htons(atoi(argv[2]));
//2、绑定
if (bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0) {
perror("bind");
return 3;
}
//监听 (建立连接,返回一个sock文件描述符,只负责数据通信)
if (listen(sock, 10) < 0) {
perror("listen");
return 4;
}
//开始连接 accept (也会返回fd文件描述符,即新连接,用于服务)
for (;;) {
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int fd = accept(sock, (struct sockadder)&peer, &len);
if (fd<0) {
perror("accept");
continue;
}
printf("get a new link....\n");
//创建线程(粒度细,消耗小)
pthread_t tid;
pthread_create(&tid, NULL, service, (void *)fd);
//线程分离
pthrread_detach(tid);
}
Linux 下的客户端:
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
// ./tcpServer 192.168.X.X XXX
int main(int argc, char *argv[])
{
if(argc != 3){
printf("%s [server_ip] [server_port]\n", argv[0]);
return 1;
}
//创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
perror("socket");
return 2;
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(argv[1]);
server.sin_port = htons(atoi(argv[2]));
//建立连接
if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0){
perror("connect");
return 3;
}
// 发送数据
char buf[1024];
for(;;){
printf("Please Enter# ");
scanf("%s", buf);
write(sock, buf, strlen(buf));
}
}
网络字节序列默认为大端;发送时主机转网络,接收时网络转主机。
struct sockaddr *:通用数据结构,类似void*
socket
系统调用:操作系统提供的接口
两个文件描述符:
1、sock :获得新连接,只用于通信;
2、fd :新连接,用于服务;
本地环回IP:127.0.0.1 9999 (本地测试)